Skip to content

Commit 2c7157a

Browse files
aneroixti
authored andcommitted
fix: Adjust queue name handling in puash backs (#201)
1 parent eb2c0ee commit 2c7157a

File tree

4 files changed

+68
-21
lines changed

4 files changed

+68
-21
lines changed

lib/sidekiq/throttled/strategy.rb

+13-6
Original file line numberDiff line numberDiff line change
@@ -143,42 +143,49 @@ def calc_target_queue(work) # rubocop:disable Metrics/MethodLength
143143
when NilClass
144144
work.queue
145145
when String, Symbol
146-
requeue_to.to_s
146+
requeue_to
147147
else
148148
raise ArgumentError, "Invalid argument for `to`"
149149
end
150150

151151
target = work.queue if target.nil? || target.empty?
152152

153-
target.start_with?("queue:") ? target : "queue:#{target}"
153+
target.to_s
154154
end
155155

156156
# Push the job back to the head of the queue.
157+
# The queue name is expected to include the "queue:" prefix, so we add it if it's missing.
157158
def re_enqueue_throttled(work, target_queue)
159+
target_queue = "queue:#{target_queue}" unless target_queue.start_with?("queue:")
160+
158161
case work.class.name
159162
when "Sidekiq::Pro::SuperFetch::UnitOfWork"
160163
# Calls SuperFetch UnitOfWork's requeue to remove the job from the
161164
# temporary queue and push job back to the head of the target queue, so that
162165
# the job won't be tried immediately after it was requeued (in most cases).
163-
work.queue = target_queue if target_queue
166+
work.queue = target_queue
164167
work.requeue
165168
else
166169
# This is the same operation Sidekiq performs upon `Sidekiq::Worker.perform_async` call.
167170
Sidekiq.redis { |conn| conn.lpush(target_queue, work.job) }
168171
end
169172
end
170173

174+
# Reschedule the job to be executed later in the target queue.
175+
# The queue name should NOT include the "queue:" prefix, so we remove it if it's present.
171176
def reschedule_throttled(work, target_queue)
172-
message = JSON.parse(work.job)
173-
job_class = message.fetch("wrapped") { message.fetch("class") { return false } }
174-
job_args = message["args"]
177+
target_queue = target_queue.delete_prefix("queue:")
178+
message = JSON.parse(work.job)
179+
job_class = message.fetch("wrapped") { message.fetch("class") { return false } }
180+
job_args = message["args"]
175181

176182
# Re-enqueue the job to the target queue at another time as a NEW unit of work
177183
# AND THEN mark this work as done, so SuperFetch doesn't think this instance is orphaned
178184
# Technically, the job could processed twice if the process dies between the two lines,
179185
# but your job should be idempotent anyway, right?
180186
# The job running twice was already a risk with SuperFetch anyway and this doesn't really increase that risk.
181187
Sidekiq::Client.enqueue_to_in(target_queue, retry_in(work), Object.const_get(job_class), *job_args)
188+
182189
work.acknowledge
183190
end
184191

lib/sidekiq/throttled/strategy_collection.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def throttled?(...)
4343

4444
# @return [Float] How long, in seconds, before we'll next be able to take on jobs
4545
def retry_in(*args)
46-
max { |s| s.retry_in(*args) }
46+
map { |s| s.retry_in(*args) }.max
4747
end
4848

4949
# Marks job as being processed.

spec/lib/sidekiq/throttled/strategy_collection_spec.rb

+20
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,26 @@
9090
end
9191
end
9292

93+
describe "#retry_in" do
94+
let(:strategies) do
95+
[
96+
{ limit: 1, key_suffix: ->(_project_id, user_id) { user_id } },
97+
{ limit: 10 },
98+
{ limit: 30 }
99+
]
100+
end
101+
let(:job_id) { jid }
102+
103+
it do
104+
collection.each_with_index do |s, i|
105+
allow(s).to receive(:retry_in).with(job_id, 11, 22).and_return(i * 10)
106+
end
107+
108+
# Returns the max value from the strategies' retry_in returned values
109+
expect(collection.retry_in(job_id, 11, 22)).to eq 20
110+
end
111+
end
112+
93113
describe "#finalize!" do
94114
subject(:finalize!) { collection.finalize!(job_id, *job_args) }
95115

spec/lib/sidekiq/throttled/strategy_spec.rb

+34-14
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ def scheduled_redis_item_and_score
401401

402402
item, score = scheduled_redis_item_and_score
403403
expect(JSON.parse(item)).to include("class" => "ThrottledTestJob", "args" => [1],
404-
"queue" => "queue:default")
404+
"queue" => "default")
405405
expect(score.to_f).to be_within(31.0).of(Time.now.to_f + 330.0)
406406

407407
# Ensure that the job is no longer in the private queue
@@ -425,7 +425,7 @@ def scheduled_redis_item_and_score
425425

426426
item, score = scheduled_redis_item_and_score
427427
expect(JSON.parse(item)).to include("class" => "ThrottledTestJob", "args" => [1],
428-
"queue" => "queue:other_queue")
428+
"queue" => "other_queue")
429429
expect(score.to_f).to be_within(31.0).of(Time.now.to_f + 330.0)
430430

431431
# Ensure that the job is no longer in the private queue
@@ -449,7 +449,7 @@ def scheduled_redis_item_and_score
449449

450450
item, score = scheduled_redis_item_and_score
451451
expect(JSON.parse(item)).to include("class" => "ThrottledTestJob", "args" => [1],
452-
"queue" => "queue:default")
452+
"queue" => "default")
453453
expect(score.to_f).to be_within(31.0).of(Time.now.to_f + 330.0)
454454

455455
# Ensure that the job is no longer in the private queue
@@ -475,7 +475,7 @@ def scheduled_redis_item_and_score
475475

476476
item, score = scheduled_redis_item_and_score
477477
expect(JSON.parse(item)).to include("class" => "ThrottledTestJob", "args" => [1],
478-
"queue" => "queue:other_queue")
478+
"queue" => "other_queue")
479479
expect(score.to_f).to be_within(31.0).of(Time.now.to_f + 330.0)
480480

481481
# Ensure that the job is no longer in the private queue
@@ -503,7 +503,7 @@ def scheduled_redis_item_and_score
503503

504504
item, score = scheduled_redis_item_and_score
505505
expect(JSON.parse(item)).to include("class" => "ThrottledTestJob", "args" => [1],
506-
"queue" => "queue:default")
506+
"queue" => "default")
507507
expect(score.to_f).to be_within(31.0).of(Time.now.to_f + 330.0)
508508

509509
# Ensure that the job is no longer in the private queue
@@ -531,7 +531,7 @@ def scheduled_redis_item_and_score
531531

532532
item, score = scheduled_redis_item_and_score
533533
expect(JSON.parse(item)).to include("class" => "ThrottledTestJob", "args" => [1],
534-
"queue" => "queue:default")
534+
"queue" => "default")
535535
expect(score.to_f).to be_within(51.0).of(Time.now.to_f + 550.0)
536536

537537
# Ensure that the job is no longer in the private queue
@@ -666,7 +666,17 @@ def scheduled_redis_item_and_score
666666
it "returns false and does not reschedule the job" do
667667
expect(Sidekiq::Client).not_to receive(:enqueue_to_in)
668668
expect(work).not_to receive(:acknowledge)
669-
expect(subject.send(:reschedule_throttled, work, requeue_to: "queue:default")).to be_falsey
669+
expect(subject.send(:reschedule_throttled, work, "queue:default")).to be_falsey
670+
end
671+
end
672+
673+
context "when target_queue has the 'queue:' prefix" do
674+
let(:target_queue) { "queue:default" }
675+
676+
it "reschedules the job to the specified queue" do
677+
expect(Sidekiq::Client).to receive(:enqueue_to_in).with("default", anything, anything, anything)
678+
expect(work).to receive(:acknowledge)
679+
subject.send(:reschedule_throttled, work, target_queue)
670680
end
671681
end
672682
end
@@ -807,7 +817,7 @@ def scheduled_redis_item_and_score
807817

808818
item, score = scheduled_redis_item_and_score
809819
expect(JSON.parse(item)).to include("class" => "ThrottledTestJob", "args" => [1],
810-
"queue" => "queue:default")
820+
"queue" => "default")
811821
expect(score.to_f).to be_within(31.0).of(Time.now.to_f + 330.0)
812822
end
813823
end
@@ -823,7 +833,7 @@ def scheduled_redis_item_and_score
823833

824834
item, score = scheduled_redis_item_and_score
825835
expect(JSON.parse(item)).to include("class" => "ThrottledTestJob", "args" => [1],
826-
"queue" => "queue:other_queue")
836+
"queue" => "other_queue")
827837
expect(score.to_f).to be_within(31.0).of(Time.now.to_f + 330.0)
828838
end
829839
end
@@ -839,7 +849,7 @@ def scheduled_redis_item_and_score
839849

840850
item, score = scheduled_redis_item_and_score
841851
expect(JSON.parse(item)).to include("class" => "ThrottledTestJob", "args" => [1],
842-
"queue" => "queue:default")
852+
"queue" => "default")
843853
expect(score.to_f).to be_within(31.0).of(Time.now.to_f + 330.0)
844854
end
845855
end
@@ -859,7 +869,7 @@ def scheduled_redis_item_and_score
859869

860870
item, score = scheduled_redis_item_and_score
861871
expect(JSON.parse(item)).to include("class" => "ThrottledTestJob", "args" => [1],
862-
"queue" => "queue:other_queue")
872+
"queue" => "other_queue")
863873
expect(score.to_f).to be_within(31.0).of(Time.now.to_f + 330.0)
864874
end
865875
end
@@ -879,7 +889,7 @@ def scheduled_redis_item_and_score
879889

880890
item, score = scheduled_redis_item_and_score
881891
expect(JSON.parse(item)).to include("class" => "ThrottledTestJob", "args" => [1],
882-
"queue" => "queue:default")
892+
"queue" => "default")
883893
expect(score.to_f).to be_within(31.0).of(Time.now.to_f + 330.0)
884894
end
885895
end
@@ -899,7 +909,7 @@ def scheduled_redis_item_and_score
899909

900910
item, score = scheduled_redis_item_and_score
901911
expect(JSON.parse(item)).to include("class" => "ThrottledTestJob", "args" => [1],
902-
"queue" => "queue:default")
912+
"queue" => "default")
903913
expect(score.to_f).to be_within(51.0).of(Time.now.to_f + 550.0)
904914
end
905915
end
@@ -998,7 +1008,17 @@ def scheduled_redis_item_and_score
9981008
it "returns false and does not reschedule the job" do
9991009
expect(Sidekiq::Client).not_to receive(:enqueue_to_in)
10001010
expect(work).not_to receive(:acknowledge)
1001-
expect(subject.send(:reschedule_throttled, work, requeue_to: "queue:default")).to be_falsey
1011+
expect(subject.send(:reschedule_throttled, work, "queue:default")).to be_falsey
1012+
end
1013+
end
1014+
1015+
context "when target_queue has the 'queue:' prefix" do
1016+
let(:target_queue) { "queue:default" }
1017+
1018+
it "reschedules the job to the specified queue" do
1019+
expect(Sidekiq::Client).to receive(:enqueue_to_in).with("default", anything, anything, anything)
1020+
expect(work).to receive(:acknowledge)
1021+
subject.send(:reschedule_throttled, work, target_queue)
10021022
end
10031023
end
10041024
end

0 commit comments

Comments
 (0)