]> cat aescling's git repositories - mastodon.git/commitdiff
Fix updating account counters when account_stat is not yet created (#15108)
authorEugen Rochko <eugen@zeonfederated.com>
Mon, 9 Nov 2020 15:00:23 +0000 (16:00 +0100)
committerGitHub <noreply@github.com>
Mon, 9 Nov 2020 15:00:23 +0000 (16:00 +0100)
app/models/account_stat.rb
spec/models/account_spec.rb

index c84e4217c8c6b11d5a0770ff443a6de39db1afd6..e70b54d79b93df00916053708e1ba0aabd9c790c 100644 (file)
@@ -21,26 +21,26 @@ class AccountStat < ApplicationRecord
 
   def increment_count!(key)
     update(attributes_for_increment(key))
-  rescue ActiveRecord::StaleObjectError
+  rescue ActiveRecord::StaleObjectError, ActiveRecord::RecordNotUnique
     begin
       reload_with_id
     rescue ActiveRecord::RecordNotFound
-      # Nothing to do
-    else
-      retry
+      return
     end
+
+    retry
   end
 
   def decrement_count!(key)
-    update(key => [public_send(key) - 1, 0].max)
-  rescue ActiveRecord::StaleObjectError
+    update(attributes_for_decrement(key))
+  rescue ActiveRecord::StaleObjectError, ActiveRecord::RecordNotUnique
     begin
       reload_with_id
     rescue ActiveRecord::RecordNotFound
-      # Nothing to do
-    else
-      retry
+      return
     end
+
+    retry
   end
 
   private
@@ -51,8 +51,13 @@ class AccountStat < ApplicationRecord
     attrs
   end
 
+  def attributes_for_decrement(key)
+    attrs = { key => [public_send(key) - 1, 0].max }
+    attrs
+  end
+
   def reload_with_id
-    self.id = find_by!(account: account).id if new_record?
+    self.id = self.class.find_by!(account: account).id if new_record?
     reload
   end
 end
index 98d29e6f3d37defe194fe2d3d8922ca50e9214ad..75f628076d00f32f89bd7b6f0b1370a4216914b2 100644 (file)
@@ -817,4 +817,27 @@ RSpec.describe Account, type: :model do
 
   include_examples 'AccountAvatar', :account
   include_examples 'AccountHeader', :account
+
+  describe '#increment_count!' do
+    subject { Fabricate(:account) }
+
+    it 'increments the count in multi-threaded an environment when account_stat is not yet initialized' do
+      subject
+
+      increment_by   = 15
+      wait_for_start = true
+
+      threads = Array.new(increment_by) do
+        Thread.new do
+          true while wait_for_start
+          Account.find(subject.id).increment_count!(:followers_count)
+        end
+      end
+
+      wait_for_start = false
+      threads.each(&:join)
+
+      expect(subject.reload.followers_count).to eq 15
+    end
+  end
 end