Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 319836 - Detach-reattach of dirty object discards pre-detach featureDeltas
Summary: Detach-reattach of dirty object discards pre-detach featureDeltas
Status: CLOSED FIXED
Alias: None
Product: EMF
Classification: Modeling
Component: cdo.core (show other bugs)
Version: 4.0   Edit
Hardware: All All
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: Caspar D. CLA
QA Contact: Eike Stepper CLA
URL:
Whiteboard: offline-xx
Keywords:
: 321086 (view as bug list)
Depends on:
Blocks:
 
Reported: 2010-07-14 07:47 EDT by Cyril Jaquier CLA
Modified: 2011-06-23 03:42 EDT (History)
2 users (show)

See Also:
stepper: review+


Attachments
Test Case v1 (7.02 KB, patch)
2010-07-14 07:49 EDT, Cyril Jaquier CLA
stepper: iplog+
Details | Diff
Test Case v2 (9.90 KB, patch)
2010-07-14 08:21 EDT, Cyril Jaquier CLA
no flags Details | Diff
Test Case v3 (12.56 KB, patch)
2010-07-14 08:43 EDT, Cyril Jaquier CLA
no flags Details | Diff
Small fix to CDOSavepointImpl - does not fix the bug! (1.50 KB, patch)
2010-07-15 05:17 EDT, Eike Stepper CLA
no flags Details | Diff
Test Case v4 (6.92 KB, patch)
2010-07-15 05:45 EDT, Cyril Jaquier CLA
stepper: iplog+
Details | Diff
Patch (10.88 KB, patch)
2010-07-20 03:29 EDT, Caspar D. CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Cyril Jaquier CLA 2010-07-14 07:47:05 EDT
Build Identifier: HEAD

Moving nodes in a tree structure (e.g using NodeA from model3) like a DragAndDropCommand would do (remove => add) may result in an inconsistent tree (nodes disappear).

Reproducible: Always
Comment 1 Cyril Jaquier CLA 2010-07-14 07:49:10 EDT
Created attachment 174283 [details]
Test Case v1
Comment 2 Cyril Jaquier CLA 2010-07-14 07:58:02 EDT
Using the attached test case, here is the output WITHOUT "remove" before "add" (looks good):

Delta revision(s):
 NodeA@OID5:0v1
  CDOFeatureDelta[null, CONTAINER, resource=NULL, container=OID6, feature=-1]
 NodeA@OID6:0v1
  CDOFeatureDelta[null, CONTAINER, resource=NULL, container=OID2, feature=-1]
  CDOListFeatureDelta
   CDOFeatureDelta[children, ADD, value=OID5, index=0]
 NodeA@OID2:0v1
  CDOListFeatureDelta
   CDOFeatureDelta[children, ADD, value=OID6, index=0]
 NodeA@OID3:0v1
  CDOListFeatureDelta
   CDOFeatureDelta[children, REMOVE, value=UNKNOWN, index=2]
   CDOFeatureDelta[children, REMOVE, value=UNKNOWN, index=1]

And now the output WITH "remove" before "add" (looks not so good):

Delta revision(s):
 NodeA@OID6:0v1
  CDOListFeatureDelta
   CDOFeatureDelta[children, REMOVE, value=UNKNOWN, index=2]
   CDOFeatureDelta[children, REMOVE, value=UNKNOWN, index=1]
 NodeA@OID3:0v1
  CDOFeatureDelta[null, CONTAINER, resource=NULL, container=OID5, feature=-1]
 NodeA@OID5:0v1
  CDOListFeatureDelta
   CDOFeatureDelta[children, ADD, value=OID3, index=0]
 NodeA@OID2:0v1
  CDOFeatureDelta[null, CONTAINER, resource=NULL, container=OID3, feature=-1]

An ADD feature delta is missing. It has been generated but removed later from CDOSavepointImpl.
Comment 3 Cyril Jaquier CLA 2010-07-14 08:21:41 EDT
Created attachment 174285 [details]
Test Case v2

I added a variation of the first test case which duplicate nodes. It is the same bug but with a different consequence ;-)
Comment 4 Cyril Jaquier CLA 2010-07-14 08:43:53 EDT
Created attachment 174286 [details]
Test Case v3

Sorry, a new (and hopefully the last) variation on the same theme (bug) again. This one results in a cycle in the tree which get materialized as a StackOverflowError in our application (followed by a database cleanup...)
Comment 5 Eike Stepper CLA 2010-07-15 05:14:29 EDT
Cyril, please confirm that:

1) The number of lines that you changed is smaller than 250.
2) You are the only author of these changed lines.
3) You apply the EPL to these changed lines.
Comment 6 Eike Stepper CLA 2010-07-15 05:15:50 EDT
After some tests I feel that the problem is related to re-attachment (possibly in conjunction with detachment). Assgining to Jasper...
Comment 7 Eike Stepper CLA 2010-07-15 05:17:00 EDT
Created attachment 174378 [details]
Small fix to CDOSavepointImpl - does not fix the bug!
Comment 8 Cyril Jaquier CLA 2010-07-15 05:45:11 EDT
Created attachment 174381 [details]
Test Case v4

Slightly modified version of the test case to conform to the following points:

1) The number of lines that you changed is smaller than 250.

Yes

2) You are the only author of these changed lines.

Yes

3) You apply the EPL to these changed lines.

Yes
Comment 9 Eike Stepper CLA 2010-07-15 06:10:59 EDT
Comment on attachment 174381 [details]
Test Case v4

Committed to HEAD
Comment 10 Caspar D. CLA 2010-07-15 06:18:13 EDT
Proposed patch 'Small fix to CDOSavepointImpl' breaks rollback logic
where reattached objects are concerned. As discussed on Skype, CDOTransactionImpl.rollback needs objects that were 
detached-then-reattached within the same savePoint, to remain present
in detachedObjects, so that it can identify these special cases and
treat them appropriately.
Comment 11 Caspar D. CLA 2010-07-15 07:21:03 EDT
About the problem itself, it is indeed related to the re-attachment
logic, in that the reattachment support creates the opportunity for
this bug to arise.

What happens is that the normal detachment logic (which predates the
reattachment feature) assumes that when an object is detached, the
revision delta associated with it, can be discarded. This happens in
CDOSavePointImpl.java, line 73: 

  revisionDeltas.remove(key);

Oops. If that revDelta contained any featureDeltas, then those have 
been lost. If the object is later reattached, then the changes that
were made to that object prior to its detachment, are, as a 
consequence, missing from the TX.

In the testcase provided therefore, the removal of the n3 child from n2,
is lost from the TX when n2 itself is detached (although locally the change
is still known.) And so n2 still has n3 as its child when it's view in a
different session.

Good find, Cyril. I don't have time to fix it right now, but will do so
early next week.
Comment 12 Eike Stepper CLA 2010-07-15 12:05:07 EDT
I have found out the same thing in the meantime. My first attempt to fix it was to store the removed revision delta in a new map. Only to find out then that the logic in the ReattachTransition computes this delta anyway from comparing two states.

I now suspect that not the right states are used to do the comparison.

BTW. I have the feeling that other issues may arise from the formerRevisions map being stored in the transaction and not in the savepoints. Should we fix that, too, Caspar?
Comment 13 Caspar D. CLA 2010-07-19 05:48:11 EDT
(In reply to comment #12)

> I now suspect that not the right states are used to do the
> comparison.

Yeah, the comparison is done between the newly constructed
revision, the values of which are populated from the
CDOObject itself, and the revision that was backing the
object at the moment of detachment. Obviously this fails to
re-create the deltas that had affected the revision prior to
detachment.

> BTW. I have the feeling that other issues may arise from
> the formerRevisions map being stored in the transaction
> and not in the savepoints. Should we fix that, too,
> Caspar?

Well... let's see. The immediate issue is the restoration of
the deltas. Either we keep them somewhere, as you were
trying to do, or we re-create them. The latter requires as a
basis for comparison, the revision that backed the object in
its CLEAN state. So that is yet another reason why we need
the session to somehow hold on to the revisions that it
originally loaded (other reasons being bug 315026, bug
312534, and bug 312535). And if the session keeps these
"clean revisions" somewhere, I don't think we'll need the
formerRevisions map anymore.

I might go ahead and attempt to fix it this way... although
I'm still wondering about how we're going to make this work
with rollbacks-to-savepoints, passiveUpdates, and refreshes.
(That is: what is a "CLEAN" revision anyway? The one that
was originally loaded? Or the one that represents the last
known server state? Each answer has problematic implications
for the rollback and update features...)
Comment 14 Eike Stepper CLA 2010-07-19 06:32:51 EDT
(In reply to comment #13)
> > I now suspect that not the right states are used to do the
> > comparison.
> 
> Yeah, the comparison is done between the newly constructed
> revision, the values of which are populated from the
> CDOObject itself, and the revision that was backing the
> object at the moment of detachment. Obviously this fails to
> re-create the deltas that had affected the revision prior to
> detachment.

Can you fix this short term (as opposed to the other issues you mentin below)?

> > BTW. I have the feeling that other issues may arise from
> > the formerRevisions map being stored in the transaction
> > and not in the savepoints. Should we fix that, too,
> > Caspar?
> 
> Well... let's see. The immediate issue is the restoration of
> the deltas. Either we keep them somewhere, as you were
> trying to do, or we re-create them. The latter requires as a
> basis for comparison, the revision that backed the object in
> its CLEAN state. So that is yet another reason why we need
> the session to somehow hold on to the revisions that it
> originally loaded (other reasons being bug 315026, bug
> 312534, and bug 312535). And if the session keeps these
> "clean revisions" somewhere, I don't think we'll need the
> formerRevisions map anymore.

Would that include the ability to rollback to arbitrary savepoints? Or only to the first (implicit) one? I have the intuition that each save point should store what *it* thinks the "original" revision was.
 
> I might go ahead and attempt to fix it this way... although
> I'm still wondering about how we're going to make this work
> with rollbacks-to-savepoints, passiveUpdates, and refreshes.
> (That is: what is a "CLEAN" revision anyway? The one that
> was originally loaded? Or the one that represents the last
> known server state? Each answer has problematic implications
> for the rollback and update features...)

I agree that with passiveUpdates==true some subtle issues may arise, but in general I think that's accepted by users of this mode, otherwise they should not use it. Of course we can discuss if we know of better overall approaches to passiveUpdates but I think that should not be done in this bugzilla ;-)
Comment 15 Caspar D. CLA 2010-07-19 07:27:20 EDT
(In reply to comment #14)

> Can you fix this short term (as opposed to the other
> issues you mentin below)?

Will try.

> <SNIP> 
> Would that include the ability to rollback to arbitrary
> savepoints? Or only to the first (implicit) one?  I have
> the intuition that each save point should store what *it*
> thinks the "original" revision was.

What's the reason behind this intuition? As far as I can
oversee now, in all cases where the clean revision is needed
(see list of bugs above), it is irrelevant in which
savePoint the object first became dirty. And having a
separate cleanRevisions collection for each savePoint, would
introduce a significant computational burden, because a
clean revision would have to be looked up in the union of
all these separate collections. So if we have no clear
reason for this approach, I don't think it's a good idea.
But I might be overlooking sth...

BTW, in general the idea of a set of "clean revisions" is a
bit wobbly, since revisions aren't immutable :-( But I guess
we'll have to live with that; or alternatively, we can
introduce sth like a #freeze() method which makes the
revision immutable from then on. (This does make me wonder a
bit whether it was such a good idea to eliminate
#isTransactional. It would have come in handy, since
isTransactional == !isImmutable).

> <SNIP>
> I agree that with passiveUpdates==true some subtle issues
> may arise, but in general I think that's accepted by users
> of this mode, otherwise they should not use it. Of course
> we can discuss if we know of better overall approaches to
> passiveUpdates but I think that should not be done in this
> bugzilla ;-)

I doubt that the issues are "subtle" and "accepted", but I
agree we should have a separate Bugzilla for that
discussion. Will open shortly.
Comment 16 Caspar D. CLA 2010-07-20 03:29:13 EDT
Created attachment 174703 [details]
Patch
Comment 17 Eike Stepper CLA 2010-07-20 04:14:19 EDT
Thank you Caspar. Looks good and can be committed.
Comment 18 Caspar D. CLA 2010-07-20 04:37:45 EDT
Changed bug summary. Was: 
 "Moving nodes in a tree structure may result in an inconsistent tree"
Comment 19 Caspar D. CLA 2010-07-20 05:54:28 EDT
Committed to HEAD
Comment 20 Cyril Jaquier CLA 2010-07-20 06:28:14 EDT
(In reply to comment #19)
> Committed to HEAD

Thank you Caspar :-) Just tested your patch in our application and I was not able to reproduce this bug anymore.
Comment 21 Caspar D. CLA 2010-07-28 02:32:51 EDT
*** Bug 321086 has been marked as a duplicate of this bug. ***
Comment 22 Eike Stepper CLA 2011-06-23 03:42:04 EDT
Available in R20110608-1407