Jesse, > On 29. 5. 2020, at 3:15 PM, Jesse Tayler <jtay...@oeinc.com> wrote: > Well, I don’t know but I think the fact you’d be having this problem points > to another problem.
Perhaps it does, but frankly, I can't see that. > I don’t know your traffic or architecture, but you really should not have > that problem at all, I wonder if perhaps you are creating too many separate > ECs? Not really. Unless I am missing something of importance (do please correct me if so), the problem can happen whenever (i) there are at least two different ECs; (ii) through which different properties of same object are changed; (iii) and, at least one EC is locked before changes and unlocked after saving (otherwise, we'd get the possible solution (a)); in a multi-threaded environment (background threads, WOAllowsConcurrentRequestHandling, or both). > I find myself generally putting the bulk of operations into one EC and > sometimes creating others for one-off stuff or just because it is a linear > process with clear results. Well each user would have normally his own EC in his own session; along with WOAllowsConcurrentRequestHandling and standard EC locking that would be quite sufficient to create the problem, I believe, far as (ii) and (iii) happen, too. Normally with Wonder auto-locking (iii) does; as for (ii), it depends on the code, but is pretty probable. Background threads do not change the principle at all; they just might increase the probability this happens if they lock their ECs, for they typically run for much longer than R/R loop workers. > Recently I did a lot of invalidating objects based on flags that were likely > to require updating from a remote thread, this isn’t a conflict but I did > need to ensure those objects were fetched and their contents written over > what is in memory. > > In that case, the user operating the session was really the only part that > needed instant updates and only in certain circumstances. Well if your session user's code either updates the changes (through unlock/lock? Or in antoher way? If so, how?) or does not make his own changes (of other properties of the same object), or does not save after the thread does, there's no problem I guess. > Anyway, I’d think about the problem more broadly since I’m personally > confident WO/Wonder has the most logical locking and EC handling that has > been honed and crafter over decades and used in all kinds of situations. I believe the root of the problem is the shared snapshot, which is a WO-level quirk (understandable to save memory, but potentially leading to this pitfall). > That confidence would lead me to at least try and solve your issues in > another way perhaps Perhaps, I'd be grateful for any reasonable way to solve it. The (a) — shown in the test code below, too — still looks to me as a pretty nice and easy solution; I just wonder why Wonder (sorry, couldn't resist) does not do that automatically in ERXEC.saveChanges? Would it perhaps cause some other grave problems which I can't see myself (but will be bit by them hard if I try to do that myself in my own EC subclass)? To better elucidate, it can be tested by a code similar to this (it's Groovy, and a bit tweaked at that; but I believe readable and understandable enough — I must admit in those years I use this infinitely better language I essentially forgot how to write in pure Java and would take me a small eternity to re-write into the darned thing :)) === WOComponent test(unlockEC) { eo.foo="new" // eo's some EO in defaultEC; assume string attributes foo/bar, works with any property same way def entity=eo.entityName,key=eo.rawPrimaryKey // for simplicity. Works with any way of fetching the object Thread.start { // thread just simulates another user changing 'bar' and saving in his own EC of his own session ERXEC ec=ERXEC.newEditingContext() // locking here irrelevant: neither harms nor helps in this case def obj=EOUtilities.objectWithPrimaryKeyValue(ec,entity,key) obj.bar="new" ec.saveChanges() println "thread: saved, foo is '$obj.foo' bar '$obj.bar'" // both would be new; so far so good } Thread.sleep(100) // simulate just a bit slow processing of whatever if (unlockEC) { // this fixes the problem in a way of (a) mentioned below eo.editingContext.unlock() // ... for here the changes from the thread are merged-in eo.editingContext.lock() } eo.editingContext.saveChanges() println "saved, foo is '$eo.foo' bar '$eo.bar'" // if !unlockEC, bar would be old, oops! } WOComponent test { test(NO) } // this action causes the problem (that after all's done, bar's again the old one) WOComponent testUnlocking { test(YES) } // this sports the possible-solution (a) and works properly === If you don't like the thread, precisely the same can be done with two actions — one with the delay and the other without — triggered by two separate users from two separate sessions with WOAllowsConcurrentRequestHandling set; it's the very same principle, just a bit more work to set up and test :) Thanks and all the best, OC > >> On May 29, 2020, at 9:00 AM, OCsite via Webobjects-dev >> <webobjects-dev@lists.apple.com> wrote: >> >> Hi there, >> >> just again, I've been bit in my tender parts by the well-known problem that >> >> 1. thread A locks its EC >> 2. thread B saves some change into DB >> 3. thread A saves its own changes (of different properties), which alas as a >> side-effect also reverts B's changes ... >> >> ... since A's EC still contains the state before B's changes, and when that >> state is compared with the shared snapshot (updated in step 2), the original >> outdated object state before B's changes looks like a new change done by A >> and thus is saved. >> >> Isn't there some general solution of this problem? I can think of at least >> three: >> >> (a) always unlock (and immediately re-lock) EC before saving, preferably >> directly in the ERXEC.saveChanges() code (resp. overridden saveChanges of my >> own ERXEC subclass, set as er.extensions.ERXEC.editingContextClassName). >> That, far as I understand, would merge all the other changes (of which EOF >> already well knows due to the notifications, but it can't merge them whilst >> the EC is locked). >> >> That would be pretty easy to do, but I am not sure of other dangers which it >> possibly might bring? There could be plenty, I am afraid. >> >> (b) implement EC-based non-shared snapshots and use them instead of (or >> rather along with) the shared ones to determine what to save into the DB. >> >> That would be considerably more work, again with possible dangers which I >> can't quite see now. >> >> (c) invent a scheme with timestamps attached to individual properties in >> shapshots and compare them, letting the newest win. >> >> Probably too much at the rube-goldbergish side to be practical. >> >> Has somebody already tried and tested either (a) or (b) or (c) or any other >> approach, better than those three, to alleviate this kind of problems with a >> success? >> >> Thanks, >> OC >> _______________________________________________ >> 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/jtayler%40oeinc.com >> >> This email sent to jtay...@oeinc.com >
_______________________________________________ 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