summaryrefslogtreecommitdiff
path: root/lib/gitlab/exclusive_lease_helpers.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/gitlab/exclusive_lease_helpers.rb')
-rw-r--r--lib/gitlab/exclusive_lease_helpers.rb39
1 files changed, 24 insertions, 15 deletions
diff --git a/lib/gitlab/exclusive_lease_helpers.rb b/lib/gitlab/exclusive_lease_helpers.rb
index 61eb030563d..10762d83588 100644
--- a/lib/gitlab/exclusive_lease_helpers.rb
+++ b/lib/gitlab/exclusive_lease_helpers.rb
@@ -6,29 +6,38 @@ module Gitlab
FailedToObtainLockError = Class.new(StandardError)
##
- # This helper method blocks a process/thread until the other process cancel the obrainted lease key.
+ # This helper method blocks a process/thread until the lease can be acquired, either due to
+ # the lease TTL expiring, or due to the current holder explicitly releasing
+ # their hold.
#
- # Note: It's basically discouraged to use this method in the unicorn's thread,
- # because it holds the connection until all `retries` is consumed.
+ # If the lease cannot be obtained, raises `FailedToObtainLockError`.
+ #
+ # @param [String] key The lock the thread will try to acquire. Only one thread
+ # in one process across all Rails instances can hold this named lock at any
+ # one time.
+ # @param [Float] ttl: The length of time the lock will be valid for. The lock
+ # will be automatically be released after this time, so any work should be
+ # completed within this time.
+ # @param [Integer] retries: The maximum number of times we will re-attempt
+ # to acquire the lock. The maximum number of attempts will be `retries + 1`:
+ # one for the initial attempt, and then one for every re-try.
+ # @param [Float|Proc] sleep_sec: Either a number of seconds to sleep, or
+ # a proc that computes the sleep time given the number of preceding attempts
+ # (from 1 to retries - 1)
+ #
+ # Note: It's basically discouraged to use this method in a unicorn thread,
+ # because this ties up all thread related resources until all `retries` are consumed.
# This could potentially eat up all connection pools.
def in_lock(key, ttl: 1.minute, retries: 10, sleep_sec: 0.01.seconds)
raise ArgumentError, 'Key needs to be specified' unless key
- lease = Gitlab::ExclusiveLease.new(key, timeout: ttl)
- retried = false
-
- until uuid = lease.try_obtain
- # Keep trying until we obtain the lease. To prevent hammering Redis too
- # much we'll wait for a bit.
- sleep(sleep_sec)
- (retries -= 1) < 0 ? break : retried ||= true
- end
+ lease = SleepingLock.new(key, timeout: ttl, delay: sleep_sec)
- raise FailedToObtainLockError, 'Failed to obtain a lock' unless uuid
+ lease.obtain(1 + retries)
- yield(retried)
+ yield(lease.retried?)
ensure
- Gitlab::ExclusiveLease.cancel(key, uuid)
+ lease&.cancel
end
end
end