Skip to content

Commit 8048de5

Browse files
Add support for lock_ttl to be a Proc/class method (#879)
* Add support for lock_ttl to be a Proc/class method Add the ability to set the lock_ttl to a callable object, such as a Proc or class method. Gives the ability for callers to set the lock_ttl based on the job arguments or other runtime information. * fixing lint and code complexity
1 parent ad89d50 commit 8048de5

File tree

2 files changed

+97
-7
lines changed

2 files changed

+97
-7
lines changed

lib/sidekiq_unique_jobs/lock_ttl.rb

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,34 @@ def scheduled_at
6666
# @return [Integer] the number of seconds to live
6767
#
6868
def calculate
69-
ttl = item[LOCK_TTL]
70-
ttl ||= job_options[LOCK_TTL]
71-
ttl ||= item[LOCK_EXPIRATION] # TODO: Deprecate at some point
72-
ttl ||= job_options[LOCK_EXPIRATION] # TODO: Deprecate at some point
73-
ttl ||= SidekiqUniqueJobs.config.lock_ttl
74-
ttl && (ttl.to_i + time_until_scheduled)
69+
ttl = fetch_ttl
70+
return unless ttl
71+
72+
timing = calculate_timing(ttl)
73+
return unless timing
74+
75+
timing.to_i + time_until_scheduled
76+
end
77+
78+
private
79+
80+
def fetch_ttl
81+
item[LOCK_TTL] ||
82+
job_options[LOCK_TTL] ||
83+
item[LOCK_EXPIRATION] || # TODO: Deprecate at some point
84+
job_options[LOCK_EXPIRATION] || # TODO: Deprecate at some point
85+
SidekiqUniqueJobs.config.lock_ttl
86+
end
87+
88+
def calculate_timing(ttl)
89+
case ttl
90+
when String, Numeric
91+
ttl
92+
when Proc
93+
ttl.call(item[ARGS])
94+
when Symbol
95+
job_class.send(ttl, item[ARGS])
96+
end
7597
end
7698
end
7799
end

spec/sidekiq_unique_jobs/lock_ttl_spec.rb

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
# frozen_string_literal: true
22

33
RSpec.describe SidekiqUniqueJobs::LockTTL do
4-
let(:calculator) { described_class.new("class" => job_class_name, "at" => schedule_time) }
4+
let(:item) { { "class" => job_class_name, "at" => schedule_time } }
5+
let(:calculator) { described_class.new(item) }
56
let(:job_class_name) { "MyUniqueJob" }
67
let(:schedule_time) { nil }
8+
let(:job_options) do
9+
{ lock: :until_executed, lock_ttl: 7_200, queue: :customqueue, retry: 10 }
10+
end
11+
12+
before do
13+
allow(MyUniqueJob).to receive(:get_sidekiq_options).and_return(job_options)
14+
end
715

816
describe "public api" do
917
subject { calculator }
@@ -34,6 +42,66 @@
3442
end
3543
end
3644

45+
describe "#calculate" do
46+
subject(:calculate) { calculator.calculate }
47+
48+
context "when no lock_ttl is set" do
49+
let(:item) { { "class" => job_class_name, "lock_ttl" => nil } }
50+
let(:job_options) { { lock: "until_expired", "lock_ttl" => nil } }
51+
52+
it "returns the default lock_ttl" do
53+
expect(calculate).to eq(SidekiqUniqueJobs.config.lock_ttl)
54+
end
55+
56+
it "returns nil" do
57+
SidekiqUniqueJobs.config.lock_ttl = nil
58+
expect(calculate).to be_nil
59+
end
60+
end
61+
62+
context "when item lock_ttl is numeric" do
63+
let(:item) { { "class" => job_class_name, "lock_ttl" => 10 } }
64+
65+
it do
66+
expect(calculate).to eq(10)
67+
end
68+
end
69+
70+
context "when item lock_ttl is a string" do
71+
let(:item) { { "class" => job_class_name, "lock_ttl" => "10" } }
72+
73+
it do
74+
expect(calculate).to eq(10)
75+
end
76+
end
77+
78+
context "when item lock_ttl is a proc" do
79+
let(:item) { { "class" => job_class_name, "lock_ttl" => ->(_args) { 20 } } }
80+
81+
it do
82+
expect(calculate).to eq(20)
83+
end
84+
end
85+
86+
context "when item lock_ttl is a function symbol" do
87+
let(:job_class_name) { "MyOtherUniqueJob" }
88+
let(:item) { { "class" => job_class_name, "lock_ttl" => :ttl_fn } }
89+
90+
it do
91+
stub_const(
92+
job_class_name,
93+
Class.new do
94+
def self.ttl_fn(_args)
95+
99
96+
end
97+
end,
98+
)
99+
100+
expect(calculate).to eq(99)
101+
end
102+
end
103+
end
104+
37105
describe "#job_class" do
38106
subject(:job_class) { calculator.job_class }
39107

0 commit comments

Comments
 (0)