Fix updating account counters when account_stat is not yet created (#15108)
This commit is contained in:
		@ -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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user