1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
|
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe ExclusiveLeaseGuard, :clean_gitlab_redis_shared_state, feature_category: :shared do
subject :subject_class do
Class.new do
include ExclusiveLeaseGuard
def self.name
'ExclusiveLeaseGuardTestClass'
end
def call(&block)
try_obtain_lease do
internal_method(&block)
end
end
def internal_method
yield
end
def lease_timeout
1.second
end
end
end
describe '#try_obtain_lease' do
let(:subject) { subject_class.new }
it 'obtains the lease, calls internal_method and releases the lease', :aggregate_failures do
expect(subject).to receive(:internal_method).and_call_original
subject.call do
expect(subject.exclusive_lease.exists?).to be_truthy
end
expect(subject.exclusive_lease.exists?).to be_falsey
end
context 'when the lease is already obtained' do
before do
subject.exclusive_lease.try_obtain
end
after do
subject.exclusive_lease.cancel
end
context 'when the class does not override lease_taken_log_level' do
it 'does not call internal_method but logs error', :aggregate_failures do
expect(subject).not_to receive(:internal_method)
expect(Gitlab::AppJsonLogger).to receive(:error).with({ message: "Cannot obtain an exclusive lease. There must be another instance already in execution.", lease_key: 'exclusive_lease_guard_test_class', class_name: 'ExclusiveLeaseGuardTestClass', lease_timeout: 1.second })
subject.call
end
end
context 'when the class overrides lease_taken_log_level to return :info' do
subject :overwritten_subject_class do
Class.new(subject_class) do
def lease_taken_log_level
:info
end
end
end
let(:subject) { overwritten_subject_class.new }
it 'logs info', :aggregate_failures do
expect(Gitlab::AppJsonLogger).to receive(:info).with({ message: "Cannot obtain an exclusive lease. There must be another instance already in execution.", lease_key: 'exclusive_lease_guard_test_class', class_name: 'ExclusiveLeaseGuardTestClass', lease_timeout: 1.second })
subject.call
end
end
context 'when the class overrides lease_taken_log_level to return :debug' do
subject :overwritten_subject_class do
Class.new(subject_class) do
def lease_taken_log_level
:debug
end
end
end
let(:subject) { overwritten_subject_class.new }
it 'logs debug', :aggregate_failures do
expect(Gitlab::AppJsonLogger).to receive(:debug).with({ message: "Cannot obtain an exclusive lease. There must be another instance already in execution.", lease_key: 'exclusive_lease_guard_test_class', class_name: 'ExclusiveLeaseGuardTestClass', lease_timeout: 1.second })
subject.call
end
end
end
context 'with overwritten lease_release?' do
subject :overwritten_subject_class do
Class.new(subject_class) do
def lease_release?
false
end
end
end
let(:subject) { overwritten_subject_class.new }
it 'does not release the lease after execution', :aggregate_failures do
subject.call do
expect(subject.exclusive_lease.exists?).to be_truthy
end
expect(subject.exclusive_lease.exists?).to be_truthy
end
end
end
describe '#exclusive_lease' do
it 'uses the class name as lease key' do
expect(Gitlab::ExclusiveLease).to receive(:new).with('exclusive_lease_guard_test_class', timeout: 1.second)
subject_class.new.exclusive_lease
end
context 'with overwritten lease_key' do
subject :overwritten_class do
Class.new(subject_class) do
def lease_key
'other_lease_key'
end
end
end
it 'uses the custom lease key' do
expect(Gitlab::ExclusiveLease).to receive(:new).with('other_lease_key', timeout: 1.second)
overwritten_class.new.exclusive_lease
end
end
end
describe '#release_lease' do
it 'sends a cancel message to ExclusiveLease' do
expect(Gitlab::ExclusiveLease).to receive(:cancel).with('exclusive_lease_guard_test_class', 'some_uuid')
subject_class.new.release_lease('some_uuid')
end
end
describe '#renew_lease!' do
let(:subject) { subject_class.new }
it 'sends a renew message to the exclusive_lease instance' do
expect(subject.exclusive_lease).to receive(:renew)
subject.renew_lease!
end
end
end
|