gitlab-sidekiq-fetcher 0.5.3 → 0.5.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/gitlab-sidekiq-fetcher.gemspec +1 -1
- data/lib/sidekiq/base_reliable_fetch.rb +27 -18
- data/spec/base_reliable_fetch_spec.rb +1 -1
- data/spec/fetch_shared_examples.rb +46 -3
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 477650a08755f00beb453c4867a270fa8469a83a372b5b09ba1c030885899699
|
4
|
+
data.tar.gz: da3fcf3f0b67dd3f71c73ea80d09666e82fa9360cdf5cbf6c8b8e5668b85bfb5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b4d03a71a6c00e2fa55affd4af5633c2c1ef58093c8bd82b95d4a1603523d940e35b8db4643becf20236224ef065fc879b70585933569efa893e0e05481bffb9
|
7
|
+
data.tar.gz: 4fac10a26e3507c70927ad8689c2d8903d452cc9ed25a6522dc5c2c7e5e7fcb13d8412013eadb435a9a2feecab51130ad516553c4bbb49c72b2b36827f75c3cf
|
@@ -68,20 +68,24 @@ module Sidekiq
|
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
71
|
-
def self.
|
72
|
-
|
71
|
+
def self.hostname
|
72
|
+
Socket.gethostname
|
73
73
|
end
|
74
74
|
|
75
|
-
def self.
|
76
|
-
|
75
|
+
def self.process_nonce
|
76
|
+
@@process_nonce ||= SecureRandom.hex(6)
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.identity
|
80
|
+
@@identity ||= "#{hostname}:#{$$}:#{process_nonce}"
|
77
81
|
end
|
78
82
|
|
79
83
|
def self.heartbeat
|
80
84
|
Sidekiq.redis do |conn|
|
81
|
-
conn.set(heartbeat_key(
|
85
|
+
conn.set(heartbeat_key(identity), 1, ex: HEARTBEAT_LIFESPAN)
|
82
86
|
end
|
83
87
|
|
84
|
-
Sidekiq.logger.debug("Heartbeat for
|
88
|
+
Sidekiq.logger.debug("Heartbeat for #{identity}")
|
85
89
|
end
|
86
90
|
|
87
91
|
def self.bulk_requeue(inprogress, _options)
|
@@ -100,9 +104,7 @@ module Sidekiq
|
|
100
104
|
Sidekiq.logger.warn("Failed to requeue #{inprogress.size} jobs: #{e.message}")
|
101
105
|
end
|
102
106
|
|
103
|
-
def self.clean_working_queue!(working_queue)
|
104
|
-
original_queue = working_queue.gsub(/#{WORKING_QUEUE_PREFIX}:|:[^:]*:[0-9]*\z/, '')
|
105
|
-
|
107
|
+
def self.clean_working_queue!(original_queue, working_queue)
|
106
108
|
Sidekiq.redis do |conn|
|
107
109
|
while job = conn.rpop(working_queue)
|
108
110
|
preprocess_interrupted_job(job, original_queue)
|
@@ -121,6 +123,14 @@ module Sidekiq
|
|
121
123
|
end
|
122
124
|
end
|
123
125
|
|
126
|
+
def self.valid_identity_format?(identity)
|
127
|
+
# New format is "{hostname}:{pid}:{randomhex}
|
128
|
+
# Old format is "{hostname}:{pid}"
|
129
|
+
|
130
|
+
# Test the newer format first, only checking the older if necessary
|
131
|
+
identity.match(/[^:]*:[0-9]*:[0-9a-f]*\z/) || identity.match(/([^:]*):([0-9]*)\z/)
|
132
|
+
end
|
133
|
+
|
124
134
|
# Detect "old" jobs and requeue them because the worker they were assigned
|
125
135
|
# to probably failed miserably.
|
126
136
|
def self.clean_working_queues!
|
@@ -128,26 +138,25 @@ module Sidekiq
|
|
128
138
|
|
129
139
|
Sidekiq.redis do |conn|
|
130
140
|
conn.scan_each(match: "#{WORKING_QUEUE_PREFIX}:queue:*", count: SCAN_COUNT) do |key|
|
131
|
-
|
132
|
-
hostname, pid = key.scan(/:([^:]*):([0-9]*)\z/).flatten
|
141
|
+
original_queue, identity = key.scan(/#{WORKING_QUEUE_PREFIX}:(queue:[^:]*):(.*)\z/).flatten
|
133
142
|
|
134
|
-
next
|
143
|
+
next unless valid_identity_format?(identity)
|
135
144
|
|
136
|
-
clean_working_queue!(key) if worker_dead?(
|
145
|
+
clean_working_queue!(original_queue, key) if worker_dead?(identity, conn)
|
137
146
|
end
|
138
147
|
end
|
139
148
|
end
|
140
149
|
|
141
|
-
def self.worker_dead?(
|
142
|
-
!conn.get(heartbeat_key(
|
150
|
+
def self.worker_dead?(identity, conn)
|
151
|
+
!conn.get(heartbeat_key(identity))
|
143
152
|
end
|
144
153
|
|
145
|
-
def self.heartbeat_key(
|
146
|
-
"reliable-fetcher-heartbeat-#{
|
154
|
+
def self.heartbeat_key(identity)
|
155
|
+
"reliable-fetcher-heartbeat-#{identity.gsub(':', '-')}"
|
147
156
|
end
|
148
157
|
|
149
158
|
def self.working_queue_name(queue)
|
150
|
-
"#{WORKING_QUEUE_PREFIX}:#{queue}:#{
|
159
|
+
"#{WORKING_QUEUE_PREFIX}:#{queue}:#{identity}"
|
151
160
|
end
|
152
161
|
|
153
162
|
def self.interruption_exhausted?(msg)
|
@@ -87,7 +87,7 @@ describe Sidekiq::BaseReliableFetch do
|
|
87
87
|
Sidekiq.redis do |conn|
|
88
88
|
sleep 0.2 # Give the time to heartbeat thread to make a loop
|
89
89
|
|
90
|
-
heartbeat_key = described_class.heartbeat_key(
|
90
|
+
heartbeat_key = described_class.heartbeat_key(described_class.identity)
|
91
91
|
heartbeat = conn.get(heartbeat_key)
|
92
92
|
|
93
93
|
expect(heartbeat).not_to be_nil
|
@@ -35,6 +35,7 @@ shared_examples 'a Sidekiq fetcher' do
|
|
35
35
|
|
36
36
|
uow = fetcher.retrieve_work
|
37
37
|
|
38
|
+
expect(uow).to_not be_nil
|
38
39
|
expect(uow.job).to eq expected_job
|
39
40
|
|
40
41
|
Sidekiq.redis do |conn|
|
@@ -57,6 +58,24 @@ shared_examples 'a Sidekiq fetcher' do
|
|
57
58
|
end
|
58
59
|
end
|
59
60
|
|
61
|
+
it 'requeues jobs from legacy dead working queue with incremented interrupted_count' do
|
62
|
+
Sidekiq.redis do |conn|
|
63
|
+
conn.rpush(legacy_other_process_working_queue_name('assigned'), job)
|
64
|
+
end
|
65
|
+
|
66
|
+
expected_job = Sidekiq.load_json(job)
|
67
|
+
expected_job['interrupted_count'] = 1
|
68
|
+
expected_job = Sidekiq.dump_json(expected_job)
|
69
|
+
|
70
|
+
uow = fetcher.retrieve_work
|
71
|
+
|
72
|
+
expect(uow).to_not be_nil
|
73
|
+
expect(uow.job).to eq expected_job
|
74
|
+
|
75
|
+
Sidekiq.redis do |conn|
|
76
|
+
expect(conn.llen(legacy_other_process_working_queue_name('assigned'))).to eq 0
|
77
|
+
end
|
78
|
+
end
|
60
79
|
|
61
80
|
it 'does not requeue jobs from live working queue' do
|
62
81
|
working_queue = live_other_process_working_queue_name('assigned')
|
@@ -113,6 +132,24 @@ shared_examples 'a Sidekiq fetcher' do
|
|
113
132
|
|
114
133
|
expect(jobs).to include 'this_job_should_not_stuck'
|
115
134
|
end
|
135
|
+
|
136
|
+
context 'with short cleanup interval' do
|
137
|
+
let(:short_interval) { 1 }
|
138
|
+
let(:fetcher) { described_class.new(queues: queues, lease_interval: short_interval, cleanup_interval: short_interval) }
|
139
|
+
|
140
|
+
it 'requeues when there is no heartbeat' do
|
141
|
+
Sidekiq.redis { |conn| conn.rpush('queue:assigned', job) }
|
142
|
+
# Use of retrieve_work twice with a sleep ensures we have exercised the
|
143
|
+
# `identity` method to create the working queue key name and that it
|
144
|
+
# matches the patterns used in the cleanup
|
145
|
+
uow = fetcher.retrieve_work
|
146
|
+
sleep(short_interval + 1)
|
147
|
+
uow = fetcher.retrieve_work
|
148
|
+
|
149
|
+
# Will only receive a UnitOfWork if the job was detected as failed and requeued
|
150
|
+
expect(uow).to_not be_nil
|
151
|
+
end
|
152
|
+
end
|
116
153
|
end
|
117
154
|
end
|
118
155
|
|
@@ -122,17 +159,23 @@ def working_queue_size(queue_name)
|
|
122
159
|
end
|
123
160
|
end
|
124
161
|
|
125
|
-
def
|
162
|
+
def legacy_other_process_working_queue_name(queue)
|
126
163
|
"#{Sidekiq::BaseReliableFetch::WORKING_QUEUE_PREFIX}:queue:#{queue}:#{Socket.gethostname}:#{::Process.pid + 1}"
|
127
164
|
end
|
128
165
|
|
166
|
+
|
167
|
+
def other_process_working_queue_name(queue)
|
168
|
+
"#{Sidekiq::BaseReliableFetch::WORKING_QUEUE_PREFIX}:queue:#{queue}:#{Socket.gethostname}:#{::Process.pid + 1}:#{::SecureRandom.hex(6)}"
|
169
|
+
end
|
170
|
+
|
129
171
|
def live_other_process_working_queue_name(queue)
|
130
172
|
pid = ::Process.pid + 1
|
131
173
|
hostname = Socket.gethostname
|
174
|
+
nonce = SecureRandom.hex(6)
|
132
175
|
|
133
176
|
Sidekiq.redis do |conn|
|
134
|
-
conn.set(Sidekiq::BaseReliableFetch.heartbeat_key(hostname
|
177
|
+
conn.set(Sidekiq::BaseReliableFetch.heartbeat_key("#{hostname}-#{pid}-#{nonce}"), 1)
|
135
178
|
end
|
136
179
|
|
137
|
-
"#{Sidekiq::BaseReliableFetch::WORKING_QUEUE_PREFIX}:queue:#{queue}:#{hostname}:#{pid}"
|
180
|
+
"#{Sidekiq::BaseReliableFetch::WORKING_QUEUE_PREFIX}:queue:#{queue}:#{hostname}:#{pid}:#{nonce}"
|
138
181
|
end
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gitlab-sidekiq-fetcher
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- TEA
|
8
8
|
- GitLab
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2021-02-
|
12
|
+
date: 2021-02-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sidekiq
|
@@ -63,7 +63,7 @@ homepage: https://gitlab.com/gitlab-org/sidekiq-reliable-fetch/
|
|
63
63
|
licenses:
|
64
64
|
- LGPL-3.0
|
65
65
|
metadata: {}
|
66
|
-
post_install_message:
|
66
|
+
post_install_message:
|
67
67
|
rdoc_options: []
|
68
68
|
require_paths:
|
69
69
|
- lib
|
@@ -79,7 +79,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
79
79
|
version: '0'
|
80
80
|
requirements: []
|
81
81
|
rubygems_version: 3.1.4
|
82
|
-
signing_key:
|
82
|
+
signing_key:
|
83
83
|
specification_version: 4
|
84
84
|
summary: Reliable fetch extension for Sidekiq
|
85
85
|
test_files: []
|