Just a quick followup: I've tested further, and alas, looks like the very same problem may happen regardless the fetchTimestamp due to inverse flattened M:N relationships.
Test case same as below, just without 1/4, but with a flattened M:N two-side relationship; culprit in (vii) very slightly different — the original relationship gets filled from the snapshot without any problem, but the inverse may (quite probably) need to be fetched, with precisely the same outcome. Sigh. > On 9. 6. 2024, at 2:46, ocs--- via Webobjects-dev > <webobjects-dev@lists.apple.com> wrote: > > Hi there again, > > I was eventually able to repeat the problem and it sort of stinks by a bug in > EOF (or I am doing something wrong, of course, as so often :)) My test code > is just a wee bit at the convoluted side, based on my own model and besides > written in Groovy, thus there's no point in quoting it; but the gist is this: > > 1. create an EC1, assign it a fetchTimestamp in future (something like > System.currentTimeMillis()+1500 should suffice) > 2. lock EC1 > 3. fetch into EC1 an object with a :N relationship and change that > relationship, not saving the changes > 4. wait until the fetchTimestamp expires > 5. in a separate thread with its own EC2 (locked) change the same > relationship of the same object (localInstanceIn(EC2) of course) > 6. save the changes in EC2 > 7. back in the original thread, wait until the thread with steps 5 and 6 > ends. Then unlock EC1 > 8. finally, in an unrelated EC3 insert some unrelated new object and > saveChanges... > > ... and at least at my side, I get “Cannot obtain globalId for an object > which is registered in an other than the databaseContext's active > editingContext“ exception all right! > > I believe the steps above are completely valid and should not cause problems, > or do I overlook something here? > > I might be wrong, it is a bit at the complex side, but I think this is what > happens under the hood: > (i) EC3 saveChanges of step 8 just calls > EOObjectStoreCoordinator.saveChangesInEditingContext ... > (ii) which first makes sure there's a proper source (happens to be > EODatabaseContext) for each inserted object in its _sources; this > EODatabaseContext happens to be the only shared EODatabaseContext a simple > application like this one uses (no more OSCs, no concurrent DB access) > (iii) each _source gets _willPrepareForSave and then each _source gets > prepareForSaveWithCoordinator — and this sets its _editingContext to EC3! > (iv) furthermore each _source gets recordChangesInEditingContext, > performChanges, and eventually commitChanges > (v) inside of commitChanges, EODatabaseContext finds that some changes really > happened and posts EOGlobalIDChangedNotification > (vi) EC1, which up to this moment was dormant, observes this notification and > through _globalIDChanged, _sendOrEnqueueNotification, yadda yadda, eventually > gets to merge the change into its own object > (vii) which change includes checking the current relationship value. Since > the fetchTimestamp is already in the past, this involves re-fetching the > relationship in EC1 ... > (viii) ... which goes through the one and only shared EODatabaseContext we > have. That EODatabaseContext though still has its _editingContext set to EC3 > from the step (iii))! > > Hilarity ensues, the “Cannot obtain globalId for an object which is > registered in an other...” exception gets thrown (if this did not happen, the > EODatabaseContext's _editingContext would get nulled at the very end of > commitChanges, too late for us). > > Do I overlook something of importance? Seems me not to; I guess anyone can > repeat the test code 1-8 outlined above (just beware logs — they might fire > the fault prematurely; also I've found essentially each localInstanceIn locks > ECs and thus disrupts the normal processing — best to prepare all the ECs and > all their local instances at the very start, around the step 1). > > What would be the best fix/work-around? I can't see any simple one; and I > would rather not override and re-implement the complete and rather > non-trivial commitChanges method. > > Thanks and all the best, > OC > >> On 8. 6. 2024, at 4:38, ocs--- via Webobjects-dev >> <webobjects-dev@lists.apple.com <mailto:webobjects-dev@lists.apple.com>> >> wrote: >> >> Hi there, >> >> again in about 2 years the $subject did happen: >> >> “Cannot obtain globalId for an object which is registered in an other than >> the databaseContext's active editingContext, object: >> [USER#1000175@EC:34ea315d/OSC:77b7ffa4], databaseContext: >> com.webobjects.eoaccess.EODatabaseContext@512d92b, object's editingContext: >> EC:34ea315d/OSC:77b7ffa4, databaseContext's active editingContext: >> EC:7de5b11a/OSC:77b7ffa4“ >> >> (ECs are my subclasses which log out the appropriate OSC, too; did that when >> I've hunted for bugs in multi-OSC environment. Unimportant here, in this >> case all OSCs are same.) >> >> Again, as before, the cause were the changes previously stored for later in >> another EC (since when they actually happened, the EC was locked). The >> important stack items (far as I can say) are here (full stack at the end of >> the message, should it help): >> >> - EODatabaseContext._globalIDForObject(EODatabaseContext.java:4660) # >> failed, since this._editingContext=EC:7de5b11a, >> object.editingContext=EC:34ea315d >> - EOCustomObject.includeObjectIntoPropertyWithKey(EOCustomObject.java:904) # >> pretty sure both were in the same EC, 34ea315d >> - EOEditingContext._processNotificationQueue(EOEditingContext.java:4741) # >> should be EC:34ea315d, whose merge queue happens to be non-empty >> - EOEditingContext._globalIDChanged(EOEditingContext.java:2038) # >> EC:34ea315d received the notification and tries to merge stored changes >> - EODatabaseContext.commitChanges(EODatabaseContext.java:6377) # >> postNotification "EOGlobalIDChangedNotification" >> - >> EOObjectStoreCoordinator.saveChangesInEditingContext(EOObjectStoreCoordinator.java:386) >> # EC:7de5b11a, normal save, happens to be a single insert, which is >> quadruple weird :-O (I believe a single insert actually should not trigger >> change merging?!?) >> >> I've spent a cosy night over the logs and Wonder sources and the >> documentation etc., and found sweet nothing — I'm still not the slightest >> bit smarter than 2 yrs ago :( >> >> It seems to me that >> (a) it is quite normal that during save of EC:A, any number of ECs:B,C,D... >> could be merging changes saved for later in their >> EOGlobalIDChangedNotification handlers >> (b) which should, though, never ever happen while EC:A is set as the EODBC's >> active context — actually the contexts should be set properly for each the >> EC. >> >> Can anybody perhaps see the possible culprit? >> >> It really does not seem a cross-EC relationship can be the culprit. I might >> still overlook something, but I am pretty darn sure that the >> includeObjectIntoPropertyWithKey method has been called with both the >> receiver and object in the same EC, the one which was merging its previously >> stored changes, 34ea315d. >> >> I am pretty sure the culprit was that at the moment this happened, the >> EODBC's active context was the one which did run the original save, 7de5b11a. >> >> How the B.H. is that possible though? >> >> Note: far as I can say with my logs, there happened to be *NO* thread >> concurrency at the moment, which makes it even more weird. Far as my logs >> say, no other thread did *anything* when my worker thread (a) saved a single >> simple insert in EC:7de5b11a, (b) which, as as side-effect, caused the >> EC:34ea315d to merge its previously saved changes, (c) which threw the >> exception as detailed above. Sigh. >> >> On the other hand, a (successful, uneventful) save in EC:7de5b11a which _did >> change_ the very USER#1000175 object did happen _very shortly_ before (about >> 20 ms before). In the same thread, in the same EC. I can't quite see how it >> can be important (far as I can say, there's no timer like “having saved X, >> merge changes to other ECs in couple of millis“ or so), but then, I still >> can be overlooking something of great importance. >> >> Thanks a lot for any insight, >> OC >> >> === full stack in case it helps (did not to me :/ ) >> Note please OCSEC is my own ERXEC subclass, which in this case does >> essentially nothing but logs out. Especially >> OCSEC._processObjectStoreChanges just logs (so that I know whether the merge >> happens or not, and if it does, with what data) and then calls super. Same >> with my own lock(), it does essentially nothing (first calls super and only >> then logs out, which in this case did not happen, for the exception happened >> inside of super.lock): >> >> - EODatabaseContext._globalIDForObject(EODatabaseContext.java:4660) >> - EODatabaseContext.databaseOperationForObject(EODatabaseContext.java:4767) >> - EODatabaseContext.valuesForKeys(EODatabaseContext.java:6535) >> - EOObjectStoreCoordinator.valuesForKeys(EOObjectStoreCoordinator.java:326) >> - >> EOQualifierSQLGeneration$_KeyValueQualifierSupport.schemaBasedQualifierWithRootEntity(EOQualifierSQLGeneration.java:439) >> - >> ERXExtensions$KeyValueQualifierSQLGenerationSupport.schemaBasedQualifierWithRootEntity(ERXExtensions.java:661) >> - >> EOQualifierSQLGeneration$Support._schemaBasedQualifierWithRootEntity(EOQualifierSQLGeneration.java:179) >> - >> EODatabaseChannel.selectObjectsWithFetchSpecification(EODatabaseChannel.java:227) >> - >> EODatabaseContext._objectsWithFetchSpecificationEditingContext(EODatabaseContext.java:3055) >> - >> EODatabaseContext.objectsWithFetchSpecification(EODatabaseContext.java:3195) >> - >> EOObjectStoreCoordinator.objectsWithFetchSpecification(EOObjectStoreCoordinator.java:488) >> - EOEditingContext.objectsWithFetchSpecification(EOEditingContext.java:4069) >> - ERXEC.objectsWithFetchSpecification(ERXEC.java:1307) >> - EODatabaseContext.objectsForSourceGlobalID(EODatabaseContext.java:4084) >> - >> EOObjectStoreCoordinator.objectsForSourceGlobalID(EOObjectStoreCoordinator.java:634) >> - EOEditingContext.objectsForSourceGlobalID(EOEditingContext.java:3923) >> - ERXEC.objectsForSourceGlobalID(ERXEC.java:1267) >> - EODatabaseContext._fireArrayFault(EODatabaseContext.java:4245) >> - >> EOAccessArrayFaultHandler.completeInitializationOfObject(EOAccessArrayFaultHandler.java:77) >> - _EOCheapCopyMutableArray.willRead(_EOCheapCopyMutableArray.java:39) >> - _EOCheapCopyMutableArray.count(_EOCheapCopyMutableArray.java:99) >> - NSArray.containsObject(NSArray.java:459) >> - EOCustomObject.includeObjectIntoPropertyWithKey(EOCustomObject.java:904) >> - >> ERXGenericRecord.includeObjectIntoPropertyWithKey(ERXGenericRecord.java:1291) >> - ERXGenericRecord$includeObjectIntoPropertyWithKey$6.callCurrent(:-1) >> - ERXGenericRecord$includeObjectIntoPropertyWithKey$6.callCurrent(:-1) >> - _DBUser.addToAuctionAccess(_DBUser.groovy:277) >> - NSSelector._safeInvokeMethod(NSSelector.java:122) >> - EOCustomObject.addObjectToPropertyWithKey(EOCustomObject.java:940) >> - EOEditingContext._mergeValueForKey(EOEditingContext.java:660) >> - EOEditingContext._mergeObjectWithChanges(EOEditingContext.java:3457) >> - EOEditingContext._processObjectStoreChanges(EOEditingContext.java:3546) >> - ERXEC._processObjectStoreChanges(ERXEC.java:1555) >> - OCSEC.super$4$_processObjectStoreChanges(OCSEnterpriseObject.groovy:-1) >> - OCSEC._processObjectStoreChanges(OCSEnterpriseObject.groovy:137) >> - NSSelector.invoke(NSSelector.java:358) >> - NSSelector._safeInvokeSelector(NSSelector.java:110) >> - EOEditingContext._processNotificationQueue(EOEditingContext.java:4741) >> - EOEditingContext.lock(EOEditingContext.java:4620) >> - ERXEC.lock(ERXEC.java:568) >> - OCSEC.super$4$lock(OCSEnterpriseObject.groovy:-1) >> - OCSEC.lock(OCSEnterpriseObject.groovy:224) >> - EOEditingContext.tryLock(EOEditingContext.java:4632) >> - EOEditingContext._sendOrEnqueueNotification(EOEditingContext.java:4705) >> - EOEditingContext._globalIDChanged(EOEditingContext.java:2038) >> - NSSelector._safeInvokeMethod(NSSelector.java:122) >> - NSNotificationCenter$_Entry.invokeMethod(NSNotificationCenter.java:588) >> - NSNotificationCenter.postNotification(NSNotificationCenter.java:532) >> - NSNotificationCenter.postNotification(NSNotificationCenter.java:562) >> - >> EOObjectStoreCoordinator._globalIDsChangedInSubStore(EOObjectStoreCoordinator.java:698) >> - NSSelector._safeInvokeMethod(NSSelector.java:122) >> - NSNotificationCenter$_Entry.invokeMethod(NSNotificationCenter.java:588) >> - NSNotificationCenter.postNotification(NSNotificationCenter.java:532) >> - NSNotificationCenter.postNotification(NSNotificationCenter.java:562) >> - EODatabaseContext.commitChanges(EODatabaseContext.java:6377) >> - >> EOObjectStoreCoordinator.saveChangesInEditingContext(EOObjectStoreCoordinator.java:386) >> - EOEditingContext.saveChanges(EOEditingContext.java:3192) >> - ERXEC._saveChanges(ERXEC.java:1178) >> - ERXEC.saveChanges(ERXEC.java:1099) >> - OCSEC.super$4$saveChanges(OCSEnterpriseObject.groovy:-1) >> - OCSEC.saveChanges(OCSEnterpriseObject.groovy:106) >> - OCSEC$saveChanges.call(:-1) >> >> _______________________________________________ >> Do not post admin requests to the list. They will be ignored. >> Webobjects-dev mailing list (Webobjects-dev@lists.apple.com >> <mailto:Webobjects-dev@lists.apple.com>) >> Help/Unsubscribe/Update your Subscription: >> https://lists.apple.com/mailman/options/webobjects-dev/ocs%40ocs.cz >> >> This email sent to o...@ocs.cz > > _______________________________________________ > Do not post admin requests to the list. They will be ignored. > Webobjects-dev mailing list (Webobjects-dev@lists.apple.com) > Help/Unsubscribe/Update your Subscription: > https://lists.apple.com/mailman/options/webobjects-dev/ocs%40ocs.cz > > This email sent to o...@ocs.cz
_______________________________________________ Do not post admin requests to the list. They will be ignored. Webobjects-dev mailing list (Webobjects-dev@lists.apple.com) Help/Unsubscribe/Update your Subscription: https://lists.apple.com/mailman/options/webobjects-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com