diff options
author | Ryuta Kamizono <kamipo@gmail.com> | 2016-12-23 21:56:20 +0900 |
---|---|---|
committer | Ryuta Kamizono <kamipo@gmail.com> | 2016-12-23 21:56:20 +0900 |
commit | 9eee7822ac4bce983ad45a98c4d111eb36285199 (patch) | |
tree | 20c13d1bcc947a2b6cfc2e8ecd25714f76aca7a5 | |
parent | fd63aa02289d64e9d14fe56723f1de64bca3bb1f (diff) | |
download | rails-9eee7822ac4bce983ad45a98c4d111eb36285199.tar.gz rails-9eee7822ac4bce983ad45a98c4d111eb36285199.tar.bz2 rails-9eee7822ac4bce983ad45a98c4d111eb36285199.zip |
Add a record to target before any callbacks loads the record
`append_record` was added at 15ddd51 for not double adding the record.
But adding `append_record` (checking `@target.include?(record)`) caused
performance regression #27434. Instead of checking not double adding the
record, add a record to target before any callbacks loads the record.
Fixes #27434.
3 files changed, 17 insertions, 14 deletions
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 46923f690a..13f77c7d4d 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -299,12 +299,23 @@ module ActiveRecord def replace_on_target(record, index, skip_callbacks) callback(:before_add, record) unless skip_callbacks - yield(record) if block_given? + begin + if index + record_was = target[index] + target[index] = record + else + target << record + end - if index - @target[index] = record - else - append_record(record) + yield(record) if block_given? + rescue + if index + target[index] = record_was + else + target.delete(record) + end + + raise end callback(:after_add, record) unless skip_callbacks @@ -502,10 +513,6 @@ module ActiveRecord load_target.select { |r| ids.include?(r.id.to_s) } end end - - def append_record(record) - @target << record unless @target.include?(record) - end end end end diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index 8c90aea975..0c0aefe3b9 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -206,10 +206,6 @@ module ActiveRecord def invertible_for?(record) false end - - def append_record(record) - @target << record - end end end end diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index d657be71cc..e18b4fd12d 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -2475,7 +2475,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase test "double insertion of new object to association when same association used in the after create callback of a new object" do reset_callbacks(:save, Bulb) do - Bulb.after_save { |record| record.car.bulbs.to_a } + Bulb.after_save { |record| record.car.bulbs.load } car = Car.create! car.bulbs << Bulb.new assert_equal 1, car.bulbs.size |