delayed_job_groups_plugin 0.14.0 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +10 -0
- data/lib/delayed/job_groups/errors.rb +16 -0
- data/lib/delayed/job_groups/job_group.rb +12 -2
- data/lib/delayed/job_groups/plugin.rb +7 -1
- data/lib/delayed/job_groups/railtie.rb +11 -0
- data/lib/delayed/job_groups/version.rb +1 -1
- data/lib/delayed_job_groups_plugin.rb +2 -0
- data/lib/generators/delayed_job_groups_plugin/install_generator.rb +32 -0
- data/spec/delayed/job_groups/job_group_spec.rb +33 -1
- data/spec/spec_helper.rb +5 -3
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9d88649deb9812581de80f6a57a265ccfd265f7f9122d837eff527647f77f01b
|
4
|
+
data.tar.gz: 4979b4dd43fb9294fb622e478ac5bb36a7f688f83236183eac2fc80a668af828
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '083c6e1461374569d30d7538fb5a7e4d4f980cae04858bbac94793597f62d588a6078b6e55e605ee7e10ff413e0c6ef5460088cd50667663ceefdfc29543ae6a'
|
7
|
+
data.tar.gz: d4ede12f4275b606f0e0704ac3cfc255727f3796979ecff7aa87781629172d0e15ad6e1d0b96254bd845f243a7f8d7bc96f4c4bb670bf4d48be4e4a2590776a4
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 1.0.0
|
4
|
+
### Breaking Changes
|
5
|
+
- This library will fail to load if `Delayed::Worker.destroy_failed_jobs` is set to true.
|
6
|
+
Delayed::Job sets this option to true by default, you will need to configure it to false
|
7
|
+
in order to include this library.
|
8
|
+
### Changes
|
9
|
+
- Moves `on_cancellation` logic from the before delayed job lifecycle hook to the after hook.
|
10
|
+
- Wrapped the job group cancel hook in a lock to prevent concurrently failing jobs from enqueueing
|
11
|
+
multiple on cancellation jobs.
|
12
|
+
|
3
13
|
## 0.14.0
|
4
14
|
- Reverts changes made in version 0.13.
|
5
15
|
|
data/README.md
CHANGED
@@ -35,6 +35,15 @@ Run the required database migrations:
|
|
35
35
|
$ rails generate delayed_job_groups_plugin:install
|
36
36
|
$ rake db:migrate
|
37
37
|
|
38
|
+
## Upgrading from 0.14.0
|
39
|
+
|
40
|
+
This library is now incompatible with Delayed::Job's default setting for `destroy_failed_jobs`.
|
41
|
+
In order to use Delayed Job Groups, you must set it to false while configuring Delayed::Job:
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
Delayed::Worker.destroy_failed_jobs = false
|
45
|
+
```
|
46
|
+
|
38
47
|
## Upgrading from 0.1.2
|
39
48
|
run the following generator to create a migration for the new configuration column.
|
40
49
|
|
@@ -42,6 +51,7 @@ run the following generator to create a migration for the new configuration colu
|
|
42
51
|
# add `default: true, null: false` to the generated migration for the failure_cancels_group column
|
43
52
|
$ rake db:migrate
|
44
53
|
|
54
|
+
|
45
55
|
## Usage
|
46
56
|
|
47
57
|
Creating a job group and queueing some jobs:
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Delayed
|
4
|
+
module JobGroups
|
5
|
+
ConfigurationError = Class.new(StandardError)
|
6
|
+
|
7
|
+
class IncompatibleWithDelayedJobError < ConfigurationError
|
8
|
+
DEFAULT_MESSAGE = 'DelayedJobGroupsPlugin is incompatible with Delayed::Job ' \
|
9
|
+
'when `destroy_failed_jobs` is set to `true`'
|
10
|
+
|
11
|
+
def initialize(msg = DEFAULT_MESSAGE)
|
12
|
+
super(msg)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -62,8 +62,18 @@ module Delayed
|
|
62
62
|
end
|
63
63
|
|
64
64
|
def cancel
|
65
|
-
|
66
|
-
|
65
|
+
with_lock do
|
66
|
+
return if destroyed?
|
67
|
+
|
68
|
+
Delayed::Job.enqueue(on_cancellation_job, on_cancellation_job_options || {}) if on_cancellation_job
|
69
|
+
destroy
|
70
|
+
end
|
71
|
+
rescue ActiveRecord::RecordNotFound
|
72
|
+
# The first failing job to attempt cancelling the job group will enqueue the
|
73
|
+
# on cancellation job and destroy the group. Any other concurrently failing job
|
74
|
+
# in the same group can therefore silently return if the job group has already
|
75
|
+
# been destroyed.
|
76
|
+
nil
|
67
77
|
end
|
68
78
|
|
69
79
|
def check_for_completion(skip_pending_jobs_check: false)
|
@@ -17,7 +17,13 @@ module Delayed
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
|
20
|
+
# In order to allow individual jobs in the JobGroup to perform cleanup activities upon
|
21
|
+
# job failure, a JobGroups on cancellation job must be enqueued in the after failure
|
22
|
+
# delayed job lifecycle hook. If both were to be performed in the same hook, the job's
|
23
|
+
# failure hook and the on cancellation job may run at the same time since hook execution
|
24
|
+
# order is never guaranteed. This is particular important if the on cancellation job
|
25
|
+
# depends on state set in the failure hook of an individual job.
|
26
|
+
lifecycle.after(:failure) do |_worker, job|
|
21
27
|
# If a job in the job group fails, then cancel the whole job group.
|
22
28
|
# Need to check that the job group is present since another
|
23
29
|
# job may have concurrently cancelled it.
|
@@ -4,6 +4,17 @@ module Delayed
|
|
4
4
|
module JobGroups
|
5
5
|
class Railtie < ::Rails::Railtie
|
6
6
|
config.after_initialize do
|
7
|
+
|
8
|
+
# On cancellation checks are performed in the after failure delayed job lifecycle, however
|
9
|
+
# https://github.com/collectiveidea/delayed_job/blob/master/lib/delayed/worker.rb#L268 may
|
10
|
+
# delete jobs before the hook runs. This could cause a successful job in the same group to
|
11
|
+
# complete the group instead of the group being cancelled. Therefore, we must ensure that
|
12
|
+
# the Delayed::Worker.destroy_failed_jobs is set to false, guaranteeing that the group is
|
13
|
+
# never empty if failure occurs.
|
14
|
+
if Delayed::Worker.destroy_failed_jobs && ActiveRecord::Base.connection.table_exists?(:delayed_job_groups)
|
15
|
+
raise Delayed::JobGroups::IncompatibleWithDelayedJobError
|
16
|
+
end
|
17
|
+
|
7
18
|
Delayed::Backend::ActiveRecord::Job.include(Delayed::JobGroups::JobExtensions)
|
8
19
|
end
|
9
20
|
end
|
@@ -4,6 +4,7 @@ require 'active_support'
|
|
4
4
|
require 'active_record'
|
5
5
|
require 'delayed_job'
|
6
6
|
require 'delayed_job_active_record'
|
7
|
+
require 'delayed/job_groups/errors'
|
7
8
|
require 'delayed/job_groups/compatibility'
|
8
9
|
require 'delayed/job_groups/complete_stuck_job_groups_job'
|
9
10
|
require 'delayed/job_groups/job_extensions'
|
@@ -18,6 +19,7 @@ if defined?(Rails::Railtie)
|
|
18
19
|
else
|
19
20
|
# Do the same as in the railtie
|
20
21
|
Delayed::Backend::ActiveRecord::Job.include(Delayed::JobGroups::JobExtensions)
|
22
|
+
raise Delayed::JobGroups::IncompatibleWithDelayedJobError if Delayed::Worker.destroy_failed_jobs
|
21
23
|
end
|
22
24
|
|
23
25
|
Delayed::Worker.plugins << Delayed::JobGroups::Plugin
|
@@ -18,5 +18,37 @@ module DelayedJobGroupsPlugin
|
|
18
18
|
ActiveRecord::Generators::Base.next_migration_number(dirname)
|
19
19
|
end
|
20
20
|
|
21
|
+
def create_initializer
|
22
|
+
initializer_file = File.join('config/initializers', 'delayed_job_config.rb')
|
23
|
+
configuration_on_matcher = /Delayed::Worker\.destroy_failed_jobs\s*=\s*true/
|
24
|
+
configuration_off_matcher = /Delayed::Worker\.destroy_failed_jobs\s*=\s*false/
|
25
|
+
|
26
|
+
say 'Attempting to initialize delayed_job_config initializer...', :green
|
27
|
+
|
28
|
+
if File.exist?(initializer_file)
|
29
|
+
say 'delayed_job_config initializer already exists... checking destroy_failed_jobs options', :green
|
30
|
+
contents = File.read(initializer_file)
|
31
|
+
if contents.match(configuration_on_matcher)
|
32
|
+
say 'Delayed::Worker.destroy_failed_jobs is set to true', :red
|
33
|
+
say 'This library requires the option to be set to false, updating config now!', :yellow
|
34
|
+
|
35
|
+
gsub_file initializer_file, configuration_on_matcher, 'Delayed::Worker.destroy_failed_jobs = false'
|
36
|
+
elsif contents.match(configuration_off_matcher)
|
37
|
+
say 'Delayed::Worker.destroy_failed_jobs is set to false; nothing to do!', :green
|
38
|
+
else
|
39
|
+
say 'Delayed::Worker.destroy_failed_jobs is not set'
|
40
|
+
say 'This library requires the option to be set to false, updating config now!', :yellow
|
41
|
+
inject_into_file initializer_file, "Delayed::Worker.destroy_failed_jobs = false\n"
|
42
|
+
end
|
43
|
+
else
|
44
|
+
create_file initializer_file do
|
45
|
+
<<~RUBY
|
46
|
+
# frozen_string_literal: true
|
47
|
+
|
48
|
+
Delayed::Worker.destroy_failed_jobs = false
|
49
|
+
RUBY
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
21
53
|
end
|
22
54
|
end
|
@@ -299,11 +299,28 @@ describe Delayed::JobGroups::JobGroup do
|
|
299
299
|
end
|
300
300
|
|
301
301
|
describe "#cancel" do
|
302
|
+
subject(:job_group) do
|
303
|
+
create(
|
304
|
+
:job_group,
|
305
|
+
on_completion_job: on_completion_job,
|
306
|
+
on_completion_job_options: on_completion_job_options,
|
307
|
+
on_cancellation_job: on_cancellation_job,
|
308
|
+
on_cancellation_job_options: on_cancellation_job_options,
|
309
|
+
blocked: blocked
|
310
|
+
)
|
311
|
+
end
|
312
|
+
|
302
313
|
let!(:queued_job) { Delayed::Job.create!(job_group_id: job_group.id) }
|
303
314
|
let!(:running_job) { Delayed::Job.create!(job_group_id: job_group.id, locked_at: Time.now, locked_by: 'test') }
|
315
|
+
let(:before_hook) {}
|
316
|
+
let(:on_cancellation_job) { 'dummy job' }
|
317
|
+
let(:on_cancellation_job_options) do
|
318
|
+
{ foo: 'bar' }
|
319
|
+
end
|
320
|
+
let(:cancel) { true }
|
304
321
|
|
305
322
|
before do
|
306
|
-
job_group.cancel
|
323
|
+
job_group.cancel if cancel
|
307
324
|
end
|
308
325
|
|
309
326
|
it "destroys the job group" do
|
@@ -317,6 +334,21 @@ describe Delayed::JobGroups::JobGroup do
|
|
317
334
|
it "does not destroy running jobs" do
|
318
335
|
expect(running_job).not_to have_been_destroyed
|
319
336
|
end
|
337
|
+
|
338
|
+
context "when already cancelled" do
|
339
|
+
let(:cancel) { false }
|
340
|
+
|
341
|
+
it "skips cancel block if already cancelled" do
|
342
|
+
other = Delayed::JobGroups::JobGroup.find(job_group.id)
|
343
|
+
job_group.cancel
|
344
|
+
|
345
|
+
other.cancel
|
346
|
+
other.cancel
|
347
|
+
|
348
|
+
expect(Delayed::Job)
|
349
|
+
.to have_received(:enqueue).with(on_cancellation_job, on_cancellation_job_options).once
|
350
|
+
end
|
351
|
+
end
|
320
352
|
end
|
321
353
|
|
322
354
|
describe "#failure_cancels_group?" do
|
data/spec/spec_helper.rb
CHANGED
@@ -13,6 +13,11 @@ end
|
|
13
13
|
|
14
14
|
require 'rspec/its'
|
15
15
|
require 'database_cleaner'
|
16
|
+
require 'delayed_job'
|
17
|
+
|
18
|
+
Delayed::Worker.read_ahead = 1
|
19
|
+
Delayed::Worker.destroy_failed_jobs = false
|
20
|
+
|
16
21
|
require 'delayed_job_groups_plugin'
|
17
22
|
require 'factory_bot'
|
18
23
|
require 'yaml'
|
@@ -23,9 +28,6 @@ Dir["#{spec_dir}/support/**/*.rb"].sort.each { |f| require f }
|
|
23
28
|
|
24
29
|
FileUtils.makedirs('log')
|
25
30
|
|
26
|
-
Delayed::Worker.read_ahead = 1
|
27
|
-
Delayed::Worker.destroy_failed_jobs = false
|
28
|
-
|
29
31
|
Delayed::Worker.logger = Logger.new('log/test.log')
|
30
32
|
Delayed::Worker.logger.level = Logger::DEBUG
|
31
33
|
ActiveRecord::Base.logger = Delayed::Worker.logger
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: delayed_job_groups_plugin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joel Turkel
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2025-
|
12
|
+
date: 2025-10-08 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
@@ -269,6 +269,7 @@ files:
|
|
269
269
|
- gemfiles/rails_8.0.gemfile
|
270
270
|
- lib/delayed/job_groups/compatibility.rb
|
271
271
|
- lib/delayed/job_groups/complete_stuck_job_groups_job.rb
|
272
|
+
- lib/delayed/job_groups/errors.rb
|
272
273
|
- lib/delayed/job_groups/job_extensions.rb
|
273
274
|
- lib/delayed/job_groups/job_group.rb
|
274
275
|
- lib/delayed/job_groups/plugin.rb
|