When after_save isn't after the save

I have a Production model, which represents a farmer’s crop. After
create (via after_save) I call a method that creates one or more
Supply models, which represent a week of anticipated product harvest.
After each of those are created, a class method gets called that tries
to match each new Supply with outstanding orders for the same time
period. If a match is made, a transaction (an Exchange model acting as
the join record) is created and it adjusts the quantities in both the
Supply and Order models.

Well, it ain’t working. I mean, it almost does, but after_save filter
seems to be calling methods during the transaction, not after the
commit. The quantity updates on the Supply and Order models are
getting blown away. It seems I was mislead by the name of
“after_save”.

before_save doesn’t work because the id for the Production hasn’t been
assigned and the relationships can’t be set then.

First, are my observations about after_save and the transaction
correct? Second, is there another callback filter I should be using
instead, one that happens after the transaction? Or is my design
suspect? Recommendations welcome.

Thanks! :slight_smile:

FYI: I’m using rails 3.0.0.rc + ruby 1.8.7 + sqlite3 for dev, postgre
for prod.

Thanks, Fred. I think you’ve helped me out before. I appreciate your
effort.

I’ve put the relevant code here: http://pastie.org/1100297

So you know what’s happening, the farmer creates a Production, which
creates one or more Supplies, which may create one or more Exchanges
if there are any Demands that match the Supply.

The Exchange is supposed to decrement the “available” variable in both
the Supply and Demand models (the “quantity” variable doesn’t change).

On exchange.rb#18 where the debugger stops, the values of
demand.available and supply.available have been correctly decremented.
After I let the code continue, they are back to their original values.

Thanks again.

On Aug 18, 3:58 pm, IAmNan [email protected] wrote:

seems to be calling methods during the transaction, not after the
suspect? Recommendations welcome.

the entirety of before_save, save, after_save is run in a single
transaction. I don’t understand why this is causing you a problem
though - could you elaborate.

Fred

IAmNan wrote:

I’ve put the relevant code here: http://pastie.org/1100297

So you know what’s happening, the farmer creates a Production, which
creates one or more Supplies, which may create one or more Exchanges
if there are any Demands that match the Supply.

The Exchange is supposed to decrement the “available” variable in both
the Supply and Demand models (the “quantity” variable doesn’t change).

On exchange.rb#18 where the debugger stops, the values of
demand.available and supply.available have been correctly decremented.
After I let the code continue, they are back to their original values.

Maybe what you’re trying to do is a bit too complex for use in
after_save. Maybe it should instead be done using a transaction. I’m not
100% sure, but worth a try:

On Aug 18, 6:24 pm, IAmNan [email protected] wrote:

the Supply and Demand models (the “quantity” variable doesn’t change).

On exchange.rb#18 where the debugger stops, the values of
demand.available and supply.available have been correctly decremented.
After I let the code continue, they are back to their original values.

You don’t seem to be saving the demand or supply objects after you
modify them in that after_save. Are you doing that somewhere else?

Fred

On Aug 18, 6:24 pm, IAmNan [email protected] wrote:

On exchange.rb#18 where the debugger stops, the values of
demand.available and supply.available have been correctly decremented.
After I let the code continue, they are back to their original values.

It doesn’t look your saving the supply & semand objects you’re
modifying. Are you doing that somewhere else?

Fred

Thanks Fred and Robert and anyone else who has read this!

I’ve had some more time to look at this and it appears there is no way
in rails to modify a parent model’s attributes from a child model
during a transaction. I wrote a basic app to test it out.

Assume Parent has_many Children and Child belongs_to Parent. Also,
Parent has a before_save filter (we’ll look at what it does in a
moment). Child also has a before_filter that updates an attribute in
it’s Parent.

Version 1: In one example, the Parent’s before_save filter builds
three Children. Because it is a “build” and not a new or create, the
child objects are saved when the parent is saved. In this case, the
child’s change to its parent attribute just disappears, although the
Child is successfully instantiated. http://pastie.org/1106223

Version 2: Okay, then I tried the same thing, this time using create
instead of build in the Parent’s before_save filter. That generates an
error, “You cannot call create unless the parent is saved.” Which
makes sense.

Version 3: Same as Version 1, but make the Parent’s filter after_save.
In this case, the before_filter of the children is never called (and
same result if the Child’s filter is changed to an after_save, it just
isn’t called).

Version 4: Same as Version 1, but remove the callback in the child and
just call that method directly from the parent immediately after the
build. You get a “undefined method” from the Child’s method because
the parent is nil at this point.

Version 5: Like Version 4, but uses after_save instead of before_save.
The results are just like Version 1, the Child’s method is called,
changes to the Parent aren’t saved, but the children are saved.

In these examples, I used
Parent.create
and
p=Parent.new
p.save
and get the same results each time. The results are also in the pastie
(http://pastie.org/1106223).

So, if there are limitations on what you can do in a callback filter,
we should probably documented it somewhere. However, I might even
call it a bug. What do you think?

d.

On Aug 21, 5:47 pm, IAmNan [email protected] wrote:

So, if there are limitations on what you can do in a callback filter,
we should probably documented it somewhere. However, I might even
call it a bug. What do you think?

Doesn’t sound like anything to do with callbacks to me. I think the
root of the problem is that there are multiple instances of parent in
memory - the one you created explicitly, and the ones loaded from the
database when you call parent from the child object. Although they
refer to the same row in the database, they are distinct in memory
objects (if you inspect the value of parent it should change). There’s
a plugin (parental_control) that goes someway to fixing this by adding
an :inverse_of option to associations which I believe was merged into
trunk (not in rails 2.3.x if my memory is correct)

Fred

Thanks Fred and Danny. Fred thanks for the explanation. As mentioned,
I am using Rails 3 and both I have them both native. I go from few
options to several. Maybe I can clean up my code a little now.
Sweet! :slight_smile:

I totally wouldn’t have expected your explanation, Fred, but I see now
it’s right. I am struggling to think of a case where I would want two
instances of the same object in memory since it would lead to a race
condition most of the time. Hence, I am also wondering why :inverse_of
isn’t the default behavior. Maybe it’s performance. Rhetorical, you
don’t need to answer those as I’ll check them out in my own time.

Thanks again.

If you really want callbacks after commit, you might try
http://github.com/freelancing-god/after_commit

  • D

On Aug 23, 11:42 am, IAmNan [email protected] wrote:

don’t need to answer those as I’ll check them out in my own time.

Inverse_of isn’t automatic because in the non trivial cases it may not
be obvious which associations are the inverse of which. Having
multiple copies of an object in memory is generally undesirable (eg
data mapper doesn;t do this), but is not particularly easy to change
(especially if backwards compatibility is a concern).

Fred