gitlab-sidekiq-fetcher 0.5.3 → 0.5.4
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-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: []
|