castor

Wrong timestamps for same object having hierarchy when loaded by many referers

Details

  • Type: Bug Bug
  • Status: Closed Closed
  • Priority: Blocker Blocker
  • Resolution: Fixed
  • Affects Version/s: 0.9.9 M2
  • Fix Version/s: 1.3.1
  • Component/s: JDO
  • Labels:
    None
  • Testcase included:
    yes
  • Number of attachments :
    6

Description

for long transactions, when you had two diferent classes that references another base class, of a class hierarchy, Castor calculates diferent timestamps for each instance.

Test case attached.

  1. patch.txt
    19/Sep/05 4:08 PM
    9 kB
    Werner Guttmann
  2. patch-C1217-20050916.txt
    16/Sep/05 1:30 PM
    38 kB
    Clovis Wichoski
  3. patch-C1217-20050917.txt
    17/Sep/05 12:35 PM
    20 kB
    Clovis Wichoski
  4. patch-C1217-20080921.txt
    21/Sep/08 5:14 PM
    16 kB
    Ralf Joachim
  5. patch-C1217-20080924.txt
    24/Sep/08 11:38 AM
    16 kB
    Ralf Joachim
  6. patch-C1217-20090214.txt
    14/Feb/09 2:40 PM
    43 kB
    Ralf Joachim

Activity

Hide
Clovis Wichoski added a comment -

testCase added

Show
Clovis Wichoski added a comment - testCase added
Hide
Clovis Wichoski added a comment -

after chat with ralf, a more simple testCase, attached, the former, is too complex, i think that the trouble is with only complex object graph, but its occurs with more simple object graphs.

in resume if you query, Product that have extends, in two diferent transactions you get same timestamps for both, but if you query a object that references to Product, the timestamps of this referenced Product is diferent than those one queried before.

Show
Clovis Wichoski added a comment - after chat with ralf, a more simple testCase, attached, the former, is too complex, i think that the trouble is with only complex object graph, but its occurs with more simple object graphs. in resume if you query, Product that have extends, in two diferent transactions you get same timestamps for both, but if you query a object that references to Product, the timestamps of this referenced Product is diferent than those one queried before.
Hide
Clovis Wichoski added a comment -

one discover, in my debugs, is that for queries of product, only ObjectLock for Part is created, and when query of CompanyProduct the references of product, cause Castor create a ObjectLock instances for Product and other for Part both instances with new timestamps, diferent from former that appears in queries.

Show
Clovis Wichoski added a comment - one discover, in my debugs, is that for queries of product, only ObjectLock for Part is created, and when query of CompanyProduct the references of product, cause Castor create a ObjectLock instances for Product and other for Part both instances with new timestamps, diferent from former that appears in queries.
Hide
Werner Guttmann added a comment -

The last patch has a couple of small code changes in the area where we need to un/-track objects after we have loaded them with a different ClassMolder. Problem is that four tests of CTF fail, but it looks like it's the same error again and again. Maybe somebody else is capable of identifying the problem ....

Show
Werner Guttmann added a comment - The last patch has a couple of small code changes in the area where we need to un/-track objects after we have loaded them with a different ClassMolder. Problem is that four tests of CTF fail, but it looks like it's the same error again and again. Maybe somebody else is capable of identifying the problem ....
Hide
Werner Guttmann added a comment -

I think I found the actual 'real' problem. In ClassMolder.loadFields(), at the bottom, there's a call to

locker.setObject(tx, fields );

that will get called on the current ObjectLock instance. It's that call that sometimes fails ... problem is, I still don't have a full understanding of why and in what case it fails. Having said that, I think that this call should only be called if the object to be loaded has not been expanded. Lokoing at the CVS logs of this file, this is what it has been historically. I do have an idea as to what could and should be done to resolve this, on the lines of adding a ObjectLock.setObject(fields) method that simply sets the fields as obtained within ClassMolder.loadFields() onto the newly created ObjectLock instance. More on this later ...

Show
Werner Guttmann added a comment - I think I found the actual 'real' problem. In ClassMolder.loadFields(), at the bottom, there's a call to locker.setObject(tx, fields ); that will get called on the current ObjectLock instance. It's that call that sometimes fails ... problem is, I still don't have a full understanding of why and in what case it fails. Having said that, I think that this call should only be called if the object to be loaded has not been expanded. Lokoing at the CVS logs of this file, this is what it has been historically. I do have an idea as to what could and should be done to resolve this, on the lines of adding a ObjectLock.setObject(fields) method that simply sets the fields as obtained within ClassMolder.loadFields() onto the newly created ObjectLock instance. More on this later ...
Hide
Ralf Joachim added a comment -

As far as I understand castor's current mechanism to maintain timestamps they always reflect the latest access to the database be it a create, update or load. There are only 2 possibilities to change this behaviour:

  • persist timestamps in the database
  • only change cache and therefore the objects timestamp if the object currently loaded differs from the cached one

both solutions seam to be quite difficult to resolve.

Show
Ralf Joachim added a comment - As far as I understand castor's current mechanism to maintain timestamps they always reflect the latest access to the database be it a create, update or load. There are only 2 possibilities to change this behaviour:
  • persist timestamps in the database
  • only change cache and therefore the objects timestamp if the object currently loaded differs from the cached one
both solutions seam to be quite difficult to resolve.
Hide
Ralf Joachim added a comment -

Integrated test case of Clovis into CPA test suite and slightly improved it to debug what's happening.

I found out that the problem with changed timestamps of cached entities only happens when the referenced entity is part of an extended class hierarchy (polymorphism). As always the referenced entity is loaded by PersistenceCapableRelationResolver. In case of a extended class hierarchy load is called for the base entity of this hierarchy. At TypeInfo.acquire Castor finds the extended entity in cache but as it is of the wrong type, extended entity instead of the base entity requested, load will continue and query entity once again from database.

Show
Ralf Joachim added a comment - Integrated test case of Clovis into CPA test suite and slightly improved it to debug what's happening. I found out that the problem with changed timestamps of cached entities only happens when the referenced entity is part of an extended class hierarchy (polymorphism). As always the referenced entity is loaded by PersistenceCapableRelationResolver. In case of a extended class hierarchy load is called for the base entity of this hierarchy. At TypeInfo.acquire Castor finds the extended entity in cache but as it is of the wrong type, extended entity instead of the base entity requested, load will continue and query entity once again from database.
Hide
Ralf Joachim added a comment -

Fixed an typo in test case.

I spend some more time to look into this but couldn't find a simple solution. ATM it seams that cache doesn't work in general for extended class hierarchies. The problem is that TransactionContext.load() first instantiates an instance of the expected type and then calls LockEngine.load() with it. When Castor detects that the entity should not be of the expected type, be it when entity got found in cache or loaded from database for the first time, LockEngine.load() returns with information about real type of the entity. Having said that the correct entity gets removed from cache during this call of LockEngine.load() and repaced by the wrong one. TransactionContext.load() now once again instantiates an entity but this time of the real type and calls LockEngine.load() once again. As the correct entity from cache has been removed during the first call to LockEngine.load() the Object[] with the filed values loaded from database are used this time and a new timestamp gets assigned.

To solve this I think we have to refactor current LockEngine.load() and everything that's called from it into 2 separate methods. The first one determines the type of the entity and loads field values into the Object[]. The second method only needs to mold this field values into the real entity constructed by TransactionContext.load() between the call to this 2 methods. Having said that I expect this to require quite some time.

Show
Ralf Joachim added a comment - Fixed an typo in test case. I spend some more time to look into this but couldn't find a simple solution. ATM it seams that cache doesn't work in general for extended class hierarchies. The problem is that TransactionContext.load() first instantiates an instance of the expected type and then calls LockEngine.load() with it. When Castor detects that the entity should not be of the expected type, be it when entity got found in cache or loaded from database for the first time, LockEngine.load() returns with information about real type of the entity. Having said that the correct entity gets removed from cache during this call of LockEngine.load() and repaced by the wrong one. TransactionContext.load() now once again instantiates an entity but this time of the real type and calls LockEngine.load() once again. As the correct entity from cache has been removed during the first call to LockEngine.load() the Object[] with the filed values loaded from database are used this time and a new timestamp gets assigned. To solve this I think we have to refactor current LockEngine.load() and everything that's called from it into 2 separate methods. The first one determines the type of the entity and loads field values into the Object[]. The second method only needs to mold this field values into the real entity constructed by TransactionContext.load() between the call to this 2 methods. Having said that I expect this to require quite some time.
Hide
Ralf Joachim added a comment -

Final patch.

Show
Ralf Joachim added a comment - Final patch.

People

Vote (0)
Watch (1)

Dates

  • Created:
    Updated:
    Resolved:

Time Tracking

Estimated:
Not Specified
Original Estimate - Not Specified
Remaining:
0m
Remaining Estimate - 0 minutes
Logged:
5h 40m
Time Spent - 5 hours, 40 minutes