delayed 1.1.0 → 1.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 +4 -4
- data/README.md +22 -2
- data/app/models/delayed/job.rb +8 -1
- data/{lib/generators/delayed/templates/migration.rb → db/migrate/1_create_delayed_jobs.rb} +1 -1
- data/db/migrate/2_add_name_to_delayed_jobs.rb +5 -0
- data/db/migrate/3_add_index_to_delayed_jobs_name.rb +11 -0
- data/lib/delayed/backend/base.rb +18 -6
- data/lib/delayed/engine.rb +2 -0
- data/lib/delayed/version.rb +1 -1
- data/spec/delayed/job_spec.rb +104 -18
- data/spec/helper.rb +7 -23
- metadata +4 -5
- data/lib/generators/delayed/generator.rb +0 -7
- data/lib/generators/delayed/migration_generator.rb +0 -28
- data/lib/generators/delayed/next_migration_version.rb +0 -14
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 02d090c8d335114c2ca9d46a6aee12025dba8e78e13351619e7712ee49a6df98
|
|
4
|
+
data.tar.gz: 9e9cc9e3e41209de66c2dac1adfe62a6b4fb7ed243220d16fd39beb1da11a6a8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9515b3eea5aa30a6745458f50bc843e4857b77e7ec73576e2acca8562a21fd14e80293f4b4278aa566a837b2b33166e92239a6904df6d60c2d42a77416362aad
|
|
7
|
+
data.tar.gz: e32e446db1d012a252d93dce7ca31318c1877c700085892a0071a88a54418fa78ad44b764c757ded8d9a157a09b7b7249679e6d5728fcf5deeec45370f881361
|
data/README.md
CHANGED
|
@@ -76,10 +76,15 @@ Before you can enqueue and run jobs, you will need a jobs table. You can create
|
|
|
76
76
|
running the following command:
|
|
77
77
|
|
|
78
78
|
```bash
|
|
79
|
-
|
|
79
|
+
rake delayed:install:migrations
|
|
80
80
|
rails db:migrate
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
+
This will produce a series of migrations ready to be run in sequence.
|
|
84
|
+
(Re-running the command should be safe and will **not** duplicate previous
|
|
85
|
+
migrations, but if you have an existing `delayed_jobs` table, you may need to
|
|
86
|
+
adjust the generated migrations to avoid conflicts.)
|
|
87
|
+
|
|
83
88
|
Then, to use this background job processor with ActiveJob, add the following to your application config:
|
|
84
89
|
|
|
85
90
|
```ruby
|
|
@@ -535,7 +540,7 @@ class OrderPurchaseJob < ApplicationJob
|
|
|
535
540
|
end
|
|
536
541
|
```
|
|
537
542
|
|
|
538
|
-
|
|
543
|
+
## Migrating from DelayedJob
|
|
539
544
|
|
|
540
545
|
If you choose to use `delayed` in an app that was originally written against `delayed_job`, several
|
|
541
546
|
non-ActiveJob APIs are still available. These include "plugins", lifecycle hooks, and the `.delay`
|
|
@@ -558,6 +563,21 @@ Delayed::Worker.destroy_failed_jobs = true # WARNING: This will irreversably del
|
|
|
558
563
|
Note that some configurations, like `queue_attributes`, `exit_on_complete`, `backend`, and
|
|
559
564
|
`raise_signal_exceptions` have been removed entirely.
|
|
560
565
|
|
|
566
|
+
Furthermore, there are optional schema migrations that you may wish to apply, to take advantage of
|
|
567
|
+
new behaviors that are unique to `delayed`:
|
|
568
|
+
|
|
569
|
+
```bash
|
|
570
|
+
rake delayed:install:migrations
|
|
571
|
+
rails db:migrate
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
As of writing, this will add the following:
|
|
575
|
+
- `delayed_jobs.name`: a string representation of the job's performable class name, populated on
|
|
576
|
+
enqueue.
|
|
577
|
+
|
|
578
|
+
Note: Schema changes may become non-optional in future `delayed` releases, and will be documented in
|
|
579
|
+
the README and release notes.
|
|
580
|
+
|
|
561
581
|
## How to Contribute
|
|
562
582
|
|
|
563
583
|
We would love for you to contribute! Anything that benefits the majority of users—from a
|
data/app/models/delayed/job.rb
CHANGED
|
@@ -15,7 +15,7 @@ module Delayed
|
|
|
15
15
|
scope :workable, ->(timestamp) { not_locked.not_failed.where("run_at <= ?", timestamp) }
|
|
16
16
|
scope :working, -> { locked.not_failed }
|
|
17
17
|
|
|
18
|
-
before_save :set_default_run_at
|
|
18
|
+
before_save :set_default_run_at, :set_name
|
|
19
19
|
|
|
20
20
|
REENQUEUE_BUFFER = 30.seconds
|
|
21
21
|
|
|
@@ -252,5 +252,12 @@ module Delayed
|
|
|
252
252
|
def attempts_alert?
|
|
253
253
|
alert_attempts&.<= attempts
|
|
254
254
|
end
|
|
255
|
+
|
|
256
|
+
private
|
|
257
|
+
|
|
258
|
+
def set_name
|
|
259
|
+
# [feat:NameColumn] remove 'if' statement once the 'name' column is required.
|
|
260
|
+
self.name ||= display_name if respond_to?(:name=)
|
|
261
|
+
end
|
|
255
262
|
end
|
|
256
263
|
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
class CreateDelayedJobs < ActiveRecord::Migration
|
|
1
|
+
class CreateDelayedJobs < ActiveRecord::Migration[6.0]
|
|
2
2
|
def self.up
|
|
3
3
|
create_table :delayed_jobs do |table|
|
|
4
4
|
table.integer :priority, default: 0, null: false # Allows some jobs to jump to the front of the queue
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
class AddIndexToDelayedJobsName < ActiveRecord::Migration[6.0]
|
|
2
|
+
disable_ddl_transaction!
|
|
3
|
+
|
|
4
|
+
def change
|
|
5
|
+
if connection.adapter_name == 'PostgreSQL'
|
|
6
|
+
add_index :delayed_jobs, :name, algorithm: :concurrently
|
|
7
|
+
else
|
|
8
|
+
add_index :delayed_jobs, :name
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
data/lib/delayed/backend/base.rb
CHANGED
|
@@ -57,12 +57,12 @@ module Delayed
|
|
|
57
57
|
|
|
58
58
|
ParseObjectFromYaml = %r{!ruby/\w+:([^\s]+)} # rubocop:disable Naming/ConstantName
|
|
59
59
|
|
|
60
|
-
def name
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
60
|
+
def name
|
|
61
|
+
if self.class.column_names.include?('name')
|
|
62
|
+
super || display_name
|
|
63
|
+
else
|
|
64
|
+
display_name # [feat:NameColumn] remove fallback once the "name" column is required.
|
|
65
|
+
end
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
def priority
|
|
@@ -151,6 +151,18 @@ module Delayed
|
|
|
151
151
|
save!
|
|
152
152
|
end
|
|
153
153
|
|
|
154
|
+
private
|
|
155
|
+
|
|
156
|
+
def display_name # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
|
|
157
|
+
@display_name ||= payload_object.job_data['job_class'] if payload_object.respond_to?(:job_data)
|
|
158
|
+
@display_name ||= payload_object.display_name if payload_object.respond_to?(:display_name)
|
|
159
|
+
@display_name ||= payload_object.class.name
|
|
160
|
+
rescue DeserializationError # [feat:NameColumn] remove this `rescue` once the "name" column is required.
|
|
161
|
+
raise if !persisted? && self.class.column_names.include?('name')
|
|
162
|
+
|
|
163
|
+
ParseObjectFromYaml.match(handler)[1]
|
|
164
|
+
end
|
|
165
|
+
|
|
154
166
|
protected
|
|
155
167
|
|
|
156
168
|
def set_default_run_at
|
data/lib/delayed/engine.rb
CHANGED
data/lib/delayed/version.rb
CHANGED
data/spec/delayed/job_spec.rb
CHANGED
|
@@ -253,29 +253,114 @@ describe Delayed::Job do
|
|
|
253
253
|
end
|
|
254
254
|
|
|
255
255
|
describe '#name' do
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
256
|
+
context 'when name column is populated' do
|
|
257
|
+
it 'is the class name of the job that was enqueued' do
|
|
258
|
+
job = described_class.new(payload_object: ErrorJob.new)
|
|
259
|
+
expect(job.name).to eq('ErrorJob')
|
|
260
|
+
job.save!
|
|
261
|
+
expect(job.reload.name).to eq('ErrorJob')
|
|
262
|
+
end
|
|
259
263
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
+
it 'is the class name of the performable job if it is an ActiveJob' do
|
|
265
|
+
job_wrapper = ActiveJob::QueueAdapters::DelayedJobAdapter::JobWrapper.new(ActiveJobJob.new.serialize)
|
|
266
|
+
job = described_class.new(payload_object: job_wrapper)
|
|
267
|
+
expect(job.name).to eq('ActiveJobJob')
|
|
268
|
+
job.save!
|
|
269
|
+
expect(job.reload.name).to eq('ActiveJobJob')
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
it 'is the returned display_name if display_name is defined on the job object' do
|
|
273
|
+
job = described_class.new(payload_object: NamedJob.new)
|
|
274
|
+
expect(job.name).to eq('named_job')
|
|
275
|
+
job.save!
|
|
276
|
+
expect(job.reload.name).to eq('named_job')
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
it 'is the instance method that will be called if its a performable method object' do
|
|
280
|
+
job = Story.create(text: '...').delay.save
|
|
281
|
+
expect(job.name).to eq('Story#save')
|
|
282
|
+
end
|
|
264
283
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
284
|
+
it 'is the custom name value when set explicitly' do
|
|
285
|
+
job = described_class.new(payload_object: ErrorJob.new)
|
|
286
|
+
job.name = 'Custom Name'
|
|
287
|
+
job.save!
|
|
288
|
+
expect(job.reload.name).to eq('Custom Name')
|
|
289
|
+
end
|
|
268
290
|
end
|
|
269
291
|
|
|
270
|
-
|
|
271
|
-
job
|
|
272
|
-
|
|
292
|
+
context 'when name column is NULL' do
|
|
293
|
+
it 'is the class name of the job that was enqueued' do
|
|
294
|
+
job = described_class.create(payload_object: ErrorJob.new)
|
|
295
|
+
job.update_column(:name, nil) # rubocop:disable Rails/SkipsModelValidations
|
|
296
|
+
expect(job.reload.name).to eq('ErrorJob')
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
it 'is the class name of the performable job if it is an ActiveJob' do
|
|
300
|
+
job_wrapper = ActiveJob::QueueAdapters::DelayedJobAdapter::JobWrapper.new(ActiveJobJob.new.serialize)
|
|
301
|
+
job = described_class.create(payload_object: job_wrapper)
|
|
302
|
+
job.update_column(:name, nil) # rubocop:disable Rails/SkipsModelValidations
|
|
303
|
+
expect(job.reload.name).to eq('ActiveJobJob')
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
it 'parses from handler on deserialization error' do
|
|
307
|
+
job = Story.create(text: '...').delay.text
|
|
308
|
+
job.payload_object.object.destroy
|
|
309
|
+
job.save!
|
|
310
|
+
job.update_column(:name, nil) # rubocop:disable Rails/SkipsModelValidations
|
|
311
|
+
expect(job.reload.name).to eq('Delayed::PerformableMethod')
|
|
312
|
+
end
|
|
273
313
|
end
|
|
274
314
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
315
|
+
context 'when name column does not exist' do
|
|
316
|
+
before do
|
|
317
|
+
ActiveRecord::Schema.define do
|
|
318
|
+
AddIndexToDelayedJobsName.migrate(:down)
|
|
319
|
+
AddNameToDelayedJobs.migrate(:down)
|
|
320
|
+
end
|
|
321
|
+
described_class.reset_column_information
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
after do
|
|
325
|
+
ActiveRecord::Schema.define do
|
|
326
|
+
AddNameToDelayedJobs.migrate(:up)
|
|
327
|
+
AddIndexToDelayedJobsName.migrate(:up)
|
|
328
|
+
end
|
|
329
|
+
described_class.reset_column_information
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
it 'is the class name of the job that was enqueued' do
|
|
333
|
+
job = described_class.new(payload_object: ErrorJob.new)
|
|
334
|
+
expect(job.name).to eq('ErrorJob')
|
|
335
|
+
job.save!
|
|
336
|
+
expect(job.reload.name).to eq('ErrorJob')
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
it 'is the class name of the performable job if it is an ActiveJob' do
|
|
340
|
+
job_wrapper = ActiveJob::QueueAdapters::DelayedJobAdapter::JobWrapper.new(ActiveJobJob.new.serialize)
|
|
341
|
+
job = described_class.new(payload_object: job_wrapper)
|
|
342
|
+
expect(job.name).to eq('ActiveJobJob')
|
|
343
|
+
job.save!
|
|
344
|
+
expect(job.reload.name).to eq('ActiveJobJob')
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
it 'is the returned display_name if display_name is defined on the job object' do
|
|
348
|
+
job = described_class.new(payload_object: NamedJob.new)
|
|
349
|
+
expect(job.name).to eq('named_job')
|
|
350
|
+
job.save!
|
|
351
|
+
expect(job.reload.name).to eq('named_job')
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
it 'is the instance method that will be called if its a performable method object' do
|
|
355
|
+
job = Story.create(text: '...').delay.save
|
|
356
|
+
expect(job.name).to eq('Story#save')
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
it 'parses from handler on deserialization error' do
|
|
360
|
+
job = Story.create(text: '...').delay.text
|
|
361
|
+
job.payload_object.object.destroy
|
|
362
|
+
expect(job.reload.name).to eq('Delayed::PerformableMethod')
|
|
363
|
+
end
|
|
279
364
|
end
|
|
280
365
|
end
|
|
281
366
|
|
|
@@ -542,7 +627,8 @@ describe Delayed::Job do
|
|
|
542
627
|
|
|
543
628
|
context 'when the job raises a deserialization error' do
|
|
544
629
|
it 'marks the job as failed' do
|
|
545
|
-
job = described_class.create!
|
|
630
|
+
job = described_class.create! payload_object: LongRunningJob.new
|
|
631
|
+
job.update_columns(handler: '--- !ruby/object:JobThatDoesNotExist {}') # rubocop:disable Rails/SkipsModelValidations
|
|
546
632
|
expect_any_instance_of(described_class).to receive(:destroy_failed_jobs?).and_return(false)
|
|
547
633
|
worker.work_off
|
|
548
634
|
job.reload
|
data/spec/helper.rb
CHANGED
|
@@ -59,32 +59,14 @@ if db_adapter == "mysql2"
|
|
|
59
59
|
types[:primary_key] = types[:primary_key].sub(" DEFAULT NULL", "")
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
# need to eval the template with the migration_version intact
|
|
65
|
-
migration_context =
|
|
66
|
-
Class.new do
|
|
67
|
-
def my_binding
|
|
68
|
-
binding
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
private
|
|
72
|
-
|
|
73
|
-
def migration_version
|
|
74
|
-
"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]" if ActiveRecord::VERSION::MAJOR >= 5
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
migration_ruby = ERB.new(migration_template.read).result(migration_context.new.my_binding)
|
|
79
|
-
eval(migration_ruby) # rubocop:disable Security/Eval
|
|
62
|
+
Dir['db/migrate/*.rb'].each { |f| require_relative("../#{f}") }
|
|
80
63
|
|
|
81
64
|
ActiveRecord::Schema.define do
|
|
82
|
-
|
|
83
|
-
# `if_exists: true` was only added in Rails 5
|
|
84
|
-
drop_table :delayed_jobs
|
|
85
|
-
end
|
|
65
|
+
drop_table :delayed_jobs, if_exists: true
|
|
86
66
|
|
|
87
|
-
CreateDelayedJobs.up
|
|
67
|
+
CreateDelayedJobs.migrate(:up)
|
|
68
|
+
AddNameToDelayedJobs.migrate(:up)
|
|
69
|
+
AddIndexToDelayedJobsName.migrate(:up)
|
|
88
70
|
|
|
89
71
|
create_table :stories, primary_key: :story_id, force: true do |table|
|
|
90
72
|
table.string :text
|
|
@@ -92,6 +74,8 @@ ActiveRecord::Schema.define do
|
|
|
92
74
|
end
|
|
93
75
|
end
|
|
94
76
|
|
|
77
|
+
Delayed::Job.reset_column_information
|
|
78
|
+
|
|
95
79
|
class Story < ActiveRecord::Base
|
|
96
80
|
self.primary_key = :story_id
|
|
97
81
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: delayed
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nathan Griffith
|
|
@@ -65,6 +65,9 @@ files:
|
|
|
65
65
|
- README.md
|
|
66
66
|
- Rakefile
|
|
67
67
|
- app/models/delayed/job.rb
|
|
68
|
+
- db/migrate/1_create_delayed_jobs.rb
|
|
69
|
+
- db/migrate/2_add_name_to_delayed_jobs.rb
|
|
70
|
+
- db/migrate/3_add_index_to_delayed_jobs_name.rb
|
|
68
71
|
- lib/delayed.rb
|
|
69
72
|
- lib/delayed/active_job_adapter.rb
|
|
70
73
|
- lib/delayed/backend/base.rb
|
|
@@ -91,10 +94,6 @@ files:
|
|
|
91
94
|
- lib/delayed/yaml_ext.rb
|
|
92
95
|
- lib/delayed_job.rb
|
|
93
96
|
- lib/delayed_job_active_record.rb
|
|
94
|
-
- lib/generators/delayed/generator.rb
|
|
95
|
-
- lib/generators/delayed/migration_generator.rb
|
|
96
|
-
- lib/generators/delayed/next_migration_version.rb
|
|
97
|
-
- lib/generators/delayed/templates/migration.rb
|
|
98
97
|
- spec/autoloaded/clazz.rb
|
|
99
98
|
- spec/autoloaded/instance_clazz.rb
|
|
100
99
|
- spec/autoloaded/instance_struct.rb
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
require "generators/delayed/generator"
|
|
2
|
-
require "generators/delayed/next_migration_version"
|
|
3
|
-
require "rails/generators/migration"
|
|
4
|
-
require "rails/generators/active_record"
|
|
5
|
-
|
|
6
|
-
# Extend the DelayedJobGenerator so that it creates an AR migration
|
|
7
|
-
module Delayed
|
|
8
|
-
class MigrationGenerator < Generator
|
|
9
|
-
include Rails::Generators::Migration
|
|
10
|
-
extend NextMigrationVersion
|
|
11
|
-
|
|
12
|
-
source_paths << File.join(File.dirname(__FILE__), "templates")
|
|
13
|
-
|
|
14
|
-
def create_migration_file
|
|
15
|
-
migration_template "migration.rb", "db/migrate/create_delayed_jobs.rb", migration_version: migration_version
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def self.next_migration_number(dirname)
|
|
19
|
-
ActiveRecord::Generators::Base.next_migration_number dirname
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
private
|
|
23
|
-
|
|
24
|
-
def migration_version
|
|
25
|
-
"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]" if ActiveRecord::VERSION::MAJOR >= 5
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
module Delayed
|
|
2
|
-
module NextMigrationVersion
|
|
3
|
-
# while methods have moved around this has been the implementation
|
|
4
|
-
# since ActiveRecord 3.0
|
|
5
|
-
def next_migration_number(dirname)
|
|
6
|
-
next_migration_number = current_migration_number(dirname) + 1
|
|
7
|
-
if ActiveRecord::Base.timestamped_migrations
|
|
8
|
-
[Time.now.utc.strftime("%Y%m%d%H%M%S"), format("%.14d", next_migration_number)].max
|
|
9
|
-
else
|
|
10
|
-
format("%.3d", next_migration_number)
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|