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

Reply via email to