Re: [SOLVED] Baffling problem with foreign key
Re: [SOLVED] Baffling problem with foreign key
- Subject: Re: [SOLVED] Baffling problem with foreign key
- From: Gary Teter <email@hidden>
- Date: Sun, 7 Dec 2008 12:44:23 -0800
I finally figured out what's going on, and I think it's a fundamental
flaw in WebObjects. Fortunately Ralf's suggestion is a good-enough
workaround:
A workaround may be to call
"EOObserverCenter.notifyObserversObjectWillChange(null)" at
strategic places like in Application.dispatchRequest().
What's supposed to happen: You change an attribute on an eo, which
calls willChange(), which calls
EOObserverCenter.notifyObserversObjectWillChange(this). The observer
center notes that this was the most recently observed object for this
thread, and calls editingContext.objectWillChange. The ec then grabs
the current snapshot of the object, and then the attribute is finally
changed in the eo. Any further calls to willChange >in the current
thread< on the eo are ignored by the EOObserverCenter. When the ec
saves changes, it calls notifyObserversObjectWillChange(null) so any
future changes to the eo get noticed again.
How it fails: A request is handled by WorkerThread0. By the end of
the request the eo has been modified but not saved, so the
EOObserverCenter remembers that WorkerThread0's most recent object is
that eo. Fifteen more requests are handled by WorkerThreads 1-15 in
sequence. One of these requests completes the modification of the eo
and calls saveChanges on the ec. At this point the ec tells the
EOObserverCenter to forget about its most recent object, but it's
being set to null in WorkerThread14 or whatever, not WorkerThread0.
The next request will wrap around to to be handled by WorkerThread0.
This request modifies an attribute on the eo, but since the
EOObserverCenter still thinks WorkerThread0 has already noticed the
eo, it ignores the willChange and the ec doesn't grab a snapshot.
Later in the processing of this request, a different object gets
changed, willChange gets called and the ec grabs a snapshot of the
second object. Then, a change gets made to the original eo,
willChange gets called, and since the EOObserverCenter was paying
attention to the second object, it goes ahead and notifies the ec
about the first object.
At this point the ec grabs a snapshot of the first object, but it's
too late -- the object has already been modified, the ec didn't know
about the previous change, so when saveChanges gets called the
previous changes don't get saved to the database. And now your object
graph no longer matches the database, and your app is borked.
I think I understand this bug well enough now to construct the
simplest possible test case and file it in Radar. I also wouldn't be
surprised to find it in 5.4 (still haven't upgraded so can't test).
On Dec 5, 2008, at 8:09 AM, Gary Teter wrote:
On Dec 5, 2008, at 3:22 AM, Ralf Schuchardt wrote:
Am 05.12.2008 um 01:41 schrieb Gary Teter:
Wow. I got misled by my logging. Turns out it's perfectly fine
for __originalSnapshot to be null before objectWillChange is
called, because the first time it's called it sets the
__originalSnapshot >before< the new value is set. Further changes
to the object don't set the __originalSnapshot.
So it turns out that the problem is that somehow objectWillChange
is not getting called until >after< the change has been made.
Here's what it should look like:
DebuggingEditingContext@2dc21d lock()
going to setServiceRate() - [2223, UPS Mail Innovations Bound
Printed Matter, $5.36 for 64 oz, zone 5 (min BPM 50%)]
beginning willChange() - Order # 664975
objectWillChange: Order # 664975, created 08/25/06 03:03:44 PM 1
items for subtotal $59.95
snapshot() - returning: { ...omitted... } <-- here's where
__originalSnapshot gets set
done willChange() - Order # 664975
beginning willChange() - Order # 664975
done willChange() - Order # 664975
done setServiceRate() - [2223, UPS Mail Innovations Bound Printed
Matter, $5.36 for 64 oz, zone 5 (min BPM 50%)]
And here's what it looks like when it's borked:
DebuggingEditingContext@2dc21d lock()
going to setServiceRate() - [5, UPS Ground for 2400 oz, United
States]
beginning willChange() - Order # 664975
done willChange() - Order # 664975
beginning willChange() - Order # 664975
done willChange() - Order # 664975
done setServiceRate() - [5, UPS Ground for 2400 oz, United States]
Note that the serviceRate has been completely set by this point,
and objectWillChange has not yet been called. (It gets called
later. Now I get to figure out why
EOObserverCenter.notifyObserversObjectWillChange() apparently
isn't doing its job...
It might well be another EOF bug (or a consequence of the bug
Chuck talked about).
EOObserverCenter stores the last changed object per thread, so it
will not repeatedly send notifications for the same object.
This information is normally reset at certain points, but
sometimes it's missed.
As a result you may see sporadic failures to recognize changes in
an EO when the same worker thread is changing the same object.
A workaround may be to call
"EOObserverCenter.notifyObserversObjectWillChange(null)" at
strategic places like in Application.dispatchRequest().
(But I believe the real culprit is the EC loosing its information
about changed objects, otherwise it shouldn't be necessary to tell
the EC about changes in an EO again ...)
Ralf
That's what I suspected, so I added a call to
notifyObserversObjectWillChange(null) to a method that responds to
the EditingContextDidSaveChanges notification, figuring that's the
best place to put it. But the borking is still there. I also added
logging to see if the observerNotificationSuppressCount was non-
zero during OrderObject's willChange (it's zero), and to display
the observersForObject for the OrderObject in case somehow its ec
was not on the list (it is).
As I understand EOObserverCenter the only thing left is that the
observer center's last changed object is being set somehow behind
my back in between my setting it to null and my attempt to set the
serviceRate relationship. But how and why is eluding me right now:
DebuggingEditingContext@6231ed
_clearOriginalSnapshotAndInitializeRec() - OrderLineItem 1302722
_editingContextDidSaveChanges - notifyObserversObjectWillChange(null)
DebuggingEditingContext@6231ed saveChanges()
_editingContextDidSaveChanges - notifyObserversObjectWillChange(null)
DebuggingEditingContext@6231ed unlock()
DebuggingEditingContext@6231ed tryLock()
DebuggingEditingContext@6231ed lock()
DebuggingEditingContext@6231ed processRecentChanges()
DebuggingEditingContext@6231ed unlock()
<end of previous request>
[2008-12-04 18:42:46 PST] <WorkerThread8> <127.0.0.1> "POST /cgi-
bin/WebObjects/Store.woa/1/wo/TdoyaGbiVbMqgXYbawJFpw/
93.2.3.1.0.2.1.35.3.1.1.1.1.2.1.5.3.0.3.1 HTTP/1.1" Mozilla/5.0
(Macintosh; U; Intel Mac OS X 10_4_11; en) AppleWebKit/525.27.1
(KHTML, like Gecko) Version/3.2.1 Safari/525.27.1
DebuggingEditingContext@6231ed lock()
going to setServiceRate() - [5, UPS Ground for 2400 oz, United States]
beginning willChange() - Order # 664975,
observerNotificationSuppressCount: 0
beginning willChange() - Order # 664975, observersForObject:
(DebuggingEditingContext@6231ed)
done willChange() - Order # 664975,
observerNotificationSuppressCount: 0
done willChange() - Order # 664975, observersForObject:
(DebuggingEditingContext@6231ed)
beginning willChange() - Order # 664975,
observerNotificationSuppressCount: 0
beginning willChange() - Order # 664975, observersForObject:
(DebuggingEditingContext@6231ed)
done willChange() - Order # 664975,
observerNotificationSuppressCount: 0
done willChange() - Order # 664975, observersForObject:
(DebuggingEditingContext@6231ed)
done setServiceRate() - [5, UPS Ground for 2400 oz, United States]
Anybody know the best way to install my own replacement
EOObserverCenter so I can see what's going on? Or a way to set
breakpoints in code that's not mine?
--
The Ent visualization project:
http://wirehose.com/research/entvisualization
_______________________________________________
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