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> 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) > 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