Thanks a lot, but at the moment and in the hurry I would slightly prefer (a) the information whether it is normal for EOF to contain at the same moment updated value of FK and stale value of a relationship in the same EC; (b) if so, what's the best way to make them consistent.
(Truth is, for all those 20-odd years I am using EOF without any bigger problem I have always used the default setting of WOAllowsConcurrentRequestHandling=NO, and thus I indeed am pretty inexperienced with this, alas.) Thanks again, OC On 17. 2. 2015, at 18:30, Ramsey Gurley <[email protected]> wrote: > I think it would help you to explore the actual behavior of EOF before making > too many more bad assumptions. > > http://www.wocommunity.org/podcasts/wowodc/east09/WOWODC09E-EOEnterpriseObjects.mov > > First watch the whole presentation. In that presentation, there’s a demo with > an app called freshness explorer. You will probably be interested in running > it locally. You can find a copy of it here > > https://github.com/nullterminated/ponder/blob/master/ERR2d2w/Support/FreshnessExplorer.zip > > I think you will find it enlightening. If you’d like to see sql transaction > logging in freshness explorer, you need to direct it at a database instead of > using Wonder's memory adaptor. > > > On Feb 17, 2015, at 6:00 AM, OC <[email protected]> wrote: > >> On 16. 2. 2015, at 9:52, OC <[email protected]> wrote: >> >>> Nevertheless with the last valid thing I already took your advice and >>> modelled it -- I dodged changing the DB for I was lucky and I happened to >>> have in my model one legacy unused INTEGER attribute, which I used for a FK >>> -- and preliminarily, it seems to work excellently. >> >> Alas, it does not when there are concurrent threads -- in that case, one of >> them can still contain the old value for the relationship, although I am >> locking the OSC :( >> >> My current code looks essentially like this: >> >> === >> EOEditingContext ec=auction.editingContext() >> EOObjectStore osc=ec.rootObjectStore() >> osc.lock() >> try { >> println "OLD: $auction.lastValidPriceOffer()" // now a modelled >> :1 relationship, auction contains FK >> DBPriceOffer po=... new offer created and inserted to EC ... >> println "ENCACHED: $po" >> auction.setLastValidPriceOffer(po) >> ec.saveChanges() >> } finally { >> println "NEW: $auction.lastValidPriceOffer()" >> ocs.unlock() >> } >> === >> >> Now, I though this code is safe (single-instance, concurrent requests), but >> it is not. If the reqeusts come sequentially, it works perfectly, but with >> concurrent requests I am getting results like this >> >> === // WorkerThread5 and WorkerThread4 run concurrently >> # WorkerThread5 happened to lock first; WorkerThread4 waits all right >> 13:07:42.163|WorkerThread5 --- OLD <DBPriceOffer@2072296340 PK:1002835 >> Price:'888' by:'vilklient' /EC:1192846461> >> 17.2 13:07:42: ENCACHED: <DBPriceOffer@892254365 PK:null N Price:'887' >> by:'vilklient3' /EC:1192846461> [1] >> >> # ops logged in databaseContextWillPerformAdaptorOperations; note the new >> lastValidPriceOffer FK (1002836) _is_ stored properly in lvo_id (where it >> replaces the previous one, 1002835): >> - 1: INSERT on 'DBPriceOffer' 6{validOffer:true, uid:1002836, >> auction_id:1000755, price:887, creationDate:2015-02-17 12:07:42 Etc/GMT, >> creator_id:1000121} >> - 2: UPDATE on 'DBAuction' ((uid = 1000755) and (lvo_id = 1002835)) >> 1{lvo_id:1002836} >> >> # and just before unlocking, in this thread, lastValidPriceOffer is all right >> 13:07:42.235|WorkerThread5 --- NEW <DBPriceOffer@892254365 PK:1002836 >> Price:'887' by:'vilklient3' /EC:1192846461> >> >> # since WorkerThread5 did save all right and did unlock the OSC, >> WorkerThread4 starts -- and oops, it still has the wrong old value of >> lastValidPriceOffer! [2] >> 13:07:42.246|WorkerThread4 --- OLD: <DBPriceOffer@746572082 PK:1002835 >> Price:'888' by:'vilklient' /EC:1851717404> prc 888 au.cpc 888 >> >> # from now on, of course it's all wrong. Nevertheless it is interesting that >> the thread _does know_ the _new_ value of lvo_id (1002836), and thus saves >> the wrong offer! [3] >> - 1: INSERT on 'DBPriceOffer' 6{validOffer:true, uid:1002837, >> auction_id:1000755, price:887, creationDate:2015-02-17 12:07:42 Etc/GMT, >> creator_id:1000049} >> - 2: UPDATE on 'DBAuction' ((uid = 1000755) and (lvo_id = 1002836)) >> 1{lvo_id:1002837} >> === >> >> I must admit I am (just again) somewhat surprised. >> >> I rather presumed saveChanges would make sure values of all EOs (including >> the relationships) in all ECs in the same instance with just one OSC are >> consistent, and thus I would at [2] get the right value of >> lastValidPriceOffer -- the one stored (in another thread) before at [1], and >> successfully saved there. >> >> I considered there's a possibility I am wrong, and the EC's will not get >> synced properly, and I will still get the old value of lastValidPriceOffer >> at [2] -- but in that case I thought the auction itself in the same EC of >> the same thread would also contain the old FK value in its 'lvo_id', and >> since it is a locking attribute, I will get an optimistic locking fail, and >> the wrong value will not be saved at [3]. >> >> I must admit I can't really see how it is possible the disastrous >> combination of >> - the auction contains the _new_ foreign key; >> - at the same moment, the relationship returns the _old_ object?!? >> >> Note that it looks like some caching issue, for the problem never happens >> when saving is not concurrent. If the first thread's R/R loop finishes >> before the latter ones' starts, the relationship is consistent with the >> foreign key. >> >> Is this normal EOF behaviour, or does it indicate another weird problem in >> my app? >> >> And even more important -- how to fix it? What am I to do at the start/end >> of the OSC-locked critical section, so as I am sure that the FK stored >> inside the object and the relationship modelled on it are consistent? >> >> Thanks a big lot! >> OC >> >> === The relationship definition in the model plist: >> { >> deleteRule = EODeleteRuleDeny; >> destination = DBPriceOffer; >> isToMany = N; >> joinSemantic = EOInnerJoin; >> joins = ( >> { >> destinationAttribute = "uid"; >> sourceAttribute = lvo_id; >> }, >> ); >> name = lastValidPriceOfferCache; >> }, >> === >> >> === The code used to access the relationship: >> public DBPriceOffer lastValidPriceOffer { >> def cached=this.lastValidPriceOfferCache() >> if (cached) return cached >> ... legacy code to search for it for old auctions ... >> } >> DBPriceOffer lastValidPriceOfferCache() { >> storedValueForKey('lastValidPriceOfferCache') >> } >> === >> >> >> _______________________________________________ >> Do not post admin requests to the list. They will be ignored. >> Webobjects-dev mailing list ([email protected]) >> Help/Unsubscribe/Update your Subscription: >> https://lists.apple.com/mailman/options/webobjects-dev/rgurley%40smarthealth.com >> >> This email sent to [email protected] > _______________________________________________ Do not post admin requests to the list. They will be ignored. Webobjects-dev mailing list ([email protected]) Help/Unsubscribe/Update your Subscription: https://lists.apple.com/mailman/options/webobjects-dev/archive%40mail-archive.com This email sent to [email protected]
