Skip to content

Instantly share code, notes, and snippets.

@tenderlove
Created April 14, 2014 03:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tenderlove/10613756 to your computer and use it in GitHub Desktop.
Save tenderlove/10613756 to your computer and use it in GitHub Desktop.
diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb
index 8272a55..e8083a5 100644
--- a/activerecord/lib/active_record/associations/belongs_to_association.rb
+++ b/activerecord/lib/active_record/associations/belongs_to_association.rb
@@ -46,8 +46,9 @@ module ActiveRecord
def update_counters(record)
with_cache_name do |name|
return unless different_target? record
- record.class.increment_counter(name, record.id)
- decrement_counter name
+ expected = record.read_attribute name
+ record.class.increment_counter(name, record.id, expected)
+ decrement_counter record, name
end
end
@@ -55,9 +56,10 @@ module ActiveRecord
with_cache_name { |name| decrement_counter name }
end
- def decrement_counter counter_cache_name
+ def decrement_counter record, counter_cache_name
if foreign_key_present?
- klass.decrement_counter(counter_cache_name, target_id)
+ expected = record.read_attribute counter_cache_name
+ klass.decrement_counter(counter_cache_name, target_id, expected)
end
end
diff --git a/activerecord/lib/active_record/associations/builder/belongs_to.rb b/activerecord/lib/active_record/associations/builder/belongs_to.rb
index 5ccaa55..68b7570 100644
--- a/activerecord/lib/active_record/associations/builder/belongs_to.rb
+++ b/activerecord/lib/active_record/associations/builder/belongs_to.rb
@@ -32,7 +32,8 @@ module ActiveRecord::Associations::Builder
def belongs_to_counter_cache_after_create(reflection)
if record = send(reflection.name)
cache_column = reflection.counter_cache_column
- record.class.increment_counter(cache_column, record.id)
+ expected = record.read_attribute cache_column
+ record.class.increment_counter(cache_column, record.id, expected)
@_after_create_counter_called = true
end
end
@@ -43,7 +44,8 @@ module ActiveRecord::Associations::Builder
record = send reflection.name
if record && !self.destroyed?
cache_column = reflection.counter_cache_column
- record.class.decrement_counter(cache_column, record.id)
+ expected = record.read_attribute cache_column
+ record.class.decrement_counter(cache_column, record.id, expected)
end
end
end
diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb
index dcbdf75..5277a00 100644
--- a/activerecord/lib/active_record/counter_cache.rb
+++ b/activerecord/lib/active_record/counter_cache.rb
@@ -70,14 +70,16 @@ module ActiveRecord
# # UPDATE posts
# # SET comment_count = COALESCE(comment_count, 0) + 1
# # WHERE id IN (10, 15)
- def update_counters(id, counters)
+ def update_counters(id, expected, counters)
+ scope = unscoped
updates = counters.map do |counter_name, value|
operator = value < 0 ? '-' : '+'
quoted_column = connection.quote_column_name(counter_name)
+ scope = scope.where(counter_name => expected)
"#{quoted_column} = COALESCE(#{quoted_column}, 0) #{operator} #{value.abs}"
end
- unscoped.where(primary_key => id).update_all updates.join(', ')
+ scope.where(primary_key => id).update_all updates.join(', ')
end
# Increment a numeric field by one, via a direct SQL update.
@@ -96,8 +98,8 @@ module ActiveRecord
#
# # Increment the post_count column for the record with an id of 5
# DiscussionBoard.increment_counter(:post_count, 5)
- def increment_counter(counter_name, id)
- update_counters(id, counter_name => 1)
+ def increment_counter(counter_name, id, expected)
+ update_counters(id, expected, counter_name => 1)
end
# Decrement a numeric field by one, via a direct SQL update.
@@ -114,8 +116,8 @@ module ActiveRecord
#
# # Decrement the post_count column for the record with an id of 5
# DiscussionBoard.decrement_counter(:post_count, 5)
- def decrement_counter(counter_name, id)
- update_counters(id, counter_name => -1)
+ def decrement_counter(counter_name, id, expected)
+ update_counters(id, expected, counter_name => -1)
end
end
end
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index 4d63b04..a2d211f 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -164,7 +164,7 @@ module ActiveRecord
# Make sure the lock version column gets updated when counters are
# updated.
- def update_counters(id, counters)
+ def update_counters(id, counters, expected)
counters = counters.merge(locking_column => 1) if locking_enabled?
super
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment