que 0.11.3 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/tests.yml +51 -0
  3. data/.gitignore +2 -0
  4. data/.ruby-version +1 -0
  5. data/CHANGELOG.md +502 -97
  6. data/Dockerfile +20 -0
  7. data/LICENSE.txt +1 -1
  8. data/README.md +205 -59
  9. data/auto/dev +21 -0
  10. data/auto/pre-push-hook +30 -0
  11. data/auto/psql +9 -0
  12. data/auto/test +5 -0
  13. data/auto/test-postgres-14 +17 -0
  14. data/bin/que +8 -81
  15. data/docker-compose.yml +47 -0
  16. data/docs/README.md +881 -0
  17. data/lib/que/active_job/extensions.rb +114 -0
  18. data/lib/que/active_record/connection.rb +51 -0
  19. data/lib/que/active_record/model.rb +48 -0
  20. data/lib/que/command_line_interface.rb +259 -0
  21. data/lib/que/connection.rb +198 -0
  22. data/lib/que/connection_pool.rb +78 -0
  23. data/lib/que/job.rb +210 -103
  24. data/lib/que/job_buffer.rb +255 -0
  25. data/lib/que/job_methods.rb +176 -0
  26. data/lib/que/listener.rb +176 -0
  27. data/lib/que/locker.rb +507 -0
  28. data/lib/que/metajob.rb +47 -0
  29. data/lib/que/migrations/4/down.sql +48 -0
  30. data/lib/que/migrations/4/up.sql +267 -0
  31. data/lib/que/migrations/5/down.sql +73 -0
  32. data/lib/que/migrations/5/up.sql +76 -0
  33. data/lib/que/migrations/6/down.sql +8 -0
  34. data/lib/que/migrations/6/up.sql +8 -0
  35. data/lib/que/migrations/7/down.sql +5 -0
  36. data/lib/que/migrations/7/up.sql +13 -0
  37. data/lib/que/migrations.rb +37 -18
  38. data/lib/que/poller.rb +274 -0
  39. data/lib/que/rails/railtie.rb +12 -0
  40. data/lib/que/result_queue.rb +35 -0
  41. data/lib/que/sequel/model.rb +52 -0
  42. data/lib/que/utils/assertions.rb +62 -0
  43. data/lib/que/utils/constantization.rb +19 -0
  44. data/lib/que/utils/error_notification.rb +68 -0
  45. data/lib/que/utils/freeze.rb +20 -0
  46. data/lib/que/utils/introspection.rb +50 -0
  47. data/lib/que/utils/json_serialization.rb +21 -0
  48. data/lib/que/utils/logging.rb +79 -0
  49. data/lib/que/utils/middleware.rb +46 -0
  50. data/lib/que/utils/queue_management.rb +18 -0
  51. data/lib/que/utils/ruby2_keywords.rb +19 -0
  52. data/lib/que/utils/transactions.rb +34 -0
  53. data/lib/que/version.rb +5 -1
  54. data/lib/que/worker.rb +145 -149
  55. data/lib/que.rb +103 -159
  56. data/que.gemspec +17 -4
  57. data/scripts/docker-entrypoint +14 -0
  58. data/scripts/test +6 -0
  59. metadata +59 -95
  60. data/.rspec +0 -2
  61. data/.travis.yml +0 -17
  62. data/Gemfile +0 -24
  63. data/docs/advanced_setup.md +0 -106
  64. data/docs/customizing_que.md +0 -200
  65. data/docs/error_handling.md +0 -47
  66. data/docs/inspecting_the_queue.md +0 -114
  67. data/docs/logging.md +0 -50
  68. data/docs/managing_workers.md +0 -80
  69. data/docs/migrating.md +0 -30
  70. data/docs/multiple_queues.md +0 -27
  71. data/docs/shutting_down_safely.md +0 -7
  72. data/docs/using_plain_connections.md +0 -41
  73. data/docs/using_sequel.md +0 -31
  74. data/docs/writing_reliable_jobs.md +0 -117
  75. data/lib/generators/que/install_generator.rb +0 -24
  76. data/lib/generators/que/templates/add_que.rb +0 -13
  77. data/lib/que/adapters/active_record.rb +0 -54
  78. data/lib/que/adapters/base.rb +0 -127
  79. data/lib/que/adapters/connection_pool.rb +0 -16
  80. data/lib/que/adapters/pg.rb +0 -21
  81. data/lib/que/adapters/pond.rb +0 -16
  82. data/lib/que/adapters/sequel.rb +0 -20
  83. data/lib/que/railtie.rb +0 -16
  84. data/lib/que/rake_tasks.rb +0 -59
  85. data/lib/que/sql.rb +0 -152
  86. data/spec/adapters/active_record_spec.rb +0 -152
  87. data/spec/adapters/connection_pool_spec.rb +0 -22
  88. data/spec/adapters/pg_spec.rb +0 -41
  89. data/spec/adapters/pond_spec.rb +0 -22
  90. data/spec/adapters/sequel_spec.rb +0 -57
  91. data/spec/gemfiles/Gemfile1 +0 -18
  92. data/spec/gemfiles/Gemfile2 +0 -18
  93. data/spec/spec_helper.rb +0 -118
  94. data/spec/support/helpers.rb +0 -19
  95. data/spec/support/jobs.rb +0 -35
  96. data/spec/support/shared_examples/adapter.rb +0 -37
  97. data/spec/support/shared_examples/multi_threaded_adapter.rb +0 -46
  98. data/spec/travis.rb +0 -23
  99. data/spec/unit/connection_spec.rb +0 -14
  100. data/spec/unit/customization_spec.rb +0 -251
  101. data/spec/unit/enqueue_spec.rb +0 -245
  102. data/spec/unit/helper_spec.rb +0 -12
  103. data/spec/unit/logging_spec.rb +0 -101
  104. data/spec/unit/migrations_spec.rb +0 -84
  105. data/spec/unit/pool_spec.rb +0 -365
  106. data/spec/unit/run_spec.rb +0 -14
  107. data/spec/unit/states_spec.rb +0 -50
  108. data/spec/unit/stats_spec.rb +0 -46
  109. data/spec/unit/transaction_spec.rb +0 -36
  110. data/spec/unit/work_spec.rb +0 -407
  111. data/spec/unit/worker_spec.rb +0 -167
  112. data/tasks/benchmark.rb +0 -3
  113. data/tasks/rspec.rb +0 -14
  114. data/tasks/safe_shutdown.rb +0 -67
@@ -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
@@ -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
@@ -1,3 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # The benchmarking tasks have been merged together and now live at github.com/chanks/queue-shootout.
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
@@ -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