Re: EC locking/shared snapshots/old data saved
Re: EC locking/shared snapshots/old data saved
- Subject: Re: EC locking/shared snapshots/old data saved
- From: ocs--- via Webobjects-dev <email@hidden>
- Date: Fri, 29 May 2020 17:42:46 +0200
Jesse,
> On 29. 5. 2020, at 3:15 PM, Jesse Tayler <email@hidden> 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
>> <email@hidden> 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 (email@hidden)
>> Help/Unsubscribe/Update your Subscription:
>>
>> This email sent to email@hidden
>
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Webobjects-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden