summaryrefslogtreecommitdiff
path: root/spec/lib/gitlab/exclusive_lease_helpers/sleeping_lock_spec.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/lib/gitlab/exclusive_lease_helpers/sleeping_lock_spec.rb')
-rw-r--r--spec/lib/gitlab/exclusive_lease_helpers/sleeping_lock_spec.rb102
1 files changed, 102 insertions, 0 deletions
diff --git a/spec/lib/gitlab/exclusive_lease_helpers/sleeping_lock_spec.rb b/spec/lib/gitlab/exclusive_lease_helpers/sleeping_lock_spec.rb
new file mode 100644
index 00000000000..8917eeec56f
--- /dev/null
+++ b/spec/lib/gitlab/exclusive_lease_helpers/sleeping_lock_spec.rb
@@ -0,0 +1,102 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::ExclusiveLeaseHelpers::SleepingLock, :clean_gitlab_redis_shared_state do
+ include ::ExclusiveLeaseHelpers
+
+ let(:timeout) { 1.second }
+ let(:delay) { 0.1.seconds }
+ let(:key) { SecureRandom.hex(10) }
+
+ subject { described_class.new(key, timeout: timeout, delay: delay) }
+
+ describe '#retried?' do
+ before do
+ stub_exclusive_lease(key, 'uuid')
+ end
+
+ context 'we have not made any attempts' do
+ it { is_expected.not_to be_retried }
+ end
+
+ context 'we just made a single (initial) attempt' do
+ it 'is not considered a retry' do
+ subject.send(:try_obtain)
+
+ is_expected.not_to be_retried
+ end
+ end
+
+ context 'made multiple attempts' do
+ it 'is considered a retry' do
+ 2.times { subject.send(:try_obtain) }
+
+ is_expected.to be_retried
+ end
+ end
+ end
+
+ describe '#obtain' do
+ context 'when the lease is not held' do
+ before do
+ stub_exclusive_lease(key, 'uuid')
+ end
+
+ it 'obtains the lease on the first attempt, without sleeping' do
+ expect(subject).not_to receive(:sleep)
+
+ subject.obtain(10)
+
+ expect(subject).not_to be_retried
+ end
+ end
+
+ context 'when the lease is held elsewhere' do
+ let!(:lease) { stub_exclusive_lease_taken(key) }
+ let(:max_attempts) { 7 }
+
+ it 'retries to obtain a lease and raises an error' do
+ expect(subject).to receive(:sleep).with(delay).exactly(max_attempts - 1).times
+ expect(lease).to receive(:try_obtain).exactly(max_attempts).times
+
+ expect { subject.obtain(max_attempts) }.to raise_error('Failed to obtain a lock')
+ end
+
+ context 'when the delay is computed from the attempt number' do
+ let(:delay) { ->(n) { 3 * n } }
+
+ it 'uses the computation to determine the sleep length' do
+ expect(subject).to receive(:sleep).with(3).once
+ expect(subject).to receive(:sleep).with(6).once
+ expect(subject).to receive(:sleep).with(9).once
+ expect(lease).to receive(:try_obtain).exactly(4).times
+
+ expect { subject.obtain(4) }.to raise_error('Failed to obtain a lock')
+ end
+ end
+
+ context 'when lease is granted after retry' do
+ it 'knows that it retried' do
+ expect(subject).to receive(:sleep).with(delay).exactly(3).times
+ expect(lease).to receive(:try_obtain).exactly(3).times { nil }
+ expect(lease).to receive(:try_obtain).once { 'obtained' }
+
+ subject.obtain(max_attempts)
+
+ expect(subject).to be_retried
+ end
+ end
+ end
+
+ describe 'cancel' do
+ let!(:lease) { stub_exclusive_lease(key, 'uuid') }
+
+ it 'cancels the lease' do
+ expect(lease).to receive(:cancel)
+
+ subject.cancel
+ end
+ end
+ end
+end