[ 
https://issues.apache.org/jira/browse/CAY-2792?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17685145#comment-17685145
 ] 

Nikita Timofeev commented on CAY-2792:
--------------------------------------

[~mattraydub] Thanks for the report and for such a detailed info, test case and 
even a solution analysis! As for the fix, could you please check if this patch 
fixes the problem? I think we could avoid {{ConcurrentModificationException}} 
as this code is using sublist and we are past iteration point.
{code:java}
@@ -63,12 +63,12 @@
         DbRowOp lastRow = null;
         for(DbRowOp row : sortedDbRows) {
             if (row.getEntity() != lastEntity) {
-                start = idx;
                 if(lastEntity != null && sorter.isReflexive(lastEntity)) {
                     ObjEntity objEntity = 
resolver.getObjEntity(lastRow.getObject().getObjectId().getEntityName());
                     List<DbRowOp> reflexiveSublist = 
sortedDbRows.subList(start, idx);
                     sorter.sortObjectsForEntity(objEntity, reflexiveSublist, 
lastRow instanceof DeleteDbRowOp);
                 }
+                start = idx;
                 lastEntity = row.getEntity();
             }
             lastRow = row;
{code}
We could also try the optimizations you suggest in the master branch, while 
it's just a first milestone.

> Fix Insertion Order For Reflexive DataObjects
> ---------------------------------------------
>
>                 Key: CAY-2792
>                 URL: https://issues.apache.org/jira/browse/CAY-2792
>             Project: Cayenne
>          Issue Type: Bug
>          Components: Core Library
>    Affects Versions: 4.2.RC2, 4.2.RC1
>         Environment: Java 17
>            Reporter: Matt Watson
>            Assignee: Nikita Timofeev
>            Priority: Critical
>             Fix For: 4.2, 5.0.M1
>
>          Time Spent: 10m
>  Remaining Estimate: 0h
>
> For terminology's sake, I assume _Reflexive_ refers to when you have 2 
> objects of the same Model, that are related, in this case, parent/child 
> (ToOne/HasMany). If this assumption is incorrect we can rename the title.
> For a little history on this, we never encountered this bug until we upgraded 
> to {{4.2.RC1}}, and I recently tested on {{4.2.RC2}} and the bug still exists.
> For a while I thought this was only a bug in our environment, thinking that 
> our code was causing the issue because Cayenne has a passing test in 
> {{CayenneDataObjectRelationshipsIT.testReflexiveRelationshipInsertOrder1}}. 
> But as I got to digging, the error occurs when the parent also has a 
> relationship to some "Other" object that is not of the same model.
> The [pull request|https://github.com/apache/cayenne/pull/563] has a breaking 
> test. I have not worked on a fix yet, but will begin investigating. I suspect 
> it has to do with the work done in CAY-2571, but I am not certain yet.
> The issue is that Cayenne randomly tries to commit the child record in front 
> of the parent record, therefore the database throws a Foreign Key error, 
> since the parent does not yet exist when the child is inserted. Because of 
> this randomness, my breaking test attempts this process 100 times. You will 
> also see another test that passes with the same Models but without setting 
> the "ToOne" Other object.
> here is a look at the code that "randomly" fails:
> {code:java}
>     public void addReflexiveParentAndChildWithOtherRelationshipOnParent() {
>         // can add Reflexive Parent (that belongsTo Other) and Child,
>         // we will do this 100 times, because it randomly does it 
> correctly/incorrectly
>         // given some "other" Object
>         final Other other = context.newObject(Other.class);
>         other.setName("OtherB");
>         context.commitChanges();
>         final int attempts = 100;
>         int errors = 0;
>         for (int i = 0; i < attempts; i++) {
>             // when parent is created and associated to "Other"
>             final Reflexive parent = context.newObject(Reflexive.class);
>             parent.setName("parentB"+i);
>             parent.setToOther(other);
>             // and child is created and associated to "Parent"
>             final Reflexive child = context.newObject(Reflexive.class);
>             child.setName("childB"+i);
>             child.setToParent(parent);
>             try {
>                 context.commitChanges();
>             } catch (final Exception e) {
>                 errors++;
>                 e.printStackTrace();
>                 context.rollbackChanges();
>             }
>         }
>         // then no error occurred
>         assertEquals(String.format("Failed on %s of %s attempts.", errors, 
> attempts), 0, errors);
>     }
> {code}
>  



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to