summaryrefslogtreecommitdiff
path: root/lib/gitlab/exclusive_lease_helpers/sleeping_lock.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/gitlab/exclusive_lease_helpers/sleeping_lock.rb')
-rw-r--r--lib/gitlab/exclusive_lease_helpers/sleeping_lock.rb50
1 files changed, 50 insertions, 0 deletions
diff --git a/lib/gitlab/exclusive_lease_helpers/sleeping_lock.rb b/lib/gitlab/exclusive_lease_helpers/sleeping_lock.rb
new file mode 100644
index 00000000000..8213c9bc042
--- /dev/null
+++ b/lib/gitlab/exclusive_lease_helpers/sleeping_lock.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module ExclusiveLeaseHelpers
+ # Wrapper around ExclusiveLease that adds retry logic
+ class SleepingLock
+ delegate :cancel, to: :@lease
+
+ def initialize(key, timeout:, delay:)
+ @lease = ::Gitlab::ExclusiveLease.new(key, timeout: timeout)
+ @delay = delay
+ @attempts = 0
+ end
+
+ def obtain(max_attempts)
+ until held?
+ raise FailedToObtainLockError, 'Failed to obtain a lock' if attempts >= max_attempts
+
+ sleep(sleep_sec) unless first_attempt?
+ try_obtain
+ end
+ end
+
+ def retried?
+ attempts > 1
+ end
+
+ private
+
+ attr_reader :delay, :attempts
+
+ def held?
+ @uuid.present?
+ end
+
+ def try_obtain
+ @uuid ||= @lease.try_obtain
+ @attempts += 1
+ end
+
+ def first_attempt?
+ attempts.zero?
+ end
+
+ def sleep_sec
+ delay.respond_to?(:call) ? delay.call(attempts) : delay
+ end
+ end
+ end
+end