Skip to content

Prevent double firing the before save callback of new object when the parent association saved in the callback #28640

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

kamipo
Copy link
Member

@kamipo kamipo commented Apr 2, 2017

Related #18155, #26661, 268a5bb, #27434, #27442, and #28599.

Originally #18155 was introduced for preventing double insertion caused
by the after save callback. But it was caused the before save issue
(#26661). 268a5bb fixed #26661, but it was caused the performance
regression (#27434). #27442 added new record to target before calling
callbacks for fixing #27434. But it was caused double firing before save
callback (#28599). We cannot add new object to target before saving
the object.

This is improving #18155 to only track callbacks after save.

Fixes #28599.

@superacidjax
Copy link

Thanks for this fix. This behavior was driving me crazy with my test suite.

@matthewd
Copy link
Member

matthewd commented Apr 5, 2017

I really like how neat this feels! But adding the block parameter to the public save / save! methods doesn't seem great 😕

@kamipo
Copy link
Member Author

kamipo commented Apr 6, 2017

Actually we don't need to change the save / save! methods in Suppressor, Transactions, AttributeMethods::Dirty, and Validations because calling super can propagate a block unless adding the block parameter.

https://github.com/rails/rails/blob/v5.1.0.rc1/activerecord/lib/active_record/suppressor.rb#L41-L48
https://github.com/rails/rails/blob/v5.1.0.rc1/activerecord/lib/active_record/transactions.rb#L306-L314
https://github.com/rails/rails/blob/v5.1.0.rc1/activerecord/lib/active_record/attribute_methods/dirty.rb#L34-L46
https://github.com/rails/rails/blob/v5.1.0.rc1/activerecord/lib/active_record/validations.rb#L43-L51

But the deepest save / save! methods in Persistence need to pass args to create_or_update. It can not be realized except by adding the block parameter.

https://github.com/rails/rails/blob/v5.1.0.rc1/activerecord/lib/active_record/persistence.rb#L124-L155

@@ -29,14 +29,9 @@ def handle_dependency
end
end

def insert_record(record, validate = true, raise = false)
def insert_record(record, validate = true, raise = false, &block)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to capture the block? I don't seems used.

@@ -35,15 +35,11 @@ def concat_records(records)
records
end

def insert_record(record, validate = true, raise = false)
def insert_record(record, validate = true, raise = false, &block)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same about capturing the block

@@ -121,8 +121,8 @@ def persisted?
#
# Attributes marked as readonly are silently ignored if the record is
# being updated.
def save(*args)
create_or_update(*args)
def save(*args, &block)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the public method and like @matthewd pointed it is not good to add a block to this method. Since we need it can we at least remove &block from the method signature on rdoc using the :args: directive?

@rafaelfranca rafaelfranca added this to the 5.1.0 milestone Apr 20, 2017
@kamipo kamipo force-pushed the prevent_double_firing_before_save_callback branch from b3ed928 to 271ed22 Compare April 20, 2017 04:50
@@ -100,6 +100,10 @@ def persisted?
!(@new_record || @destroyed)
end

##
# :call-seq:
# save(*args)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried using :args: to remove &block but didn't work at least in my local.
So I used :call-seq: instead, then it worked properly.

image

Copy link
Member

@rafaelfranca rafaelfranca left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome. Could you squash the commits?

… parent association saved in the callback

Related rails#18155, rails#26661, 268a5bb, rails#27434, rails#27442, and rails#28599.

Originally rails#18155 was introduced for preventing double insertion caused
by the after save callback. But it was caused the before save issue
(rails#26661). 268a5bb fixed rails#26661, but it was caused the performance
regression (rails#27434). rails#27442 added new record to `target` before calling
callbacks for fixing rails#27434. But it was caused double firing before save
callback (rails#28599). We cannot add new object to `target` before saving
the object.

This is improving rails#18155 to only track callbacks after `save`.

Fixes rails#28599.
@kamipo kamipo force-pushed the prevent_double_firing_before_save_callback branch from c4f5f1a to c0038f7 Compare April 20, 2017 22:22
@kamipo
Copy link
Member Author

kamipo commented Apr 20, 2017

Squashed!

@rafaelfranca rafaelfranca merged commit 7fcd25f into rails:master Apr 20, 2017
rafaelfranca added a commit that referenced this pull request Apr 20, 2017
…ve_callback

Prevent double firing the before save callback of new object when the parent association saved in the callback
@rafaelfranca
Copy link
Member

Backported in 9d112c2

@kamipo kamipo deleted the prevent_double_firing_before_save_callback branch April 20, 2017 23:39
@kamipo
Copy link
Member Author

kamipo commented Apr 20, 2017

@rafaelfranca
Copy link
Member

rafaelfranca commented Apr 21, 2017

@kamipo could you create a backport PR? It doesn't apply clearly.

@kamipo
Copy link
Member Author

kamipo commented Apr 21, 2017

Sure, will do.

kamipo pushed a commit to kamipo/rails that referenced this pull request Apr 21, 2017
…re_save_callback

Prevent double firing the before save callback of new object when the parent association saved in the callback
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
5 participants