backburner-allq 1.0.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.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.travis.yml +29 -0
  4. data/CHANGELOG.md +133 -0
  5. data/CONTRIBUTING.md +37 -0
  6. data/Gemfile +4 -0
  7. data/HOOKS.md +99 -0
  8. data/LICENSE +22 -0
  9. data/README.md +658 -0
  10. data/Rakefile +17 -0
  11. data/TODO +4 -0
  12. data/backburner-allq.gemspec +26 -0
  13. data/bin/backburner +7 -0
  14. data/circle.yml +3 -0
  15. data/deploy.sh +3 -0
  16. data/examples/custom.rb +25 -0
  17. data/examples/demo.rb +60 -0
  18. data/examples/god.rb +46 -0
  19. data/examples/hooked.rb +87 -0
  20. data/examples/retried.rb +31 -0
  21. data/examples/simple.rb +43 -0
  22. data/examples/stress.rb +31 -0
  23. data/lib/backburner.rb +75 -0
  24. data/lib/backburner/allq_wrapper.rb +317 -0
  25. data/lib/backburner/async_proxy.rb +25 -0
  26. data/lib/backburner/cli.rb +53 -0
  27. data/lib/backburner/configuration.rb +48 -0
  28. data/lib/backburner/connection.rb +157 -0
  29. data/lib/backburner/helpers.rb +193 -0
  30. data/lib/backburner/hooks.rb +53 -0
  31. data/lib/backburner/job.rb +118 -0
  32. data/lib/backburner/logger.rb +53 -0
  33. data/lib/backburner/performable.rb +95 -0
  34. data/lib/backburner/queue.rb +145 -0
  35. data/lib/backburner/tasks.rb +54 -0
  36. data/lib/backburner/version.rb +3 -0
  37. data/lib/backburner/worker.rb +221 -0
  38. data/lib/backburner/workers/forking.rb +52 -0
  39. data/lib/backburner/workers/simple.rb +29 -0
  40. data/lib/backburner/workers/threading.rb +163 -0
  41. data/lib/backburner/workers/threads_on_fork.rb +263 -0
  42. data/test/async_proxy_test.rb +36 -0
  43. data/test/back_burner_test.rb +88 -0
  44. data/test/connection_test.rb +179 -0
  45. data/test/fixtures/hooked.rb +122 -0
  46. data/test/fixtures/test_fork_jobs.rb +72 -0
  47. data/test/fixtures/test_forking_jobs.rb +56 -0
  48. data/test/fixtures/test_jobs.rb +87 -0
  49. data/test/fixtures/test_queue_settings.rb +14 -0
  50. data/test/helpers/templogger.rb +22 -0
  51. data/test/helpers_test.rb +278 -0
  52. data/test/hooks_test.rb +112 -0
  53. data/test/job_test.rb +185 -0
  54. data/test/logger_test.rb +44 -0
  55. data/test/performable_test.rb +88 -0
  56. data/test/queue_test.rb +69 -0
  57. data/test/test_helper.rb +128 -0
  58. data/test/worker_test.rb +157 -0
  59. data/test/workers/forking_worker_test.rb +181 -0
  60. data/test/workers/simple_worker_test.rb +350 -0
  61. data/test/workers/threading_worker_test.rb +104 -0
  62. data/test/workers/threads_on_fork_worker_test.rb +484 -0
  63. metadata +217 -0
@@ -0,0 +1,112 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+ require File.expand_path('../fixtures/hooked', __FILE__)
3
+
4
+ describe "Backburner::Hooks module" do
5
+ before do
6
+ $hooked_fail_count = 0
7
+ @hooks = Backburner::Hooks
8
+ end
9
+
10
+ describe "for invoke_hook_events method" do
11
+ describe "with before_enqueue" do
12
+ it "should support successful invocation" do
13
+ out = silenced { @res = @hooks.invoke_hook_events(HookedObjectSuccess, :before_enqueue, 5, 6) }
14
+ assert_equal [nil, nil], @res
15
+ assert_match(/!!before_enqueue_foo!! \[5\, 6\]/, out)
16
+ assert_match(/!!before_enqueue_bar!! \[5\, 6\]/, out)
17
+ end
18
+
19
+ it "should support fail case" do
20
+ out = silenced { @res = @hooks.invoke_hook_events(HookedObjectBeforeEnqueueFail, :before_enqueue, 5, 6) }
21
+ assert_equal false, @res
22
+ assert_match(/!!before_enqueue_foo!! \[5\, 6\]/, out)
23
+ end
24
+ end # before_enqueue
25
+
26
+ describe "with after_enqueue" do
27
+ it "should support successful invocation" do
28
+ out = silenced { @hooks.invoke_hook_events(HookedObjectSuccess, :after_enqueue, 7, 8) }
29
+ assert_match(/!!after_enqueue_foo!! \[7\, 8\]/, out)
30
+ assert_match(/!!after_enqueue_bar!! \[7\, 8\]/, out)
31
+ end
32
+
33
+ it "should support fail case" do
34
+ assert_raises(HookFailError) do
35
+ silenced { @res = @hooks.invoke_hook_events(HookedObjectAfterEnqueueFail, :after_enqueue, 5, 6) }
36
+ end
37
+ end
38
+ end # after_enqueue
39
+
40
+ describe "with before_perform" do
41
+ it "should support successful invocation" do
42
+ out = silenced { @hooks.invoke_hook_events(HookedObjectSuccess, :before_perform, 1, 2) }
43
+ assert_match(/!!before_perform_foo!! \[1\, 2\]/, out)
44
+ end
45
+
46
+ it "should support fail case" do
47
+ out = silenced { @res = @hooks.invoke_hook_events(HookedObjectBeforePerformFail, :before_perform, 5, 6) }
48
+ assert_equal false, @res
49
+ assert_match(/!!before_perform_foo!! \[5\, 6\]/, out)
50
+ end
51
+ end # before_perform
52
+
53
+ describe "with after_perform" do
54
+ it "should support successful invocation" do
55
+ out = silenced { @hooks.invoke_hook_events(HookedObjectSuccess, :after_perform, 3, 4) }
56
+ assert_match(/!!after_perform_foo!! \[3\, 4\]/, out)
57
+ end
58
+
59
+ it "should support fail case" do
60
+ assert_raises(HookFailError) do
61
+ silenced { @res = @hooks.invoke_hook_events(HookedObjectAfterPerformFail, :after_perform, 5, 6) }
62
+ end
63
+ end
64
+ end # after_perform
65
+
66
+ describe "with on_failure" do
67
+ it "should support successful invocation" do
68
+ out = silenced { @hooks.invoke_hook_events(HookedObjectSuccess, :on_failure, RuntimeError, 10) }
69
+ assert_match(/!!on_failure_foo!! RuntimeError \[10\]/, out)
70
+ end
71
+ end # on_failure
72
+
73
+ describe 'with on_retry' do
74
+ it "should support successful invocation" do
75
+ out = silenced { @hooks.invoke_hook_events(HookedObjectSuccess, :on_retry, 1, 0, 10) }
76
+ assert_match(/!!on_retry_foo!! 1 0 \[10\]/, out)
77
+ end
78
+ end
79
+
80
+ describe 'with on_bury' do
81
+ it "should support successful invocation" do
82
+ out = silenced { @hooks.invoke_hook_events(HookedObjectSuccess, :on_bury, 10) }
83
+ assert_match(/!!on_bury_foo!! \[10\]/, out)
84
+ end
85
+ end
86
+
87
+ describe "with on_reconnect" do
88
+ it "should support successful invocation" do
89
+ out = silenced { @hooks.invoke_hook_events(HookedWorker.new, :on_reconnect)}
90
+ assert_match(/!!on_reconnect!!/, out)
91
+ end
92
+ end
93
+ end # invoke_hook_events
94
+
95
+ describe "for around_hook_events method" do
96
+ describe "with around_perform" do
97
+ it "should support successful invocation" do
98
+ out = silenced do
99
+ @hooks.around_hook_events(HookedObjectSuccess, :around_perform, 7, 8) {
100
+ puts "!!FIRED!!"
101
+ }
102
+ end
103
+ assert_match(/BEGIN.*?bar.*BEGIN.*cat.*FIRED.*END.*cat.*END.*bar/m, out)
104
+ assert_match(/!!BEGIN around_perform_bar!! \[7\, 8\]/, out)
105
+ assert_match(/!!BEGIN around_perform_cat!! \[7\, 8\]/, out)
106
+ assert_match(/!!FIRED!!/, out)
107
+ assert_match(/!!END around_perform_cat!! \[7\, 8\]/, out)
108
+ assert_match(/!!END around_perform_bar!! \[7\, 8\]/, out)
109
+ end
110
+ end # successful
111
+ end # around_hook_events
112
+ end # Hooks
@@ -0,0 +1,185 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+
3
+ module NestedDemo
4
+ class TestJobC
5
+ def self.perform(x); puts "Performed #{x} in #{self}"; end
6
+ end
7
+
8
+ class TestJobD
9
+ include Backburner::Queue
10
+ def self.perform(x); raise RuntimeError; end
11
+ end
12
+ end
13
+
14
+ describe "Backburner::Job module" do
15
+ describe "for initialize" do
16
+ describe "with hash" do
17
+ before do
18
+ @task = stub(:body => task_body, :ttr => 120, :delete => true, :bury => true)
19
+ end
20
+
21
+ describe "with string keys" do
22
+ let(:task_body) { { "class" => "NewsletterSender", "args" => ["foo@bar.com", "bar@foo.com"] } }
23
+ it "should create job with correct task data" do
24
+ @job = Backburner::Job.new(@task)
25
+ assert_equal @task, @job.task
26
+ assert_equal ["class", "args"], @job.body.keys
27
+ assert_equal task_body["class"], @job.name
28
+ assert_equal task_body["args"], @job.args
29
+ end
30
+ end
31
+
32
+ describe "with symbol keys" do
33
+ let(:task_body) { { :class => "NewsletterSender", :args => ["foo@bar.com", "bar@foo.com"] } }
34
+ it "should create job with correct task data" do
35
+ @job = Backburner::Job.new(@task)
36
+ assert_equal @task, @job.task
37
+ assert_equal [:class, :args], @job.body.keys
38
+ assert_equal task_body[:class], @job.name
39
+ assert_equal task_body[:args], @job.args
40
+ end
41
+ end
42
+ end # with hash
43
+
44
+ describe "with json string" do
45
+ before do
46
+ @task_body = { "class" => "NewsletterSender", "args" => ["foo@bar.com", "bar@foo.com"] }
47
+ @task = stub(:body => @task_body.to_json, :ttr => 120, :delete => true, :bury => true)
48
+ end
49
+
50
+ it "should create job with correct task data" do
51
+ @job = Backburner::Job.new(@task)
52
+ assert_equal @task, @job.task
53
+ assert_equal ["class", "args"], @job.body.keys
54
+ assert_equal @task_body["class"], @job.name
55
+ assert_equal @task_body["args"], @job.args
56
+ end
57
+ end # with json
58
+
59
+ describe "with invalid string" do
60
+ before do
61
+ @task_body = "^%$*&^*"
62
+ @task = stub(:body => @task_body, :ttr => 120, :delete => true, :bury => true)
63
+ end
64
+
65
+ it "should raise a job format exception" do
66
+ assert_raises(Backburner::Job::JobFormatInvalid) {
67
+ @job = Backburner::Job.new(@task)
68
+ }
69
+ end
70
+ end # invalid
71
+ end # initialize
72
+
73
+ describe "for process method" do
74
+ describe "with valid task" do
75
+ before do
76
+ @task_body = { "class" => "NestedDemo::TestJobC", "args" => [56] }
77
+ @task = stub(:body => @task_body, :ttr => 120, :delete => true, :bury => true)
78
+ @task.expects(:delete).once
79
+ end
80
+
81
+ it "should process task" do
82
+ @job = Backburner::Job.new(@task)
83
+ out = silenced(1) { @job.process }
84
+ assert_match(/Performed 56 in NestedDemo::TestJobC/, out)
85
+ end # process
86
+ end # valid
87
+
88
+ describe "with invalid task" do
89
+ before do
90
+ @task_body = { "class" => "NestedDemo::TestJobD", "args" => [56] }
91
+ @task = stub(:body => @task_body, :ttr => 120, :delete => true, :bury => true)
92
+ @task.expects(:delete).never
93
+ end
94
+
95
+ it "should raise an exception" do
96
+ @job = Backburner::Job.new(@task)
97
+ assert_raises(RuntimeError) { @job.process }
98
+ end # error invalid
99
+ end # invalid
100
+
101
+ describe "with invalid class" do
102
+ before do
103
+ @task_body = { "class" => "NestedDemo::TestJobY", "args" => [56] }
104
+ @task = stub(:body => @task_body, :ttr => 120, :delete => true, :bury => true)
105
+ @task.expects(:delete).never
106
+ end
107
+
108
+ it "should raise an exception" do
109
+ @job = Backburner::Job.new(@task)
110
+ assert_raises(Backburner::Job::JobNotFound) { @job.process }
111
+ end # error class
112
+ end # invalid
113
+ end # process
114
+
115
+ describe "for simple delegation method" do
116
+ describe "with valid class" do
117
+ before do
118
+ @task_body = { "class" => "NestedDemo::TestJobC", "args" => [56] }
119
+ @task = stub(:body => @task_body, :ttr => 120, :delete => true, :bury => true)
120
+ @task.expects(:bury).once
121
+ end
122
+
123
+ it "should call bury for task" do
124
+ @job = Backburner::Job.new(@task)
125
+ @job.bury
126
+ end # bury
127
+ end
128
+
129
+ describe "with invalid class" do
130
+ before do
131
+ @task_body = { "class" => "AnUnknownClass", "args" => [] }
132
+ @task = stub(:body => @task_body, :ttr => 120, :delete => true, :bury => true, :release => true)
133
+ end
134
+
135
+ it "should call bury for task" do
136
+ @task.expects(:bury).once
137
+ @job = Backburner::Job.new(@task)
138
+ Backburner::Hooks.expects(:invoke_hook_events)
139
+ .with("AnUnknownClass", :on_bury, anything)
140
+ @job.bury
141
+ end
142
+
143
+ it "should call retry for task" do
144
+ @task.expects(:release).once
145
+ @job = Backburner::Job.new(@task)
146
+ Backburner::Hooks.expects(:invoke_hook_events)
147
+ .with("AnUnknownClass", :on_retry, 0, is_a(Integer), anything)
148
+ @job.retry(0, 0)
149
+ end
150
+ end
151
+ end # simple delegation
152
+
153
+ describe "timing out for various values of ttr" do
154
+ before do
155
+ @task_body = { "class" => "NestedDemo::TestJobC", "args" => [56] }
156
+ end
157
+
158
+ describe "when ttr == 0" do
159
+ it "should use 0 for the timeout" do
160
+ @task = stub(:body => @task_body, :delete => true, :ttr => 0)
161
+ @job = Backburner::Job.new(@task)
162
+ Timeout.expects(:timeout).with(0)
163
+ @job.process
164
+ end
165
+ end
166
+
167
+ describe "when ttr == 1" do
168
+ it "should use 1 for the timeout" do
169
+ @task = stub(:body => @task_body, :delete => true, :ttr => 1)
170
+ @job = Backburner::Job.new(@task)
171
+ Timeout.expects(:timeout).with(1)
172
+ @job.process
173
+ end
174
+ end
175
+
176
+ describe "when ttr > 1" do
177
+ it "should use ttr-1 for the timeout" do
178
+ @task = stub(:body => @task_body, :delete => true, :ttr => 2)
179
+ @job = Backburner::Job.new(@task)
180
+ Timeout.expects(:timeout).with(1)
181
+ @job.process
182
+ end
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,44 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+
3
+ describe "Backburner::Logger module" do
4
+ include Backburner::Logger
5
+
6
+ before do
7
+ @strio = StringIO.new
8
+ @logger = Logger.new(@strio)
9
+ end
10
+
11
+ describe "for log_info method" do
12
+ it "prints out to std out" do
13
+ output = capture_stdout { log_info("foo") }
14
+ assert_equal "foo\n", output
15
+ end
16
+
17
+ it "can be configured to log to logger" do
18
+ Backburner.configure { |config| config.logger = @logger }
19
+ log_info("foo")
20
+ assert_match(/I,.*?foo/, @strio.string)
21
+ end
22
+
23
+ after do
24
+ Backburner.configure { |config| config.logger = nil }
25
+ end
26
+ end # log_info
27
+
28
+ describe "for log_error method" do
29
+ it "prints out to std err" do
30
+ output = capture_stdout { log_error("bar") }
31
+ assert_equal "bar\n", output
32
+ end
33
+
34
+ it "can be configured to log to logger" do
35
+ Backburner.configure { |config| config.logger = @logger }
36
+ log_error("bar")
37
+ assert_match(/E,.*?bar/, @strio.string)
38
+ end
39
+
40
+ after do
41
+ Backburner.configure { |config| config.logger = nil }
42
+ end
43
+ end # log_error
44
+ end
@@ -0,0 +1,88 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+
3
+ class TestObj
4
+ ID = 56
5
+ def id; ID; end
6
+ def self.find(id); TestObj.new if id == ID; end
7
+ def foo(state, state2); "bar #{state} #{state2}"; end
8
+ def self.bar(state, state2); "baz #{state} #{state2}"; end
9
+ end
10
+
11
+ class PerformableTestObj < TestObj
12
+ include Backburner::Performable
13
+ end
14
+
15
+ class AutomagicTestObj < TestObj
16
+ # Don't include Backburner::Performable because it should be automagically included
17
+ def qux(state, state2); "garply #{state} #{state2}" end
18
+ def self.garply(state, state2); "thud #{state} #{state2}" end
19
+ def qux?; "garply!" end
20
+ end
21
+
22
+ class AsyncInstanceMethodsTestObj < PerformableTestObj; end
23
+ class AsyncStaticMethodsTestObj < PerformableTestObj; end
24
+
25
+
26
+
27
+ describe "Backburner::Performable module" do
28
+ after { ENV["TEST"] = nil }
29
+
30
+ describe "for async instance method" do
31
+ it "should invoke worker enqueue" do
32
+ Backburner::Worker.expects(:enqueue).with(PerformableTestObj, [56, :foo, true, false], has_entries(:pri => 5000, :queue => "foo"))
33
+ PerformableTestObj.new.async(:pri => 5000, :queue => "foo").foo(true, false)
34
+ end
35
+ end # async instance
36
+
37
+ describe "for async class method" do
38
+ it "should invoke worker enqueue" do
39
+ Backburner::Worker.expects(:enqueue).with(PerformableTestObj, [nil, :bar, true, false], has_entries(:pri => 5000, :queue => "foo"))
40
+ PerformableTestObj.async(:pri => 5000, :queue => "foo").bar(true, false)
41
+ end
42
+ end # async class
43
+
44
+ describe "for perform class method" do
45
+ it "should work for instance" do
46
+ assert_equal "bar true false", PerformableTestObj.perform(PerformableTestObj::ID, :foo, true, false)
47
+ end # instance
48
+
49
+ it "should work for class level" do
50
+ assert_equal "baz false true", PerformableTestObj.perform(nil, :bar, false, true)
51
+ end # class
52
+ end # perform
53
+
54
+ describe "for handle_asynchronously class method" do
55
+ it "should automagically asynchronously proxy calls to the method" do
56
+ Backburner::Performable.handle_asynchronously(AutomagicTestObj, :qux, :pri => 5000, :queue => "qux")
57
+
58
+ Backburner::Worker.expects(:enqueue).with(AutomagicTestObj, [56, :qux_without_async, true, false], has_entries(:pri => 5000, :queue => "qux"))
59
+ AutomagicTestObj.new.qux(true, false)
60
+ end
61
+
62
+ it "should work for class methods, too" do
63
+ Backburner::Performable.handle_static_asynchronously(AutomagicTestObj, :garply, :pri => 5000, :queue => "garply")
64
+
65
+ Backburner::Worker.expects(:enqueue).with(AutomagicTestObj, [nil, :garply_without_async, true, false], has_entries(:pri => 5000, :queue => "garply"))
66
+ AutomagicTestObj.garply(true, false)
67
+ end
68
+
69
+ it "should correctly handle punctuation" do
70
+ Backburner::Performable.handle_asynchronously(AutomagicTestObj, :qux?)
71
+
72
+ Backburner::Worker.expects(:enqueue).with(AutomagicTestObj, [56, :qux_without_async?], {})
73
+ AutomagicTestObj.new.qux?
74
+ end
75
+
76
+ it "should be available for instance methods on any class that includes the Performable module" do
77
+ AsyncInstanceMethodsTestObj.handle_asynchronously :foo, pri: 5000, queue: 'qux'
78
+ Backburner::Worker.expects(:enqueue).with(AsyncInstanceMethodsTestObj, [56, :foo_without_async, true, false], has_entries(:pri => 5000, :queue => "qux"))
79
+ AsyncInstanceMethodsTestObj.new.foo(true, false)
80
+ end
81
+
82
+ it "should be available for class methods on any class that includes the Performable module" do
83
+ AsyncStaticMethodsTestObj.handle_static_asynchronously :bar, pri: 5000, queue: 'garply'
84
+ Backburner::Worker.expects(:enqueue).with(AsyncStaticMethodsTestObj, [nil, :bar_without_async, true, false], has_entries(:pri => 5000, :queue => "garply"))
85
+ AsyncStaticMethodsTestObj.bar(true, false)
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,69 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+
3
+ module NestedDemo
4
+ class TestJobA; include Backburner::Queue; end
5
+ class TestJobB; include Backburner::Queue; end
6
+ end
7
+
8
+ describe "Backburner::Queue module" do
9
+ describe "contains known_queue_classes" do
10
+ it "has all defined known queues" do
11
+ assert_contains Backburner::Worker.known_queue_classes, NestedDemo::TestJobA
12
+ assert_contains Backburner::Worker.known_queue_classes, NestedDemo::TestJobB
13
+ end
14
+ end
15
+
16
+ describe "for queue method accessor" do
17
+ it "should return the queue name" do
18
+ assert_equal Backburner.configuration.primary_queue, NestedDemo::TestJobA.queue
19
+ end
20
+ end # queue_name
21
+
22
+ describe "for queue assignment method" do
23
+ it "should allow queue name to be assigned" do
24
+ NestedDemo::TestJobB.queue("nested/job")
25
+ assert_equal "nested/job", NestedDemo::TestJobB.queue
26
+ end
27
+
28
+ it "should allow lambdas" do
29
+ NestedDemo::TestJobB.queue(lambda { |klass| klass.name })
30
+ assert_equal "NestedDemo::TestJobB", NestedDemo::TestJobB.queue
31
+ end
32
+ end # queue
33
+
34
+ describe "for queue_priority assignment method" do
35
+ it "should allow queue priority to be assigned" do
36
+ NestedDemo::TestJobB.queue_priority(1000)
37
+ assert_equal 1000, NestedDemo::TestJobB.queue_priority
38
+ end
39
+ end # queue_priority
40
+
41
+ describe "for queue_respond_timeout assignment method" do
42
+ it "should allow queue respond_timeout to be assigned" do
43
+ NestedDemo::TestJobB.queue_respond_timeout(300)
44
+ assert_equal 300, NestedDemo::TestJobB.queue_respond_timeout
45
+ end
46
+ end # queue_respond_timeout
47
+
48
+ describe "for queue_max_job_retries assignment method" do
49
+ it "should allow queue max_job_retries to be assigned" do
50
+ NestedDemo::TestJobB.queue_max_job_retries(5)
51
+ assert_equal 5, NestedDemo::TestJobB.queue_max_job_retries
52
+ end
53
+ end # queue_max_job_retries
54
+
55
+ describe "for queue_retry_delay assignment method" do
56
+ it "should allow queue retry_delay to be assigned" do
57
+ NestedDemo::TestJobB.queue_retry_delay(300)
58
+ assert_equal 300, NestedDemo::TestJobB.queue_retry_delay
59
+ end
60
+ end # queue_retry_delay
61
+
62
+ describe "for queue_retry_delay_proc assignment method" do
63
+ it "should allow queue retry_delay_proc to be assigned" do
64
+ retry_delay_proc = lambda { |x, y| x - y }
65
+ NestedDemo::TestJobB.queue_retry_delay_proc(retry_delay_proc)
66
+ assert_equal retry_delay_proc.call(2, 1), NestedDemo::TestJobB.queue_retry_delay_proc.call(2, 1)
67
+ end
68
+ end # queue_retry_delay_proc
69
+ end # Backburner::Queue