I've been thinking about this, and I'll here are my thoughts of how to
approach this. If it's ok, I'll go ahead and work on it.
The test case I produced is just one situation in which saving would fail in
a multithreaded environment. Imagine if in between modifications to an
object's member collection, another thread invokes the equivalent of
getCivPeoples() in the test case, replacing the object's member collection.
Oblivious to this, the original thread continues modification of the member
collection. However, the Vector object that the original thread modifies is
now defunct. The object no longer has a reference to it, and all
modifications are lost.
One solution, then, would be to ensure that the get method for the member
collection does not succeed if any modifications have been performed to the
Vector representing the member collection, either throwing an exception, or
maybe just waiting (which is probably a bad idea). This can be implemented
by
1) creating a class which extends Vector, and provides access to modCount
(number of modified elements in the Vector). This is currently a protected
variable.
2) modify the BaseXXX template to use this modified Vector instead of a
normal one
3) adding a boolean flag for each collection, whether it has been modified
(using modCount)
4) modifying get{Collection} to check for the boolean flag, and maybe throw
an exception if it's true
Kelvin
----- Original Message -----
From: Kelvin Tan <[EMAIL PROTECTED]>
To: Turbine Users List <[EMAIL PROTECTED]>
Sent: Saturday, December 08, 2001 11:58 AM
Subject: Re: [Torque] Failed testcase
> > On 12/7/01 9:52 PM, "Kelvin Tan" <[EMAIL PROTECTED]> wrote:
> >
> > > Neglected to mention that this is with the CVS version checked out on
> Dec 7
> > > itself.
> >
> > You've got the testcase, now you get to figure out what's wrong :-)
> >
> > I don't know off the top of my head what the problem is.
>
> I think I do. As I mentioned, I believe it's because of the way Torque
> caches the previous Criteria's result, and then attempts to use that to
save
> to the database.
>
> Using the testcase example:
>
> Within BaseCivilization,
>
> Calling getCivPeoples(Criteria criteria) assigns a vector collCivPeoples
as
> the result of the doSelect, and returns this vector.
>
> In save(DBConnection dbCon),
>
> if (collCivPeoples != null )
> {
> for (int i=0; i<collCivPeoples.size(); i++)
> {
> ((CivPeople)collCivPeoples.get(i)).save(dbCon);
> }
> }
>
> basically using the results of the doSelect as the basis for saving, which
> makes sense since modifications to an object's member collections should
be
> saved when an object is saved.
>
> The trouble, of course, arrives when, in between an object being modified
> and the save, getCivPeoples(Criteria criteria) is called, essentially
> setting the vector collCivPeoples to a new value (depending on the
criteria)
> and wiping out all modifications.
>
> This problem will not arise if application-level caching is not performed,
> since Torque itself doesn't do any caching, everytime a doSelect is
called,
> a new Civilization object is created with its own collCivPeoples. In the
> case when it is used in conjunction with a cache like Turbine's global
> cache, then this will pose a problem.
>
> Kelvin
>
> >
> >
> > > ----- Original Message -----
> > > From: Kelvin Tan <[EMAIL PROTECTED]>
> > > To: <[EMAIL PROTECTED]>
> > > Sent: Friday, December 07, 2001 6:11 PM
> > > Subject: [Torque] Failed testcase
> > >
> > >
> > > The testcase I've submitted below fails. The problem, I think, is that
> > > before a save() is called on an object whose collection has been
> modified,
> > > the modification is basically lost because of the way Torque caches
the
> > > previous Criteria's dbhit. Attention to Line 1 and Line 2.
> > >
> > > This situation would be possible in a high-load environment with lots
of
> > > concurrent hits.
> > >
> > > public class TorqueRunner
> > > {
> > > /**
> > > * Category used for logging in the runtime test class.
> > > */
> > > private static Category cat =
> > > Category.getInstance(TorqueRunner.class.getName());
> > >
> > > public static void main(String[] args)
> > > throws TorqueException, Exception
> > > {
> > > String configurationFile = "/schema/Torque.properties";
> > >
> > > // initializing Torque
> > > try
> > > {
> > > Torque.init(configurationFile);
> > > }
> > > catch (Exception e)
> > > {
> > > throw new TorqueException("Can't initialize Torque!", e);
> > > }
> > >
> > > // run the tests
> > > TorqueRunner tr = new TorqueRunner();
> > >
> > > //tr.insertData();
> > > //tr.retrieveData();
> > > tr.test();
> > > Runtime.getRuntime().exit(0);
> > > }
> > >
> > > private void test() throws Exception
> > > {
> > > Civilization civ = new Civilization();
> > > civ.setCivId("1");
> > > civ.setName("Civilization");
> > > civ.save();
> > >
> > > int newCivPeopleSize = civ.getCivPeoples(new Criteria()).size();
> > >
> > > CivPeople cp = new CivPeople();
> > > cp.setCivId(civ.getCivId());
> > > People p = new People();
> > > p.setPeopleId("1");
> > > p.setName("peoplename");
> > > p.save();
> > > cp.setPeopleId(p.getPeopleId());
> > > //cp.save(); // Line 1
> > > civ.addCivPeople(cp);
> > > civ.getCivPeoples(new Criteria()).size(); // Line 2
> > > civ.save();
> > > civ = (Civilization)
> CivilizationPeer.doSelect(civ).firstElement();
> > > int newerCivPeopleSize = civ.getCivPeoples(new
Criteria()).size();
> > > if (newerCivPeopleSize != (newCivPeopleSize + 1))
> > > throw new Exception("Added people not reflected! Expected:"
> > > + (newCivPeopleSize + 1) + " but was:"
> > > + newerCivPeopleSize);
> > > }
> > > }
> > >
> > >
> > > Regards,
> > > Kelvin Tan
> > >
> > > Relevanz Pte Ltd
> > > http://www.relevanz.com
> > >
> > > 180B Bencoolen St.
> > > The Bencoolen, #04-01
> > > S(189648)
> > >
> > > Tel: 238 6229
> > > Fax: 337 4417
> > >
> > >
> > >
> > >
> > > --
> > > To unsubscribe, e-mail:
> <mailto:[EMAIL PROTECTED]>
> > > For additional commands, e-mail:
> <mailto:[EMAIL PROTECTED]>
> >
> >
> > --
> > To unsubscribe, e-mail:
> <mailto:[EMAIL PROTECTED]>
> > For additional commands, e-mail:
> <mailto:[EMAIL PROTECTED]>
> >
>
>
> --
> To unsubscribe, e-mail:
<mailto:[EMAIL PROTECTED]>
> For additional commands, e-mail:
<mailto:[EMAIL PROTECTED]>
>
--
To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>