gitlab-sidekiq-fetcher 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitlab-ci.yml +0 -2
- data/Gemfile +1 -1
- data/Gemfile.lock +9 -11
- data/README.md +2 -0
- data/gitlab-sidekiq-fetcher.gemspec +2 -2
- data/lib/sidekiq/base_reliable_fetch.rb +74 -72
- data/lib/sidekiq/reliable_fetch.rb +4 -6
- data/spec/base_reliable_fetch_spec.rb +6 -5
- data/spec/fetch_shared_examples.rb +3 -3
- data/tests/reliability/reliability_test.rb +1 -1
- data/tests/reliability/worker.rb +1 -13
- data/tests/support/utils.rb +1 -1
- metadata +7 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6b9cf610a1915d63141331ba0e4820306235fc1c58e37e0124a9700d19005b99
|
4
|
+
data.tar.gz: 3690c4aaff9d47c8ec108ff264984343bc3a4412123ad56ac727cdf1ae3e8fd3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6d5d61280c6db3b91c8107fca593fa12246db18c727389ef31cf8edf58e923bc78a2cbbe2be505962664ed56c8189513dc0ad0312e545988b70666839f191f66
|
7
|
+
data.tar.gz: cd1459179a3f97b3b3194a21e8335367e9021db590cb9441613d6d88030b1b86cc495b00e1ff37dc0cf7ea683d40e6b10d3c511c7d956a70e8c26c67a4bc0903
|
data/.gitlab-ci.yml
CHANGED
@@ -40,7 +40,6 @@ integration_reliable:
|
|
40
40
|
variables:
|
41
41
|
JOB_FETCHER: reliable
|
42
42
|
|
43
|
-
|
44
43
|
integration_basic:
|
45
44
|
extends: .integration
|
46
45
|
allow_failure: yes
|
@@ -63,7 +62,6 @@ term_interruption:
|
|
63
62
|
services:
|
64
63
|
- redis:alpine
|
65
64
|
|
66
|
-
|
67
65
|
# rubocop:
|
68
66
|
# script:
|
69
67
|
# - bundle exec rubocop
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -2,7 +2,7 @@ GEM
|
|
2
2
|
remote: https://rubygems.org/
|
3
3
|
specs:
|
4
4
|
coderay (1.1.2)
|
5
|
-
connection_pool (2.2.
|
5
|
+
connection_pool (2.2.3)
|
6
6
|
diff-lcs (1.3)
|
7
7
|
docile (1.3.1)
|
8
8
|
json (2.1.0)
|
@@ -10,10 +10,8 @@ GEM
|
|
10
10
|
pry (0.11.3)
|
11
11
|
coderay (~> 1.1.0)
|
12
12
|
method_source (~> 0.9.0)
|
13
|
-
rack (2.
|
14
|
-
|
15
|
-
rack
|
16
|
-
redis (4.0.2)
|
13
|
+
rack (2.2.3)
|
14
|
+
redis (4.2.1)
|
17
15
|
rspec (3.8.0)
|
18
16
|
rspec-core (~> 3.8.0)
|
19
17
|
rspec-expectations (~> 3.8.0)
|
@@ -27,10 +25,10 @@ GEM
|
|
27
25
|
diff-lcs (>= 1.2.0, < 2.0)
|
28
26
|
rspec-support (~> 3.8.0)
|
29
27
|
rspec-support (3.8.0)
|
30
|
-
sidekiq (
|
31
|
-
connection_pool (
|
32
|
-
rack
|
33
|
-
redis (>=
|
28
|
+
sidekiq (6.1.0)
|
29
|
+
connection_pool (>= 2.2.2)
|
30
|
+
rack (~> 2.0)
|
31
|
+
redis (>= 4.2.0)
|
34
32
|
simplecov (0.16.1)
|
35
33
|
docile (~> 1.1)
|
36
34
|
json (>= 1.8, < 3)
|
@@ -43,8 +41,8 @@ PLATFORMS
|
|
43
41
|
DEPENDENCIES
|
44
42
|
pry
|
45
43
|
rspec (~> 3)
|
46
|
-
sidekiq (~>
|
44
|
+
sidekiq (~> 6.1)
|
47
45
|
simplecov
|
48
46
|
|
49
47
|
BUNDLED WITH
|
50
|
-
1.17.
|
48
|
+
1.17.2
|
data/README.md
CHANGED
@@ -6,6 +6,8 @@ fetches from Redis.
|
|
6
6
|
|
7
7
|
It's based on https://github.com/TEA-ebook/sidekiq-reliable-fetch.
|
8
8
|
|
9
|
+
**IMPORTANT NOTE:** Since version `0.7.0` this gem works only with `sidekiq >= 6.1` (which introduced Fetch API breaking changes). Please use version `~> 0.5` if you use older version of the `sidekiq` .
|
10
|
+
|
9
11
|
There are two strategies implemented: [Reliable fetch](http://redis.io/commands/rpoplpush#pattern-reliable-queue) using `rpoplpush` command and
|
10
12
|
semi-reliable fetch that uses regular `brpop` and `lpush` to pick the job and put it to working queue. The main benefit of "Reliable" strategy is that `rpoplpush` is atomic, eliminating a race condition in which jobs can be lost.
|
11
13
|
However, it comes at a cost because `rpoplpush` can't watch multiple lists at the same time so we need to iterate over the entire queue list which significantly increases pressure on Redis when there are more than a few queues. The "semi-reliable" strategy is much more reliable than the default Sidekiq fetcher, though. Compared to the reliable fetch strategy, it does not increase pressure on Redis significantly.
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'gitlab-sidekiq-fetcher'
|
3
|
-
s.version = '0.
|
3
|
+
s.version = '0.7.0'
|
4
4
|
s.authors = ['TEA', 'GitLab']
|
5
5
|
s.email = 'valery@gitlab.com'
|
6
6
|
s.license = 'LGPL-3.0'
|
@@ -10,5 +10,5 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.require_paths = ['lib']
|
11
11
|
s.files = `git ls-files`.split($\)
|
12
12
|
s.test_files = []
|
13
|
-
s.add_dependency 'sidekiq', '
|
13
|
+
s.add_dependency 'sidekiq', '~> 6.1'
|
14
14
|
end
|
@@ -41,10 +41,14 @@ module Sidekiq
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def self.setup_reliable_fetch!(config)
|
44
|
-
|
45
|
-
|
44
|
+
fetch_strategy = if config.options[:semi_reliable_fetch]
|
45
|
+
Sidekiq::SemiReliableFetch
|
46
|
+
else
|
47
|
+
Sidekiq::ReliableFetch
|
48
|
+
end
|
49
|
+
|
50
|
+
config.options[:fetch] = fetch_strategy.new(config.options)
|
46
51
|
|
47
|
-
config.options[:fetch] = fetch
|
48
52
|
Sidekiq.logger.info('GitLab reliable fetch activated!')
|
49
53
|
|
50
54
|
start_heartbeat_thread
|
@@ -82,11 +86,44 @@ module Sidekiq
|
|
82
86
|
Sidekiq.logger.debug("Heartbeat for hostname: #{hostname} and pid: #{pid}")
|
83
87
|
end
|
84
88
|
|
85
|
-
def
|
86
|
-
|
89
|
+
def self.worker_dead?(hostname, pid, conn)
|
90
|
+
!conn.get(heartbeat_key(hostname, pid))
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.heartbeat_key(hostname, pid)
|
94
|
+
"reliable-fetcher-heartbeat-#{hostname}-#{pid}"
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.working_queue_name(queue)
|
98
|
+
"#{WORKING_QUEUE_PREFIX}:#{queue}:#{hostname}:#{pid}"
|
99
|
+
end
|
100
|
+
|
101
|
+
attr_reader :cleanup_interval, :last_try_to_take_lease_at, :lease_interval,
|
102
|
+
:queues, :use_semi_reliable_fetch,
|
103
|
+
:strictly_ordered_queues
|
104
|
+
|
105
|
+
def initialize(options)
|
106
|
+
raise ArgumentError, 'missing queue list' unless options[:queues]
|
107
|
+
|
108
|
+
@cleanup_interval = options.fetch(:cleanup_interval, DEFAULT_CLEANUP_INTERVAL)
|
109
|
+
@lease_interval = options.fetch(:lease_interval, DEFAULT_LEASE_INTERVAL)
|
110
|
+
@last_try_to_take_lease_at = 0
|
111
|
+
@strictly_ordered_queues = !!options[:strict]
|
112
|
+
@queues = options[:queues].map { |q| "queue:#{q}" }
|
113
|
+
end
|
114
|
+
|
115
|
+
def retrieve_work
|
116
|
+
clean_working_queues! if take_lease
|
117
|
+
|
118
|
+
retrieve_unit_of_work
|
119
|
+
end
|
120
|
+
|
121
|
+
def retrieve_unit_of_work
|
122
|
+
raise NotImplementedError,
|
123
|
+
"#{self.class} does not implement #{__method__}"
|
87
124
|
end
|
88
125
|
|
89
|
-
def
|
126
|
+
def bulk_requeue(inprogress, _options)
|
90
127
|
return if inprogress.empty?
|
91
128
|
|
92
129
|
Sidekiq.redis do |conn|
|
@@ -94,7 +131,7 @@ module Sidekiq
|
|
94
131
|
conn.multi do |multi|
|
95
132
|
preprocess_interrupted_job(unit_of_work.job, unit_of_work.queue, multi)
|
96
133
|
|
97
|
-
multi.lrem(working_queue_name(unit_of_work.queue), 1, unit_of_work.job)
|
134
|
+
multi.lrem(self.class.working_queue_name(unit_of_work.queue), 1, unit_of_work.job)
|
98
135
|
end
|
99
136
|
end
|
100
137
|
end
|
@@ -102,17 +139,9 @@ module Sidekiq
|
|
102
139
|
Sidekiq.logger.warn("Failed to requeue #{inprogress.size} jobs: #{e.message}")
|
103
140
|
end
|
104
141
|
|
105
|
-
|
106
|
-
original_queue = working_queue.gsub(/#{WORKING_QUEUE_PREFIX}:|:[^:]*:[0-9]*\z/, '')
|
107
|
-
|
108
|
-
Sidekiq.redis do |conn|
|
109
|
-
while job = conn.rpop(working_queue)
|
110
|
-
preprocess_interrupted_job(job, original_queue)
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
142
|
+
private
|
114
143
|
|
115
|
-
def
|
144
|
+
def preprocess_interrupted_job(job, queue, conn = nil)
|
116
145
|
msg = Sidekiq.load_json(job)
|
117
146
|
msg['interrupted_count'] = msg['interrupted_count'].to_i + 1
|
118
147
|
|
@@ -123,9 +152,23 @@ module Sidekiq
|
|
123
152
|
end
|
124
153
|
end
|
125
154
|
|
155
|
+
# If you want this method to be run in a scope of multi connection
|
156
|
+
# you need to pass it
|
157
|
+
def requeue_job(queue, msg, conn)
|
158
|
+
with_connection(conn) do |conn|
|
159
|
+
conn.lpush(queue, Sidekiq.dump_json(msg))
|
160
|
+
end
|
161
|
+
|
162
|
+
Sidekiq.logger.info(
|
163
|
+
message: "Pushed job #{msg['jid']} back to queue #{queue}",
|
164
|
+
jid: msg['jid'],
|
165
|
+
queue: queue
|
166
|
+
)
|
167
|
+
end
|
168
|
+
|
126
169
|
# Detect "old" jobs and requeue them because the worker they were assigned
|
127
170
|
# to probably failed miserably.
|
128
|
-
def
|
171
|
+
def clean_working_queues!
|
129
172
|
Sidekiq.logger.info('Cleaning working queues')
|
130
173
|
|
131
174
|
Sidekiq.redis do |conn|
|
@@ -135,30 +178,28 @@ module Sidekiq
|
|
135
178
|
|
136
179
|
continue if hostname.nil? || pid.nil?
|
137
180
|
|
138
|
-
clean_working_queue!(key) if worker_dead?(hostname, pid, conn)
|
181
|
+
clean_working_queue!(key) if self.class.worker_dead?(hostname, pid, conn)
|
139
182
|
end
|
140
183
|
end
|
141
184
|
end
|
142
185
|
|
143
|
-
def
|
144
|
-
|
145
|
-
end
|
146
|
-
|
147
|
-
def self.heartbeat_key(hostname, pid)
|
148
|
-
"reliable-fetcher-heartbeat-#{hostname}-#{pid}"
|
149
|
-
end
|
186
|
+
def clean_working_queue!(working_queue)
|
187
|
+
original_queue = working_queue.gsub(/#{WORKING_QUEUE_PREFIX}:|:[^:]*:[0-9]*\z/, '')
|
150
188
|
|
151
|
-
|
152
|
-
|
189
|
+
Sidekiq.redis do |conn|
|
190
|
+
while job = conn.rpop(working_queue)
|
191
|
+
preprocess_interrupted_job(job, original_queue)
|
192
|
+
end
|
193
|
+
end
|
153
194
|
end
|
154
195
|
|
155
|
-
def
|
196
|
+
def interruption_exhausted?(msg)
|
156
197
|
return false if max_retries_after_interruption(msg['class']) < 0
|
157
198
|
|
158
199
|
msg['interrupted_count'].to_i >= max_retries_after_interruption(msg['class'])
|
159
200
|
end
|
160
201
|
|
161
|
-
def
|
202
|
+
def max_retries_after_interruption(worker_class)
|
162
203
|
max_retries_after_interruption = nil
|
163
204
|
|
164
205
|
max_retries_after_interruption ||= begin
|
@@ -171,7 +212,7 @@ module Sidekiq
|
|
171
212
|
max_retries_after_interruption
|
172
213
|
end
|
173
214
|
|
174
|
-
def
|
215
|
+
def send_to_quarantine(msg, multi_connection = nil)
|
175
216
|
Sidekiq.logger.warn(
|
176
217
|
class: msg['class'],
|
177
218
|
jid: msg['jid'],
|
@@ -182,52 +223,13 @@ module Sidekiq
|
|
182
223
|
Sidekiq::InterruptedSet.new.put(job, connection: multi_connection)
|
183
224
|
end
|
184
225
|
|
185
|
-
# If you want this method to be run is a scope of multi connection
|
186
|
-
# you need to pass it
|
187
|
-
def self.requeue_job(queue, msg, conn)
|
188
|
-
with_connection(conn) do |conn|
|
189
|
-
conn.lpush(queue, Sidekiq.dump_json(msg))
|
190
|
-
end
|
191
|
-
|
192
|
-
Sidekiq.logger.info(
|
193
|
-
message: "Pushed job #{msg['jid']} back to queue #{queue}",
|
194
|
-
jid: msg['jid'],
|
195
|
-
queue: queue
|
196
|
-
)
|
197
|
-
end
|
198
|
-
|
199
226
|
# Yield block with an existing connection or creates another one
|
200
|
-
def
|
227
|
+
def with_connection(conn)
|
201
228
|
return yield(conn) if conn
|
202
229
|
|
203
|
-
Sidekiq.redis { |
|
204
|
-
end
|
205
|
-
|
206
|
-
attr_reader :cleanup_interval, :last_try_to_take_lease_at, :lease_interval,
|
207
|
-
:queues, :use_semi_reliable_fetch,
|
208
|
-
:strictly_ordered_queues
|
209
|
-
|
210
|
-
def initialize(options)
|
211
|
-
@cleanup_interval = options.fetch(:cleanup_interval, DEFAULT_CLEANUP_INTERVAL)
|
212
|
-
@lease_interval = options.fetch(:lease_interval, DEFAULT_LEASE_INTERVAL)
|
213
|
-
@last_try_to_take_lease_at = 0
|
214
|
-
@strictly_ordered_queues = !!options[:strict]
|
215
|
-
@queues = options[:queues].map { |q| "queue:#{q}" }
|
216
|
-
end
|
217
|
-
|
218
|
-
def retrieve_work
|
219
|
-
self.class.clean_working_queues! if take_lease
|
220
|
-
|
221
|
-
retrieve_unit_of_work
|
230
|
+
Sidekiq.redis { |redis_conn| yield(redis_conn) }
|
222
231
|
end
|
223
232
|
|
224
|
-
def retrieve_unit_of_work
|
225
|
-
raise NotImplementedError,
|
226
|
-
"#{self.class} does not implement #{__method__}"
|
227
|
-
end
|
228
|
-
|
229
|
-
private
|
230
|
-
|
231
233
|
def take_lease
|
232
234
|
return unless allowed_to_take_a_lease?
|
233
235
|
|
@@ -6,23 +6,21 @@ module Sidekiq
|
|
6
6
|
# we inject a regular sleep into the loop.
|
7
7
|
RELIABLE_FETCH_IDLE_TIMEOUT = 5 # seconds
|
8
8
|
|
9
|
-
attr_reader :
|
9
|
+
attr_reader :queues_size
|
10
10
|
|
11
11
|
def initialize(options)
|
12
12
|
super
|
13
13
|
|
14
|
+
@queues = queues.uniq if strictly_ordered_queues
|
14
15
|
@queues_size = queues.size
|
15
|
-
@queues_iterator = queues.cycle
|
16
16
|
end
|
17
17
|
|
18
18
|
private
|
19
19
|
|
20
20
|
def retrieve_unit_of_work
|
21
|
-
|
22
|
-
|
23
|
-
queues_size.times do
|
24
|
-
queue = queues_iterator.next
|
21
|
+
queues_list = strictly_ordered_queues ? queues : queues.shuffle
|
25
22
|
|
23
|
+
queues_list.each do |queue|
|
26
24
|
work = Sidekiq.redis do |conn|
|
27
25
|
conn.rpoplpush(queue, self.class.working_queue_name(queue))
|
28
26
|
end
|
@@ -39,14 +39,15 @@ describe Sidekiq::BaseReliableFetch do
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
describe '
|
42
|
+
describe '#bulk_requeue' do
|
43
|
+
let(:options) { { queues: %w[foo bar] } }
|
43
44
|
let!(:queue1) { Sidekiq::Queue.new('foo') }
|
44
45
|
let!(:queue2) { Sidekiq::Queue.new('bar') }
|
45
46
|
|
46
47
|
it 'requeues the bulk' do
|
47
48
|
uow = described_class::UnitOfWork
|
48
49
|
jobs = [ uow.new('queue:foo', job), uow.new('queue:foo', job), uow.new('queue:bar', job) ]
|
49
|
-
described_class.bulk_requeue(jobs,
|
50
|
+
described_class.new(options).bulk_requeue(jobs, nil)
|
50
51
|
|
51
52
|
expect(queue1.size).to eq 2
|
52
53
|
expect(queue2.size).to eq 1
|
@@ -56,7 +57,7 @@ describe Sidekiq::BaseReliableFetch do
|
|
56
57
|
uow = described_class::UnitOfWork
|
57
58
|
interrupted_job = Sidekiq.dump_json(class: 'Bob', args: [1, 2, 'foo'], interrupted_count: 3)
|
58
59
|
jobs = [ uow.new('queue:foo', interrupted_job), uow.new('queue:foo', job), uow.new('queue:bar', job) ]
|
59
|
-
described_class.bulk_requeue(jobs,
|
60
|
+
described_class.new(options).bulk_requeue(jobs, nil)
|
60
61
|
|
61
62
|
expect(queue1.size).to eq 1
|
62
63
|
expect(queue2.size).to eq 1
|
@@ -69,7 +70,7 @@ describe Sidekiq::BaseReliableFetch do
|
|
69
70
|
uow = described_class::UnitOfWork
|
70
71
|
interrupted_job = Sidekiq.dump_json(class: 'Bob', args: [1, 2, 'foo'], interrupted_count: 3)
|
71
72
|
jobs = [ uow.new('queue:foo', interrupted_job), uow.new('queue:foo', job), uow.new('queue:bar', job) ]
|
72
|
-
described_class.bulk_requeue(jobs,
|
73
|
+
described_class.new(options).bulk_requeue(jobs, nil)
|
73
74
|
|
74
75
|
expect(queue1.size).to eq 2
|
75
76
|
expect(queue2.size).to eq 1
|
@@ -80,7 +81,7 @@ describe Sidekiq::BaseReliableFetch do
|
|
80
81
|
end
|
81
82
|
|
82
83
|
it 'sets heartbeat' do
|
83
|
-
config = double(:sidekiq_config, options: { queues: [] })
|
84
|
+
config = double(:sidekiq_config, options: { queues: %w[foo bar] })
|
84
85
|
|
85
86
|
heartbeat_thread = described_class.setup_reliable_fetch!(config)
|
86
87
|
|
@@ -5,7 +5,7 @@ shared_examples 'a Sidekiq fetcher' do
|
|
5
5
|
|
6
6
|
describe '#retrieve_work' do
|
7
7
|
let(:job) { Sidekiq.dump_json(class: 'Bob', args: [1, 2, 'foo']) }
|
8
|
-
let(:fetcher) { described_class.new(queues:
|
8
|
+
let(:fetcher) { described_class.new(queues: queues) }
|
9
9
|
|
10
10
|
it 'retrieves the job and puts it to working queue' do
|
11
11
|
Sidekiq.redis { |conn| conn.rpush('queue:assigned', job) }
|
@@ -61,11 +61,11 @@ shared_examples 'a Sidekiq fetcher' do
|
|
61
61
|
it 'does not clean up orphaned jobs more than once per cleanup interval' do
|
62
62
|
Sidekiq.redis = Sidekiq::RedisConnection.create(url: REDIS_URL, size: 10)
|
63
63
|
|
64
|
-
expect(
|
64
|
+
expect(fetcher).to receive(:clean_working_queues!).once
|
65
65
|
|
66
66
|
threads = 10.times.map do
|
67
67
|
Thread.new do
|
68
|
-
|
68
|
+
fetcher.retrieve_work
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
data/tests/reliability/worker.rb
CHANGED
@@ -8,19 +8,7 @@ class ReliabilityTestWorker
|
|
8
8
|
sleep 1
|
9
9
|
|
10
10
|
Sidekiq.redis do |redis|
|
11
|
-
redis.lpush(REDIS_FINISHED_LIST,
|
11
|
+
redis.lpush(REDIS_FINISHED_LIST, jid)
|
12
12
|
end
|
13
13
|
end
|
14
|
-
|
15
|
-
def get_sidekiq_job_id
|
16
|
-
context_data = Thread.current[:sidekiq_context]&.first
|
17
|
-
|
18
|
-
return unless context_data
|
19
|
-
|
20
|
-
index = context_data.index('JID-')
|
21
|
-
|
22
|
-
return unless index
|
23
|
-
|
24
|
-
context_data[index + 4..-1]
|
25
|
-
end
|
26
14
|
end
|
data/tests/support/utils.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gitlab-sidekiq-fetcher
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- TEA
|
@@ -9,28 +9,22 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2020-07-
|
12
|
+
date: 2020-07-30 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sidekiq
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- - "
|
18
|
+
- - "~>"
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: '
|
21
|
-
- - "<"
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version: '7'
|
20
|
+
version: '6.1'
|
24
21
|
type: :runtime
|
25
22
|
prerelease: false
|
26
23
|
version_requirements: !ruby/object:Gem::Requirement
|
27
24
|
requirements:
|
28
|
-
- - "
|
29
|
-
- !ruby/object:Gem::Version
|
30
|
-
version: '5'
|
31
|
-
- - "<"
|
25
|
+
- - "~>"
|
32
26
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
27
|
+
version: '6.1'
|
34
28
|
description: Redis reliable queue pattern implemented in Sidekiq
|
35
29
|
email: valery@gitlab.com
|
36
30
|
executables: []
|
@@ -84,7 +78,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
84
78
|
- !ruby/object:Gem::Version
|
85
79
|
version: '0'
|
86
80
|
requirements: []
|
87
|
-
rubygems_version: 3.0.
|
81
|
+
rubygems_version: 3.0.6
|
88
82
|
signing_key:
|
89
83
|
specification_version: 4
|
90
84
|
summary: Reliable fetch extension for Sidekiq
|