que 0.11.3 → 2.2.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.
- checksums.yaml +5 -5
- data/.github/workflows/tests.yml +51 -0
- data/.gitignore +2 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +502 -97
- data/Dockerfile +20 -0
- data/LICENSE.txt +1 -1
- data/README.md +205 -59
- data/auto/dev +21 -0
- data/auto/pre-push-hook +30 -0
- data/auto/psql +9 -0
- data/auto/test +5 -0
- data/auto/test-postgres-14 +17 -0
- data/bin/que +8 -81
- data/docker-compose.yml +47 -0
- data/docs/README.md +881 -0
- data/lib/que/active_job/extensions.rb +114 -0
- data/lib/que/active_record/connection.rb +51 -0
- data/lib/que/active_record/model.rb +48 -0
- data/lib/que/command_line_interface.rb +259 -0
- data/lib/que/connection.rb +198 -0
- data/lib/que/connection_pool.rb +78 -0
- data/lib/que/job.rb +210 -103
- data/lib/que/job_buffer.rb +255 -0
- data/lib/que/job_methods.rb +176 -0
- data/lib/que/listener.rb +176 -0
- data/lib/que/locker.rb +507 -0
- data/lib/que/metajob.rb +47 -0
- data/lib/que/migrations/4/down.sql +48 -0
- data/lib/que/migrations/4/up.sql +267 -0
- data/lib/que/migrations/5/down.sql +73 -0
- data/lib/que/migrations/5/up.sql +76 -0
- data/lib/que/migrations/6/down.sql +8 -0
- data/lib/que/migrations/6/up.sql +8 -0
- data/lib/que/migrations/7/down.sql +5 -0
- data/lib/que/migrations/7/up.sql +13 -0
- data/lib/que/migrations.rb +37 -18
- data/lib/que/poller.rb +274 -0
- data/lib/que/rails/railtie.rb +12 -0
- data/lib/que/result_queue.rb +35 -0
- data/lib/que/sequel/model.rb +52 -0
- data/lib/que/utils/assertions.rb +62 -0
- data/lib/que/utils/constantization.rb +19 -0
- data/lib/que/utils/error_notification.rb +68 -0
- data/lib/que/utils/freeze.rb +20 -0
- data/lib/que/utils/introspection.rb +50 -0
- data/lib/que/utils/json_serialization.rb +21 -0
- data/lib/que/utils/logging.rb +79 -0
- data/lib/que/utils/middleware.rb +46 -0
- data/lib/que/utils/queue_management.rb +18 -0
- data/lib/que/utils/ruby2_keywords.rb +19 -0
- data/lib/que/utils/transactions.rb +34 -0
- data/lib/que/version.rb +5 -1
- data/lib/que/worker.rb +145 -149
- data/lib/que.rb +103 -159
- data/que.gemspec +17 -4
- data/scripts/docker-entrypoint +14 -0
- data/scripts/test +6 -0
- metadata +59 -95
- data/.rspec +0 -2
- data/.travis.yml +0 -17
- data/Gemfile +0 -24
- data/docs/advanced_setup.md +0 -106
- data/docs/customizing_que.md +0 -200
- data/docs/error_handling.md +0 -47
- data/docs/inspecting_the_queue.md +0 -114
- data/docs/logging.md +0 -50
- data/docs/managing_workers.md +0 -80
- data/docs/migrating.md +0 -30
- data/docs/multiple_queues.md +0 -27
- data/docs/shutting_down_safely.md +0 -7
- data/docs/using_plain_connections.md +0 -41
- data/docs/using_sequel.md +0 -31
- data/docs/writing_reliable_jobs.md +0 -117
- data/lib/generators/que/install_generator.rb +0 -24
- data/lib/generators/que/templates/add_que.rb +0 -13
- data/lib/que/adapters/active_record.rb +0 -54
- data/lib/que/adapters/base.rb +0 -127
- data/lib/que/adapters/connection_pool.rb +0 -16
- data/lib/que/adapters/pg.rb +0 -21
- data/lib/que/adapters/pond.rb +0 -16
- data/lib/que/adapters/sequel.rb +0 -20
- data/lib/que/railtie.rb +0 -16
- data/lib/que/rake_tasks.rb +0 -59
- data/lib/que/sql.rb +0 -152
- data/spec/adapters/active_record_spec.rb +0 -152
- data/spec/adapters/connection_pool_spec.rb +0 -22
- data/spec/adapters/pg_spec.rb +0 -41
- data/spec/adapters/pond_spec.rb +0 -22
- data/spec/adapters/sequel_spec.rb +0 -57
- data/spec/gemfiles/Gemfile1 +0 -18
- data/spec/gemfiles/Gemfile2 +0 -18
- data/spec/spec_helper.rb +0 -118
- data/spec/support/helpers.rb +0 -19
- data/spec/support/jobs.rb +0 -35
- data/spec/support/shared_examples/adapter.rb +0 -37
- data/spec/support/shared_examples/multi_threaded_adapter.rb +0 -46
- data/spec/travis.rb +0 -23
- data/spec/unit/connection_spec.rb +0 -14
- data/spec/unit/customization_spec.rb +0 -251
- data/spec/unit/enqueue_spec.rb +0 -245
- data/spec/unit/helper_spec.rb +0 -12
- data/spec/unit/logging_spec.rb +0 -101
- data/spec/unit/migrations_spec.rb +0 -84
- data/spec/unit/pool_spec.rb +0 -365
- data/spec/unit/run_spec.rb +0 -14
- data/spec/unit/states_spec.rb +0 -50
- data/spec/unit/stats_spec.rb +0 -46
- data/spec/unit/transaction_spec.rb +0 -36
- data/spec/unit/work_spec.rb +0 -407
- data/spec/unit/worker_spec.rb +0 -167
- data/tasks/benchmark.rb +0 -3
- data/tasks/rspec.rb +0 -14
- data/tasks/safe_shutdown.rb +0 -67
data/spec/unit/work_spec.rb
DELETED
|
@@ -1,407 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'spec_helper'
|
|
4
|
-
|
|
5
|
-
describe Que::Job, '.work' do
|
|
6
|
-
it "should pass a job's arguments to the run method and delete it from the database" do
|
|
7
|
-
ArgsJob.enqueue 1, 'two', {'three' => 3}
|
|
8
|
-
DB[:que_jobs].count.should be 1
|
|
9
|
-
|
|
10
|
-
result = Que::Job.work
|
|
11
|
-
result[:event].should == :job_worked
|
|
12
|
-
result[:job][:job_class].should == 'ArgsJob'
|
|
13
|
-
|
|
14
|
-
DB[:que_jobs].count.should be 0
|
|
15
|
-
$passed_args.should == [1, 'two', {'three' => 3}]
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
it "should respect a custom json converter when processing the job's arguments" do
|
|
19
|
-
ArgsJob.enqueue 1, 'two', {'three' => 3}
|
|
20
|
-
DB[:que_jobs].count.should be 1
|
|
21
|
-
|
|
22
|
-
begin
|
|
23
|
-
Que.json_converter = Que::SYMBOLIZER
|
|
24
|
-
|
|
25
|
-
result = Que::Job.work
|
|
26
|
-
result[:event].should == :job_worked
|
|
27
|
-
result[:job][:job_class].should == 'ArgsJob'
|
|
28
|
-
|
|
29
|
-
DB[:que_jobs].count.should be 0
|
|
30
|
-
$passed_args.should == [1, 'two', {:three => 3}]
|
|
31
|
-
ensure
|
|
32
|
-
Que.json_converter = Que::INDIFFERENTIATOR
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
it "should default to only working jobs without a named queue" do
|
|
37
|
-
Que::Job.enqueue 1, :queue => 'other_queue'
|
|
38
|
-
Que::Job.enqueue 2
|
|
39
|
-
|
|
40
|
-
result = Que::Job.work
|
|
41
|
-
result[:event].should == :job_worked
|
|
42
|
-
result[:job][:args].should == [2]
|
|
43
|
-
|
|
44
|
-
result = Que::Job.work
|
|
45
|
-
result[:event].should == :job_unavailable
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
it "should accept the name of a single queue to pull jobs from" do
|
|
49
|
-
Que::Job.enqueue 1, :queue => 'other_queue'
|
|
50
|
-
Que::Job.enqueue 2, :queue => 'other_queue'
|
|
51
|
-
Que::Job.enqueue 3
|
|
52
|
-
|
|
53
|
-
result = Que::Job.work(:other_queue)
|
|
54
|
-
result[:event].should == :job_worked
|
|
55
|
-
result[:job][:args].should == [1]
|
|
56
|
-
|
|
57
|
-
result = Que::Job.work('other_queue')
|
|
58
|
-
result[:event].should == :job_worked
|
|
59
|
-
result[:job][:args].should == [2]
|
|
60
|
-
|
|
61
|
-
result = Que::Job.work(:other_queue)
|
|
62
|
-
result[:event].should == :job_unavailable
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
it "should make a job's argument hashes indifferently accessible" do
|
|
66
|
-
DB[:que_jobs].count.should be 0
|
|
67
|
-
ArgsJob.enqueue 1, 'two', {'array' => [{'number' => 3}]}
|
|
68
|
-
DB[:que_jobs].count.should be 1
|
|
69
|
-
|
|
70
|
-
result = Que::Job.work
|
|
71
|
-
result[:event].should == :job_worked
|
|
72
|
-
result[:job][:job_class].should == 'ArgsJob'
|
|
73
|
-
|
|
74
|
-
DB[:que_jobs].count.should be 0
|
|
75
|
-
|
|
76
|
-
$passed_args.last[:array].first[:number].should == 3
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
it "should not fail if there are no jobs to work" do
|
|
80
|
-
Que::Job.work[:event].should be :job_unavailable
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
it "should prefer a job with a higher priority" do
|
|
84
|
-
# 1 is highest priority.
|
|
85
|
-
[5, 4, 3, 2, 1, 2, 3, 4, 5].map{|p| Que::Job.enqueue :priority => p}
|
|
86
|
-
DB[:que_jobs].order(:job_id).select_map(:priority).should == [5, 4, 3, 2, 1, 2, 3, 4, 5]
|
|
87
|
-
|
|
88
|
-
result = Que::Job.work
|
|
89
|
-
result[:event].should == :job_worked
|
|
90
|
-
result[:job][:job_class].should == 'Que::Job'
|
|
91
|
-
DB[:que_jobs].select_map(:priority).should == [5, 4, 3, 2, 2, 3, 4, 5]
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
it "should prefer a job that was scheduled to run longer ago when priorities are equal" do
|
|
95
|
-
Que::Job.enqueue :run_at => Time.now - 30
|
|
96
|
-
Que::Job.enqueue :run_at => Time.now - 60
|
|
97
|
-
Que::Job.enqueue :run_at => Time.now - 30
|
|
98
|
-
|
|
99
|
-
recent1, old, recent2 = DB[:que_jobs].order(:job_id).select_map(:run_at)
|
|
100
|
-
|
|
101
|
-
result = Que::Job.work
|
|
102
|
-
result[:event].should == :job_worked
|
|
103
|
-
result[:job][:job_class].should == 'Que::Job'
|
|
104
|
-
DB[:que_jobs].order_by(:job_id).select_map(:run_at).should == [recent1, recent2]
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
it "should prefer a job that was queued earlier when priorities and run_ats are equal" do
|
|
108
|
-
run_at = Time.now - 30
|
|
109
|
-
Que::Job.enqueue :run_at => run_at
|
|
110
|
-
Que::Job.enqueue :run_at => run_at
|
|
111
|
-
Que::Job.enqueue :run_at => run_at
|
|
112
|
-
|
|
113
|
-
first, second, third = DB[:que_jobs].select_order_map(:job_id)
|
|
114
|
-
|
|
115
|
-
result = Que::Job.work
|
|
116
|
-
result[:event].should == :job_worked
|
|
117
|
-
result[:job][:job_class].should == 'Que::Job'
|
|
118
|
-
DB[:que_jobs].select_order_map(:job_id).should == [second, third]
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
it "should only work a job whose scheduled time to run has passed" do
|
|
122
|
-
Que::Job.enqueue :run_at => Time.now + 30
|
|
123
|
-
Que::Job.enqueue :run_at => Time.now - 30
|
|
124
|
-
Que::Job.enqueue :run_at => Time.now + 30
|
|
125
|
-
|
|
126
|
-
future1, past, future2 = DB[:que_jobs].order(:job_id).select_map(:run_at)
|
|
127
|
-
|
|
128
|
-
result = Que::Job.work
|
|
129
|
-
result[:event].should == :job_worked
|
|
130
|
-
result[:job][:job_class].should == 'Que::Job'
|
|
131
|
-
Que::Job.work[:event].should be :job_unavailable
|
|
132
|
-
DB[:que_jobs].order_by(:job_id).select_map(:run_at).should == [future1, future2]
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
it "should lock the job it selects" do
|
|
136
|
-
BlockJob.enqueue
|
|
137
|
-
id = DB[:que_jobs].get(:job_id)
|
|
138
|
-
thread = Thread.new { Que::Job.work }
|
|
139
|
-
|
|
140
|
-
$q1.pop
|
|
141
|
-
DB[:pg_locks].where(:locktype => 'advisory').select_map(:objid).should == [id]
|
|
142
|
-
$q2.push nil
|
|
143
|
-
|
|
144
|
-
thread.join
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
it "should skip jobs that are advisory-locked" do
|
|
148
|
-
Que::Job.enqueue :priority => 2
|
|
149
|
-
Que::Job.enqueue :priority => 1
|
|
150
|
-
Que::Job.enqueue :priority => 3
|
|
151
|
-
id = DB[:que_jobs].where(:priority => 1).get(:job_id)
|
|
152
|
-
|
|
153
|
-
begin
|
|
154
|
-
DB.select{pg_advisory_lock(id)}.single_value
|
|
155
|
-
|
|
156
|
-
result = Que::Job.work
|
|
157
|
-
result[:event].should == :job_worked
|
|
158
|
-
result[:job][:job_class].should == 'Que::Job'
|
|
159
|
-
|
|
160
|
-
DB[:que_jobs].order_by(:job_id).select_map(:priority).should == [1, 3]
|
|
161
|
-
ensure
|
|
162
|
-
DB.select{pg_advisory_unlock(id)}.single_value
|
|
163
|
-
end
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
it "should handle subclasses of other jobs" do
|
|
167
|
-
class SubClassJob < Que::Job
|
|
168
|
-
@priority = 2
|
|
169
|
-
|
|
170
|
-
def run
|
|
171
|
-
$job_spec_result << :sub
|
|
172
|
-
end
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
class SubSubClassJob < SubClassJob
|
|
176
|
-
@priority = 4
|
|
177
|
-
|
|
178
|
-
def run
|
|
179
|
-
super
|
|
180
|
-
$job_spec_result << :subsub
|
|
181
|
-
end
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
$job_spec_result = []
|
|
185
|
-
SubClassJob.enqueue
|
|
186
|
-
DB[:que_jobs].select_map(:priority).should == [2]
|
|
187
|
-
result = Que::Job.work
|
|
188
|
-
result[:event].should == :job_worked
|
|
189
|
-
result[:job][:job_class].should == 'SubClassJob'
|
|
190
|
-
$job_spec_result.should == [:sub]
|
|
191
|
-
|
|
192
|
-
$job_spec_result = []
|
|
193
|
-
SubSubClassJob.enqueue
|
|
194
|
-
DB[:que_jobs].select_map(:priority).should == [4]
|
|
195
|
-
result = Que::Job.work
|
|
196
|
-
result[:event].should == :job_worked
|
|
197
|
-
result[:job][:job_class].should == 'SubSubClassJob'
|
|
198
|
-
$job_spec_result.should == [:sub, :subsub]
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
it "should handle namespaced subclasses" do
|
|
202
|
-
module ModuleJobModule
|
|
203
|
-
class ModuleJob < Que::Job
|
|
204
|
-
end
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
ModuleJobModule::ModuleJob.enqueue
|
|
208
|
-
DB[:que_jobs].get(:job_class).should == "ModuleJobModule::ModuleJob"
|
|
209
|
-
|
|
210
|
-
result = Que::Job.work
|
|
211
|
-
result[:event].should == :job_worked
|
|
212
|
-
result[:job][:job_class].should == 'ModuleJobModule::ModuleJob'
|
|
213
|
-
end
|
|
214
|
-
|
|
215
|
-
it "should make it easy to destroy the job within the same transaction as other changes" do
|
|
216
|
-
class DestroyJob < Que::Job
|
|
217
|
-
def run
|
|
218
|
-
destroy
|
|
219
|
-
end
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
DestroyJob.enqueue
|
|
223
|
-
DB[:que_jobs].count.should be 1
|
|
224
|
-
Que::Job.work
|
|
225
|
-
DB[:que_jobs].count.should be 0
|
|
226
|
-
end
|
|
227
|
-
|
|
228
|
-
describe "when encountering an error" do
|
|
229
|
-
it "should exponentially back off the job" do
|
|
230
|
-
ErrorJob.enqueue
|
|
231
|
-
|
|
232
|
-
result = Que::Job.work
|
|
233
|
-
result[:event].should == :job_errored
|
|
234
|
-
result[:error].should be_an_instance_of RuntimeError
|
|
235
|
-
result[:job][:job_class].should == 'ErrorJob'
|
|
236
|
-
|
|
237
|
-
DB[:que_jobs].count.should be 1
|
|
238
|
-
job = DB[:que_jobs].first
|
|
239
|
-
job[:error_count].should be 1
|
|
240
|
-
job[:last_error].should =~ /\AErrorJob!\n/
|
|
241
|
-
job[:run_at].should be_within(3).of Time.now + 4
|
|
242
|
-
|
|
243
|
-
DB[:que_jobs].update :error_count => 5,
|
|
244
|
-
:run_at => Time.now - 60
|
|
245
|
-
|
|
246
|
-
result = Que::Job.work
|
|
247
|
-
result[:event].should == :job_errored
|
|
248
|
-
result[:error].should be_an_instance_of RuntimeError
|
|
249
|
-
result[:job][:job_class].should == 'ErrorJob'
|
|
250
|
-
|
|
251
|
-
DB[:que_jobs].count.should be 1
|
|
252
|
-
job = DB[:que_jobs].first
|
|
253
|
-
job[:error_count].should be 6
|
|
254
|
-
job[:last_error].should =~ /\AErrorJob!\n/
|
|
255
|
-
job[:run_at].should be_within(3).of Time.now + 1299
|
|
256
|
-
end
|
|
257
|
-
|
|
258
|
-
it "should respect a custom retry interval" do
|
|
259
|
-
class RetryIntervalJob < ErrorJob
|
|
260
|
-
@retry_interval = 3155760000000 # 100,000 years from now
|
|
261
|
-
end
|
|
262
|
-
|
|
263
|
-
RetryIntervalJob.enqueue
|
|
264
|
-
|
|
265
|
-
result = Que::Job.work
|
|
266
|
-
result[:event].should == :job_errored
|
|
267
|
-
result[:error].should be_an_instance_of RuntimeError
|
|
268
|
-
result[:job][:job_class].should == 'RetryIntervalJob'
|
|
269
|
-
|
|
270
|
-
DB[:que_jobs].count.should be 1
|
|
271
|
-
job = DB[:que_jobs].first
|
|
272
|
-
job[:error_count].should be 1
|
|
273
|
-
job[:last_error].should =~ /\AErrorJob!\n/
|
|
274
|
-
job[:run_at].to_f.should be_within(3).of Time.now.to_f + RetryIntervalJob.retry_interval
|
|
275
|
-
|
|
276
|
-
DB[:que_jobs].update :error_count => 5,
|
|
277
|
-
:run_at => Time.now - 60
|
|
278
|
-
|
|
279
|
-
result = Que::Job.work
|
|
280
|
-
result[:event].should == :job_errored
|
|
281
|
-
result[:error].should be_an_instance_of RuntimeError
|
|
282
|
-
result[:job][:job_class].should == 'RetryIntervalJob'
|
|
283
|
-
|
|
284
|
-
DB[:que_jobs].count.should be 1
|
|
285
|
-
job = DB[:que_jobs].first
|
|
286
|
-
job[:error_count].should be 6
|
|
287
|
-
job[:last_error].should =~ /\AErrorJob!\n/
|
|
288
|
-
job[:run_at].to_f.should be_within(3).of Time.now.to_f + RetryIntervalJob.retry_interval
|
|
289
|
-
end
|
|
290
|
-
|
|
291
|
-
it "should respect a custom retry interval formula" do
|
|
292
|
-
class RetryIntervalFormulaJob < ErrorJob
|
|
293
|
-
@retry_interval = proc { |count| count * 10 }
|
|
294
|
-
end
|
|
295
|
-
|
|
296
|
-
RetryIntervalFormulaJob.enqueue
|
|
297
|
-
|
|
298
|
-
result = Que::Job.work
|
|
299
|
-
result[:event].should == :job_errored
|
|
300
|
-
result[:error].should be_an_instance_of RuntimeError
|
|
301
|
-
result[:job][:job_class].should == 'RetryIntervalFormulaJob'
|
|
302
|
-
|
|
303
|
-
DB[:que_jobs].count.should be 1
|
|
304
|
-
job = DB[:que_jobs].first
|
|
305
|
-
job[:error_count].should be 1
|
|
306
|
-
job[:last_error].should =~ /\AErrorJob!\n/
|
|
307
|
-
job[:run_at].should be_within(3).of Time.now + 10
|
|
308
|
-
|
|
309
|
-
DB[:que_jobs].update :error_count => 5,
|
|
310
|
-
:run_at => Time.now - 60
|
|
311
|
-
|
|
312
|
-
result = Que::Job.work
|
|
313
|
-
result[:event].should == :job_errored
|
|
314
|
-
result[:error].should be_an_instance_of RuntimeError
|
|
315
|
-
result[:job][:job_class].should == 'RetryIntervalFormulaJob'
|
|
316
|
-
|
|
317
|
-
DB[:que_jobs].count.should be 1
|
|
318
|
-
job = DB[:que_jobs].first
|
|
319
|
-
job[:error_count].should be 6
|
|
320
|
-
job[:last_error].should =~ /\AErrorJob!\n/
|
|
321
|
-
job[:run_at].should be_within(3).of Time.now + 60
|
|
322
|
-
end
|
|
323
|
-
|
|
324
|
-
it "should pass it to an error handler, if one is defined" do
|
|
325
|
-
begin
|
|
326
|
-
errors = []
|
|
327
|
-
Que.error_handler = proc { |error| errors << error }
|
|
328
|
-
|
|
329
|
-
ErrorJob.enqueue
|
|
330
|
-
|
|
331
|
-
result = Que::Job.work
|
|
332
|
-
result[:event].should == :job_errored
|
|
333
|
-
result[:error].should be_an_instance_of RuntimeError
|
|
334
|
-
result[:job][:job_class].should == 'ErrorJob'
|
|
335
|
-
|
|
336
|
-
errors.count.should be 1
|
|
337
|
-
error = errors[0]
|
|
338
|
-
error.should be_an_instance_of RuntimeError
|
|
339
|
-
error.message.should == "ErrorJob!"
|
|
340
|
-
ensure
|
|
341
|
-
Que.error_handler = nil
|
|
342
|
-
end
|
|
343
|
-
end
|
|
344
|
-
|
|
345
|
-
it "should pass job to an error handler, if one is defined" do
|
|
346
|
-
begin
|
|
347
|
-
jobs = []
|
|
348
|
-
Que.error_handler = proc { |error, job| jobs << job }
|
|
349
|
-
|
|
350
|
-
ErrorJob.enqueue
|
|
351
|
-
result = Que::Job.work
|
|
352
|
-
|
|
353
|
-
jobs.count.should be 1
|
|
354
|
-
job = jobs[0]
|
|
355
|
-
job.should be result[:job]
|
|
356
|
-
ensure
|
|
357
|
-
Que.error_handler = nil
|
|
358
|
-
end
|
|
359
|
-
end
|
|
360
|
-
|
|
361
|
-
it "should not do anything if the error handler itelf throws an error" do
|
|
362
|
-
begin
|
|
363
|
-
Que.error_handler = proc { |error| raise "Another error!" }
|
|
364
|
-
ErrorJob.enqueue
|
|
365
|
-
|
|
366
|
-
result = Que::Job.work
|
|
367
|
-
result[:event].should == :job_errored
|
|
368
|
-
result[:error].should be_an_instance_of RuntimeError
|
|
369
|
-
ensure
|
|
370
|
-
Que.error_handler = nil
|
|
371
|
-
end
|
|
372
|
-
end
|
|
373
|
-
|
|
374
|
-
it "should throw an error properly if there's no corresponding job class" do
|
|
375
|
-
DB[:que_jobs].insert :job_class => "NonexistentClass"
|
|
376
|
-
|
|
377
|
-
result = Que::Job.work
|
|
378
|
-
result[:event].should == :job_errored
|
|
379
|
-
result[:error].should be_an_instance_of NameError
|
|
380
|
-
result[:job][:job_class].should == 'NonexistentClass'
|
|
381
|
-
|
|
382
|
-
DB[:que_jobs].count.should be 1
|
|
383
|
-
job = DB[:que_jobs].first
|
|
384
|
-
job[:error_count].should be 1
|
|
385
|
-
job[:last_error].should =~ /uninitialized constant:? NonexistentClass/
|
|
386
|
-
job[:run_at].should be_within(3).of Time.now + 4
|
|
387
|
-
end
|
|
388
|
-
|
|
389
|
-
it "should throw an error properly if the corresponding job class doesn't descend from Que::Job" do
|
|
390
|
-
class J
|
|
391
|
-
def run(*args)
|
|
392
|
-
end
|
|
393
|
-
end
|
|
394
|
-
|
|
395
|
-
Que.enqueue :job_class => "J"
|
|
396
|
-
|
|
397
|
-
result = Que::Job.work
|
|
398
|
-
result[:event].should == :job_errored
|
|
399
|
-
result[:job][:job_class].should == 'J'
|
|
400
|
-
|
|
401
|
-
DB[:que_jobs].count.should be 1
|
|
402
|
-
job = DB[:que_jobs].first
|
|
403
|
-
job[:error_count].should be 1
|
|
404
|
-
job[:run_at].should be_within(3).of Time.now + 4
|
|
405
|
-
end
|
|
406
|
-
end
|
|
407
|
-
end
|
data/spec/unit/worker_spec.rb
DELETED
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'spec_helper'
|
|
4
|
-
|
|
5
|
-
describe Que::Worker do
|
|
6
|
-
it "should work jobs when started until there are none available" do
|
|
7
|
-
begin
|
|
8
|
-
Que::Job.enqueue
|
|
9
|
-
Que::Job.enqueue
|
|
10
|
-
DB[:que_jobs].count.should be 2
|
|
11
|
-
|
|
12
|
-
@worker = Que::Worker.new
|
|
13
|
-
sleep_until { @worker.sleeping? }
|
|
14
|
-
DB[:que_jobs].count.should be 0
|
|
15
|
-
|
|
16
|
-
$logger.messages.map{|m| JSON.load(m)['event']}.should == %w(job_worked job_worked job_unavailable)
|
|
17
|
-
|
|
18
|
-
json = JSON.load($logger.messages[0])
|
|
19
|
-
json['job']['job_class'].should == 'Que::Job'
|
|
20
|
-
ensure
|
|
21
|
-
if @worker
|
|
22
|
-
@worker.stop
|
|
23
|
-
@worker.wait_until_stopped
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
it "should work jobs without a named queue by default" do
|
|
29
|
-
begin
|
|
30
|
-
Que::Job.enqueue 1
|
|
31
|
-
Que::Job.enqueue 2, :queue => 'my_queue'
|
|
32
|
-
|
|
33
|
-
@worker = Que::Worker.new
|
|
34
|
-
sleep_until { @worker.sleeping? }
|
|
35
|
-
DB[:que_jobs].count.should be 1
|
|
36
|
-
|
|
37
|
-
$logger.messages.map{|m| JSON.load(m)['event']}.should == %w(job_worked job_unavailable)
|
|
38
|
-
|
|
39
|
-
json = JSON.load($logger.messages[0])
|
|
40
|
-
json['job']['queue'].should == ''
|
|
41
|
-
json['job']['job_class'].should == 'Que::Job'
|
|
42
|
-
json['job']['args'].should == [1]
|
|
43
|
-
ensure
|
|
44
|
-
if @worker
|
|
45
|
-
@worker.stop
|
|
46
|
-
@worker.wait_until_stopped
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
it "should accept the name of a single queue to work jobs from" do
|
|
52
|
-
begin
|
|
53
|
-
Que::Job.enqueue 1
|
|
54
|
-
Que::Job.enqueue 2, :queue => 'my_queue'
|
|
55
|
-
|
|
56
|
-
@worker = Que::Worker.new(:my_queue)
|
|
57
|
-
sleep_until { @worker.sleeping? }
|
|
58
|
-
DB[:que_jobs].count.should be 1
|
|
59
|
-
|
|
60
|
-
$logger.messages.map{|m| JSON.load(m)['event']}.should == %w(job_worked job_unavailable)
|
|
61
|
-
|
|
62
|
-
json = JSON.load($logger.messages[0])
|
|
63
|
-
json['job']['queue'].should == 'my_queue'
|
|
64
|
-
json['job']['job_class'].should == 'Que::Job'
|
|
65
|
-
json['job']['args'].should == [2]
|
|
66
|
-
ensure
|
|
67
|
-
if @worker
|
|
68
|
-
@worker.stop
|
|
69
|
-
@worker.wait_until_stopped
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
it "#wake! should return truthy if the worker was asleep and is woken up, at which point it should work until no jobs are available" do
|
|
75
|
-
begin
|
|
76
|
-
@worker = Que::Worker.new
|
|
77
|
-
sleep_until { @worker.sleeping? }
|
|
78
|
-
|
|
79
|
-
Que::Job.enqueue
|
|
80
|
-
Que::Job.enqueue
|
|
81
|
-
DB[:que_jobs].count.should be 2
|
|
82
|
-
|
|
83
|
-
@worker.wake!.should be true
|
|
84
|
-
sleep_until { @worker.sleeping? }
|
|
85
|
-
DB[:que_jobs].count.should be 0
|
|
86
|
-
ensure
|
|
87
|
-
if @worker
|
|
88
|
-
@worker.stop
|
|
89
|
-
@worker.wait_until_stopped
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
it "#wake! should return falsy if the worker was already working" do
|
|
95
|
-
begin
|
|
96
|
-
BlockJob.enqueue
|
|
97
|
-
@worker = Que::Worker.new
|
|
98
|
-
|
|
99
|
-
$q1.pop
|
|
100
|
-
DB[:que_jobs].count.should be 1
|
|
101
|
-
@worker.wake!.should be nil
|
|
102
|
-
$q2.push nil
|
|
103
|
-
ensure
|
|
104
|
-
if @worker
|
|
105
|
-
@worker.stop
|
|
106
|
-
@worker.wait_until_stopped
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
it "should not be deterred by a job that raises an error" do
|
|
112
|
-
begin
|
|
113
|
-
ErrorJob.enqueue :priority => 1
|
|
114
|
-
Que::Job.enqueue :priority => 5
|
|
115
|
-
|
|
116
|
-
@worker = Que::Worker.new
|
|
117
|
-
|
|
118
|
-
sleep_until { @worker.sleeping? }
|
|
119
|
-
|
|
120
|
-
DB[:que_jobs].count.should be 1
|
|
121
|
-
job = DB[:que_jobs].first
|
|
122
|
-
job[:job_class].should == 'ErrorJob'
|
|
123
|
-
job[:run_at].should be_within(3).of Time.now + 4
|
|
124
|
-
|
|
125
|
-
log = JSON.load($logger.messages[0])
|
|
126
|
-
log['event'].should == 'job_errored'
|
|
127
|
-
log['error']['class'].should == 'RuntimeError'
|
|
128
|
-
log['error']['message'].should == "ErrorJob!"
|
|
129
|
-
log['job']['job_class'].should == 'ErrorJob'
|
|
130
|
-
ensure
|
|
131
|
-
if @worker
|
|
132
|
-
@worker.stop
|
|
133
|
-
@worker.wait_until_stopped
|
|
134
|
-
end
|
|
135
|
-
end
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
it "should receive and respect a notification to stop down when it is working, after its current job completes" do
|
|
139
|
-
begin
|
|
140
|
-
BlockJob.enqueue :priority => 1
|
|
141
|
-
Que::Job.enqueue :priority => 5
|
|
142
|
-
DB[:que_jobs].count.should be 2
|
|
143
|
-
|
|
144
|
-
@worker = Que::Worker.new
|
|
145
|
-
|
|
146
|
-
$q1.pop
|
|
147
|
-
@worker.stop
|
|
148
|
-
$q2.push nil
|
|
149
|
-
|
|
150
|
-
@worker.wait_until_stopped
|
|
151
|
-
|
|
152
|
-
DB[:que_jobs].count.should be 1
|
|
153
|
-
job = DB[:que_jobs].first
|
|
154
|
-
job[:job_class].should == 'Que::Job'
|
|
155
|
-
end
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
it "should receive and respect a notification to stop when it is currently asleep" do
|
|
159
|
-
begin
|
|
160
|
-
@worker = Que::Worker.new
|
|
161
|
-
sleep_until { @worker.sleeping? }
|
|
162
|
-
|
|
163
|
-
@worker.stop
|
|
164
|
-
@worker.wait_until_stopped
|
|
165
|
-
end
|
|
166
|
-
end
|
|
167
|
-
end
|
data/tasks/benchmark.rb
DELETED
data/tasks/rspec.rb
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'rspec/core/rake_task'
|
|
4
|
-
|
|
5
|
-
RSpec::Core::RakeTask.new :default do |spec|
|
|
6
|
-
spec.pattern = './spec/**/*_spec.rb'
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
# Shortcut to skip the adapter specs, and run only with the basic PG
|
|
10
|
-
# connection. I use this occasionally to make sure ActiveRecord isn't loaded,
|
|
11
|
-
# so any accidental Rails-isms are caught.
|
|
12
|
-
RSpec::Core::RakeTask.new :pg do |spec|
|
|
13
|
-
spec.pattern = './spec/unit/*_spec.rb'
|
|
14
|
-
end
|
data/tasks/safe_shutdown.rb
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# This task is used to test Que's behavior when its process is shut down.
|
|
4
|
-
|
|
5
|
-
# The situation we're trying to avoid occurs when the process dies while a job
|
|
6
|
-
# is in the middle of a transaction - ideally, the transaction would be rolled
|
|
7
|
-
# back and the job could just be reattempted later, but if we're not careful,
|
|
8
|
-
# the transaction could be committed prematurely. For specifics, see here:
|
|
9
|
-
|
|
10
|
-
# http://coderrr.wordpress.com/2011/05/03/beware-of-threadkill-or-your-activerecord-transactions-are-in-danger-of-being-partially-committed/
|
|
11
|
-
|
|
12
|
-
# So, this task opens a transaction within a job, makes a write, then prompts
|
|
13
|
-
# you to kill it with one of a few signals. You can then run it again to make
|
|
14
|
-
# sure that the write was rolled back (if it wasn't, Que isn't functioning
|
|
15
|
-
# like it should). This task only explicitly tests Sequel, but the behavior
|
|
16
|
-
# for ActiveRecord is very similar.
|
|
17
|
-
|
|
18
|
-
task :safe_shutdown do
|
|
19
|
-
require 'sequel'
|
|
20
|
-
require 'que'
|
|
21
|
-
|
|
22
|
-
url = ENV['DATABASE_URL'] || 'postgres://postgres:@localhost/que-test'
|
|
23
|
-
DB = Sequel.connect(url)
|
|
24
|
-
|
|
25
|
-
if DB.table_exists?(:que_jobs)
|
|
26
|
-
puts "Uh-oh! Previous shutdown wasn't clean!" if DB[:que_jobs].where(:job_id => 0).count > 0
|
|
27
|
-
DB.drop_table :que_jobs
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
Que.connection = DB
|
|
31
|
-
Que.create!
|
|
32
|
-
|
|
33
|
-
$queue = Queue.new
|
|
34
|
-
|
|
35
|
-
class SafeJob < Que::Job
|
|
36
|
-
def run
|
|
37
|
-
DB.transaction do
|
|
38
|
-
DB[:que_jobs].insert(:job_id => 0, :job_class => 'Que::Job')
|
|
39
|
-
$queue.push nil
|
|
40
|
-
sleep
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
SafeJob.enqueue
|
|
46
|
-
Que.mode = :async
|
|
47
|
-
$queue.pop
|
|
48
|
-
|
|
49
|
-
puts "From a different terminal window, run one of the following:"
|
|
50
|
-
%w(SIGINT SIGTERM SIGKILL).each do |signal|
|
|
51
|
-
puts "kill -#{signal} #{Process.pid}"
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
stop = false
|
|
55
|
-
trap('INT'){stop = true}
|
|
56
|
-
|
|
57
|
-
at_exit do
|
|
58
|
-
$stdout.puts "Finishing Que's current jobs before exiting..."
|
|
59
|
-
Que.mode = :off
|
|
60
|
-
$stdout.puts "Que's jobs finished, exiting..."
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
loop do
|
|
64
|
-
sleep 0.01
|
|
65
|
-
break if stop
|
|
66
|
-
end
|
|
67
|
-
end
|