diff --git a/app/jobs/concerns/has_active_job_lock.rb b/app/jobs/concerns/has_active_job_lock.rb index 9931a4c11..563e7a1ef 100644 --- a/app/jobs/concerns/has_active_job_lock.rb +++ b/app/jobs/concerns/has_active_job_lock.rb @@ -95,6 +95,10 @@ module HasActiveJobLock # but it's safe to retry as described in the docs: # https://www.postgresql.org/docs/10/transaction-iso.html e.message.include?('PG::TRSerializationFailure') ? retry : raise + rescue ActiveRecord::Deadlocked => e + # MySQL handles lock race condition differently and raises a Deadlock exception: + # Mysql2::Error: Deadlock found when trying to get lock; try restarting transaction + e.message.include?('Mysql2::Error: Deadlock found when trying to get lock') ? retry : raise rescue ActiveRecord::RecordNotUnique existing_active_job_lock! end diff --git a/spec/jobs/concerns/has_active_job_lock_spec.rb b/spec/jobs/concerns/has_active_job_lock_spec.rb index 4e8ae77e5..362d0d23b 100644 --- a/spec/jobs/concerns/has_active_job_lock_spec.rb +++ b/spec/jobs/concerns/has_active_job_lock_spec.rb @@ -121,6 +121,27 @@ RSpec.describe HasActiveJobLock, type: :job do expect(exception_raised).to be true end end + + context "when ActiveRecord::Deadlocked 'Mysql2::Error: Deadlock found when trying to get lock; try restarting transaction' is raised" do + + it 'retries execution until succeed' do + allow(ActiveRecord::Base.connection).to receive(:open_transactions).and_return(0) + allow(ActiveJobLock).to receive(:transaction).and_call_original + exception_raised = false + allow(ActiveJobLock).to receive(:transaction).with(isolation: :serializable) do |&block| + + if !exception_raised + exception_raised = true + raise ActiveRecord::Deadlocked, 'Mysql2::Error: Deadlock found when trying to get lock; try restarting transaction' + end + + block.call + end + + expect { job_class.perform_later }.to have_enqueued_job(job_class).exactly(:once) + expect(exception_raised).to be true + end + end end include_examples 'handle locking of jobs'