Re: Baffling problem with foreign key
Re: Baffling problem with foreign key
- Subject: Re: Baffling problem with foreign key
- From: Ralf Schuchardt <email@hidden>
- Date: Fri, 5 Dec 2008 12:22:19 +0100
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
On Dec 4, 2008, at 3:55 PM, Chuck Hill wrote:
On Dec 4, 2008, at 3:33 PM, Gary Teter wrote:
On Dec 4, 2008, at 2:45 PM, Chuck Hill wrote:
On Dec 4, 2008, at 2:21 PM, Gary Teter wrote:
Ahh, the joys of using a debugger. Here's the sequence of events
that I think is fouling up EOF:
Set serviceRate on OrderObject.
OrderObject calls willChange().
EOEditingContext does its objectWillChange(). During this
method, it sees that OrderObject's __lastSnapshot is null, so it
sets that to the OrderObject's current snapshot. (This contains
the new serviceRate.) Then it checks to see if the
__originalSnapshot is null, and it is, so it sets the
__originalSnapshot to the __lastSnapshot.
OK, that is the problem but I suspect this is after the cause.
The cause is whatever is discarding the __originalSnapshot. I
can't see why that would be null except for a new object.
I think that actually gets nulled out every time you save a change
to an object.
Does it have to succeed for that to get nulled out? Is there some
odd exception that is causing the save to fail, but this to still
get nulled?
It gets refilled during EOEditingContext's objectWillChange and
committedSnapshotForObject (and possibly elsewhere I guess).
Maybe the EC state is getting borked and it starts to think this
is a new object.
Is revert() or undo() getting called on this ec? You mentioned
before removing an order item. Is that doing an ec.deleteObject()?
OrderObject has ownsDestination on its orderLineItems
relationship, so deleting a line item is just
removeObjectFromBothSidesOfRelationshipWithKey. I've reduced the
number of steps to cause the problem and now believe that I may be
triggering a bug in EOF's implementation of deleting orphaned
objects in such relationships.
What happens if you delete it manually using ec.deleteObject()?
I have a feeling there may be a defect in some combination of
those. Are there any delete rules that might be getting fired?
Is saveChanges() ever called (even if it fails with a validation
error)?
This code saves changes All. The. Frigging. Time. I inherited it
from someone whose approach then to solving problems was to add
another call to saveChanges because, I guess, hey, it couldn't
hurt, right?....
I think they may have also worked on a project that I inherited.
It also exhibited this exact same bug...
Are there any delete rules? Do any faults get fired during the
various calls to save changes? Do any faults get fired while
evaluating (traversing relationships) the delete rules?
ownsDestination relationships might count. There is a bug there
where the EO can lose its state.
I believe this is the point at which EOF is borked -- comparing
the original snapshot and the last snapshot will yield no change
to the serviceRate relationship, so it's never updated in the
database.
Of course, this raises the question: Why is __originalSnapshot
null at this point? During a non-borked save, __lastSnapshot is
null but __originalSnapshot has already been filled with the
correct values.
I wish these methods weren't private so I could set breakpoints
on them....
private methods are an annoying developer conceit. At one point,
I resorted to decompiling and re-implementing EOEditingContext so
that I could debug a very similar problem.
I also hate methods marked "final".
Those too.
On Dec 3, 2008, at 3:39 PM, Gary Teter wrote:
On Dec 3, 2008, at 12:08 PM, Chuck Hill wrote:
Hi Gary,
Nice to see you around!
I've been lurking. :-)
On Dec 3, 2008, at 10:11 AM, Gary Teter wrote:
This one has had me scratching my head for two days. I have a
situation where I can set a to-one relationship on an eo but
the change to the foreign key is not reflected in the SQL
emitted during saveChanges.
This code works properly in most circumstances, but a
customer found that if you follow a very specific series of
otherwise completely ordinary actions that work perfectly
well in any other context, then at some point EOF gets
confused. The object graph no longer matches what's in the
database.
I think the particular sequence of user actions is a red
herring, but they're along the lines of, "Add two products to
your cart. Proceed to checkout. Change shipping address.
Remove item from cart. Proceed to checkout. Change shipping
method." (This is the relationship that becomes borked.) We
process tons of orders every week with this code, so it's not
like the shipping method relationship is simply broken.
It is possible that an EO is getting removed from an EC and
then altered?
No objects are being deleted during these operations as far as
I can tell. Only two new objects (order line items) are being
inserted. The relationship in question is actually two
relationships, between three objects that are already in the
database. OrderObject <<---> DeliveryServiceClass, and
OrderObject <<---> ServiceRate. (Setting the ServiceRate for an
order also sets the DeliveryServiceClass.)
This may produce the same bugs that modifying an EO before it
has been inserted cause. I take it you can reproduce this
yourself and that no back tracking or multiple browser windows
are involved?
Yes. It's like doing a combo move in a video game. If I vary
any of the steps it does not fail. If I perform the sequence
precisely right it fails. Grr.
It is possible that an EO is getting removed from an EC and re-
inserted?
It is possible that these steps are resulting in an EO in an
unlocked EC getting altered?
Sadly, this is all in the session's default editing context.
I'm pretty sure it's not a locking/unlocking issue because I'd
have noticed that long before now.
I'm pretty sure I've eliminated threading and remote
synchronization as possible culprits (can still duplicate
with those turned off). This is WebObjects 5.3.3.
The following are true when it's failing:
I can repeatedly change the destination object, save changes,
and not see the proper update statement.
If the object has other changes that need saving, an update
statement gets properly generated for those attributes, but
not the changed foreign key.
Logging statements in the setBorkedRelationship(OtherObject
value) method show that the relationship is being set properly.
Overriding snapshot() and changesFromSnapshot() show that
during saveChanges the current snapshot includes the new
value, and so does the snapshot being compared. This is
definitely not normal, usually the snapshot being compared
will include the old value, so that changesFromSnapshot()
will indicate the foreign key needs to be updated.
That really does sound like what I was seeing when modifying
an EO that was not yet in an EC.
I'm reasonably sure that our code doesn't commit that
particular sin anywhere.
Is the only updated data in the snapshot being compared the FK?
That's what it >should< be, but what I'm seeing is that the
snapshot being compared is identical to the object's current
snapshot, so EOF thinks there are no changes that need to be
saved. Even though if I step through with a debugger the object
is clearly on the list of objects which need to be updated.
Now I must be seeing things. If other attributes on the object
are also being changed, they are not showing up as different in
the snapshot being compared. Yet the update statement issued
properly includes those attributes (but not the two FKs).
The other updated values only appear in snapshot()? Is this
key getting propagated from some other object? A mutable
value can also do this, but I can't see you using an mutable FK.
Gah. No mutable FK's. The relationships are modeled like so:
OrderObject:
{
allowsNull = Y;
columnName = serviceRateID;
name = serviceRateID;
prototypeName = id;
},
{
allowsNull = Y;
columnName = deliveryServiceClassID;
name = deliveryServiceClassID;
prototypeName = id;
userInfo = {"_EntityModeler" = {generateSource =
NO; }; };
}
...
{
destination = ServiceRate;
isToMany = N;
joinSemantic = EOInnerJoin;
joins = ({destinationAttribute = id; sourceAttribute =
serviceRateID; });
name = serviceRate;
},
{
destination = DeliveryServiceClass;
isToMany = N;
joinSemantic = EOInnerJoin;
joins = ({destinationAttribute = id; sourceAttribute =
deliveryServiceClassID; });
name = deliveryServiceClass;
},
The back-pointing relationships from ServiceRate and
DeliveryServiceClass are modeled but unmarked as class
properties.
Hmmmmm.... Now that's odd. What's that generateSource = NO
doing in there? Ah well, taking it out and doing a full clean/
build didn't help.
If the foreign key is marked as "use for locking", the update
SQL will properly include the >old< foreign key in the where
clause, but the foreign key column is not in the list of
columns being updated. This is interesting to me because one
of my theories was that somehow the EODatabase's snapshot was
getting updated behind my back somehow to reflect the new
value. But this indicates to me that the committed snapshot
does in fact have the correct (old) value.
Wow. That is odd. That makes it seem as if the problem is
local to that EC.
If I modify a different to-one relationship and save changes,
that change gets reflected properly, and I can then
successfully change the previously borked relationship.
Maybe saving that change resets the internal EC state?
That's my guess, but I don't want to add some hacky thing like
"change this extra relationship for no good reason other than
once in awhile EOF gets confuzzled." The fact that my object
graph can get out of sync with the database seriously scares
the hell out of me. What if it's not just this one specific set
of circumstances? Bad enough that it leads to an order being
sent via the wrong delivery service and ticks off a customer.
What if it's happening when I'm processing credit card
transactions or something? Argh.
I must be breaking some EOF commandment but damned if I can
figure out which one.... Suggestions, hints, theories greatly
appreciated.
That is the best I can do at this point.
--
The Ent visualization project:
http://wirehose.com/research/entvisualization
--
Chuck Hill Senior Consultant / VP Development
Practical WebObjects - for developers who want to increase their
overall knowledge of WebObjects or who are trying to solve specific
problems.
http://www.global-village.net/products/practical_webobjects
--
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
_______________________________________________
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