From 47778f2d2fb27745d52c1221739ecc5d2f8af667 Mon Sep 17 00:00:00 2001 From: Thorsten Eckel Date: Fri, 29 Nov 2019 16:48:21 +0100 Subject: [PATCH] Follow up - 2c8825b387445fc9aaaa945433aaf94851540375 - ActiveJobLock lock update fails due to ActiveRecord::Deadlocked "Mysql2::Error: Deadlock found when trying to get lock; try restarting transaction" exception. --- app/jobs/concerns/has_active_job_lock.rb | 4 ++++ .../jobs/concerns/has_active_job_lock_spec.rb | 21 +++++++++++++++++++ 2 files changed, 25 insertions(+) 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'