delayed_job_groups_plugin 0.7.0 → 0.9.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/.circleci/config.yml +9 -13
- data/.github/CODEOWNERS +1 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +1 -1
- data/Appraisals +6 -12
- data/CHANGELOG.md +12 -0
- data/README.md +21 -8
- data/bin/console +15 -0
- data/delayed_job_groups.gemspec +10 -2
- data/gemfiles/rails_6.1.gemfile +2 -2
- data/gemfiles/{rails_6.0.gemfile → rails_7.0.gemfile} +2 -2
- data/lib/delayed/job_groups/complete_stuck_job_groups_job.rb +19 -0
- data/lib/delayed/job_groups/job_group.rb +11 -2
- data/lib/delayed/job_groups/version.rb +1 -1
- data/lib/delayed_job_groups_plugin.rb +1 -0
- data/spec/delayed/job_groups/complete_stuck_job_groups_job_spec.rb +40 -0
- data/spec/delayed/job_groups/job_extensions_spec.rb +1 -1
- data/spec/delayed/job_groups/job_group_spec.rb +95 -5
- data/spec/delayed/job_groups/plugin_spec.rb +7 -4
- data/spec/factories/delayed_jobs.rb +7 -0
- data/spec/factories/job_groups.rb +10 -0
- data/spec/spec_helper.rb +4 -0
- metadata +43 -19
- data/gemfiles/rails_5.2.gemfile +0 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 392054f5714a429eb7c2ebdde98483c314ce2cfb75730b245b09b50e7a985b14
|
|
4
|
+
data.tar.gz: c91fa4ec6ad52ec7df474b66206d2d6e8f2a605236d9a9172ff7eefe0566a84f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 18a9acd69ea9789c1633289b2b39cc5fa8a497276eb574397b307294d890aedb90ac8d9537077c36fe28547abf3c514eb8881408a1fda093270c73f5ac80b7f7
|
|
7
|
+
data.tar.gz: 0f3d59781205003bc9aa0c50d6a1e34cea7d23a5d5318928187858bb5ed6e64cb3159b659955691016b7b1de4ca345b2bcbaa574ec9bcae06e9d97061208b900
|
data/.circleci/config.yml
CHANGED
|
@@ -2,14 +2,14 @@ version: 2.1
|
|
|
2
2
|
jobs:
|
|
3
3
|
lint:
|
|
4
4
|
docker:
|
|
5
|
-
- image:
|
|
5
|
+
- image: cimg/ruby:3.0.6
|
|
6
6
|
working_directory: ~/delayed_job_groups
|
|
7
7
|
steps:
|
|
8
8
|
- checkout
|
|
9
9
|
- restore_cache:
|
|
10
10
|
keys:
|
|
11
|
-
- v1-gems-ruby-
|
|
12
|
-
- v1-gems-ruby-
|
|
11
|
+
- v1-gems-ruby-3.0.6-{{ checksum "delayed_job_groups.gemspec" }}-{{ checksum "Gemfile" }}
|
|
12
|
+
- v1-gems-ruby-3.0.6-
|
|
13
13
|
- run:
|
|
14
14
|
name: Install Gems
|
|
15
15
|
command: |
|
|
@@ -18,7 +18,7 @@ jobs:
|
|
|
18
18
|
bundle clean
|
|
19
19
|
fi
|
|
20
20
|
- save_cache:
|
|
21
|
-
key: v1-gems-ruby-
|
|
21
|
+
key: v1-gems-ruby-3.0.6-{{ checksum "delayed_job_groups.gemspec" }}-{{ checksum "Gemfile" }}
|
|
22
22
|
paths:
|
|
23
23
|
- "vendor/bundle"
|
|
24
24
|
- "gemfiles/vendor/bundle"
|
|
@@ -32,7 +32,7 @@ jobs:
|
|
|
32
32
|
ruby_version:
|
|
33
33
|
type: string
|
|
34
34
|
docker:
|
|
35
|
-
- image:
|
|
35
|
+
- image: cimg/ruby:<< parameters.ruby_version >>
|
|
36
36
|
environment:
|
|
37
37
|
CIRCLE_TEST_REPORTS: "test-results"
|
|
38
38
|
BUNDLE_GEMFILE: << parameters.gemfile >>
|
|
@@ -69,13 +69,9 @@ workflows:
|
|
|
69
69
|
matrix:
|
|
70
70
|
parameters:
|
|
71
71
|
gemfile:
|
|
72
|
-
- "gemfiles/rails_5.2.gemfile"
|
|
73
|
-
- "gemfiles/rails_6.0.gemfile"
|
|
74
72
|
- "gemfiles/rails_6.1.gemfile"
|
|
73
|
+
- "gemfiles/rails_7.0.gemfile"
|
|
75
74
|
ruby_version:
|
|
76
|
-
- "
|
|
77
|
-
- "
|
|
78
|
-
- "3.
|
|
79
|
-
exclude:
|
|
80
|
-
- gemfile: "gemfiles/rails_5.2.gemfile"
|
|
81
|
-
ruby_version: "3.0.0"
|
|
75
|
+
- "3.0.6"
|
|
76
|
+
- "3.1.4"
|
|
77
|
+
- "3.2.2"
|
data/.github/CODEOWNERS
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
* @jturkel @salsify/infrastructure-services
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
data/Appraisals
CHANGED
|
@@ -1,17 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
appraise 'rails-
|
|
4
|
-
gem '
|
|
5
|
-
gem '
|
|
6
|
-
gem 'activesupport', '5.2.1'
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
appraise 'rails-6.0' do
|
|
10
|
-
gem 'activerecord', '6.0.3'
|
|
11
|
-
gem 'activesupport', '6.0.3'
|
|
3
|
+
appraise 'rails-6.1' do
|
|
4
|
+
gem 'activerecord', '~> 6.1.5'
|
|
5
|
+
gem 'activesupport', '~> 6.1.5'
|
|
12
6
|
end
|
|
13
7
|
|
|
14
|
-
appraise 'rails-
|
|
15
|
-
gem 'activerecord', '
|
|
16
|
-
gem 'activesupport', '
|
|
8
|
+
appraise 'rails-7.0' do
|
|
9
|
+
gem 'activerecord', '~> 7.0.2'
|
|
10
|
+
gem 'activesupport', '~> 7.0.2'
|
|
17
11
|
end
|
data/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
|
+
|
|
3
|
+
### 0.9.0
|
|
4
|
+
- Add a `CompleteStuckJobGroupsJob`, which can be run periodically to close "stuck" job groups
|
|
5
|
+
- Drop support for Ruby 2.7
|
|
6
|
+
- Drop support for Rails 6.0
|
|
7
|
+
|
|
8
|
+
### 0.8.0
|
|
9
|
+
- Drop support for ruby < 2.7
|
|
10
|
+
- Add support for ruby 3.1
|
|
11
|
+
- Drop Rails 5.2
|
|
12
|
+
- Add Rails 7.0
|
|
13
|
+
|
|
2
14
|
### 0.7.0
|
|
3
15
|
* Add support for ruby 3
|
|
4
16
|
* Drop support for ruby < 2.6
|
data/README.md
CHANGED
|
@@ -49,7 +49,7 @@ Creating a job group and queueing some jobs:
|
|
|
49
49
|
```ruby
|
|
50
50
|
job_group = Delayed::JobGroups::JobGroup.create!
|
|
51
51
|
|
|
52
|
-
# JobGroup#enqueue has the same signature as Delayed::Job.enqueue
|
|
52
|
+
# JobGroup#enqueue has the same signature as Delayed::Job.enqueue
|
|
53
53
|
# i.e. it takes a job and an optional hash of options.
|
|
54
54
|
job_group.enqueue(MyJob.new('some arg'), queue: 'general')
|
|
55
55
|
job_group.enqueue(MyJob.new('some other arg'), queue: 'general', priority: 10)
|
|
@@ -62,7 +62,7 @@ Registering a job to run after all jobs in the job group have completed:
|
|
|
62
62
|
|
|
63
63
|
```ruby
|
|
64
64
|
# We can optionally pass options that will be used when queueing the on completion job
|
|
65
|
-
job_group = Delayed::JobGroups::JobGroup.create!(on_completion_job: MyCompletionJob.new,
|
|
65
|
+
job_group = Delayed::JobGroups::JobGroup.create!(on_completion_job: MyCompletionJob.new,
|
|
66
66
|
on_completion_job_options: { queue: 'general' })
|
|
67
67
|
```
|
|
68
68
|
|
|
@@ -70,7 +70,7 @@ Registering a job to run if the job group is canceled or fails:
|
|
|
70
70
|
|
|
71
71
|
```ruby
|
|
72
72
|
# We can optionally pass options that will be used when queueing the on cancellation job
|
|
73
|
-
job_group = Delayed::JobGroups::JobGroup.create!(on_cancellation_job: MyCancellationJob.new,
|
|
73
|
+
job_group = Delayed::JobGroups::JobGroup.create!(on_cancellation_job: MyCancellationJob.new,
|
|
74
74
|
on_cancellation_job_options: { queue: 'general' })
|
|
75
75
|
```
|
|
76
76
|
|
|
@@ -81,9 +81,9 @@ Block and unblock jobs in a job group:
|
|
|
81
81
|
job_group = Delayed::JobGroups::JobGroup.create!(blocked: true)
|
|
82
82
|
job_group.enqueue(MyJob.new('some arg'), queue: 'general')
|
|
83
83
|
job_group.mark_queueing_complete
|
|
84
|
-
|
|
84
|
+
|
|
85
85
|
# Do more stuff...
|
|
86
|
-
|
|
86
|
+
|
|
87
87
|
# Unblock the JobGroup so its jobs can run
|
|
88
88
|
job_group.unblock
|
|
89
89
|
```
|
|
@@ -92,19 +92,32 @@ Cancel a job group:
|
|
|
92
92
|
|
|
93
93
|
```ruby
|
|
94
94
|
job_group = Delayed::JobGroups::JobGroup.create!
|
|
95
|
-
|
|
95
|
+
|
|
96
96
|
# Do more stuff...
|
|
97
|
-
|
|
97
|
+
|
|
98
98
|
job_group.cancel
|
|
99
99
|
```
|
|
100
100
|
|
|
101
101
|
Configuration to allow failed jobs not to cancel the group
|
|
102
102
|
```ruby
|
|
103
103
|
# We can optionally pass options that will allow jobs to fail without cancelling the group.
|
|
104
|
-
# This also allows the on_completion job to fire once all jobs have either succeeded or failed.
|
|
104
|
+
# This also allows the on_completion job to fire once all jobs have either succeeded or failed.
|
|
105
105
|
job_group = Delayed::JobGroups::JobGroup.create!(failure_cancels_group: false)
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
+
### Maintenance
|
|
109
|
+
|
|
110
|
+
It's possible to end up in a state where all jobs in a group have been completed, but the completion job has not run.
|
|
111
|
+
This is due a race condition where the final job in a group is completed, and the worker running it is terminated before
|
|
112
|
+
the completion job can be enqueued.
|
|
113
|
+
|
|
114
|
+
As a remedy for the above scenario, a job is provided which cleans up any job groups in this state. It is recommended to
|
|
115
|
+
run this job periodically (for example in a cron job), especially in high-thoughput applications.
|
|
116
|
+
|
|
117
|
+
```ruby
|
|
118
|
+
Delayed::JobGroups::CompleteStuckJobGroupsJob.enqueue
|
|
119
|
+
```
|
|
120
|
+
|
|
108
121
|
## Supported Platforms
|
|
109
122
|
|
|
110
123
|
* Only the Delayed Job Active Record backend is supported.
|
data/bin/console
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'bundler/setup'
|
|
5
|
+
require 'delayed_job_groups_plugin'
|
|
6
|
+
|
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
|
9
|
+
|
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
11
|
+
# require "pry"
|
|
12
|
+
# Pry.start
|
|
13
|
+
|
|
14
|
+
require 'irb'
|
|
15
|
+
IRB.start
|
data/delayed_job_groups.gemspec
CHANGED
|
@@ -14,11 +14,18 @@ Gem::Specification.new do |spec|
|
|
|
14
14
|
spec.homepage = 'https://github.com/salsify/delayed_job_groups_plugin'
|
|
15
15
|
spec.license = 'MIT'
|
|
16
16
|
|
|
17
|
+
if spec.respond_to?(:metadata)
|
|
18
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
|
19
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
20
|
+
else
|
|
21
|
+
raise 'RubyGems 2.0 or newer is required to set allowed_push_host.'
|
|
22
|
+
end
|
|
23
|
+
|
|
17
24
|
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
|
18
25
|
spec.test_files = Dir.glob('spec/**/*')
|
|
19
26
|
spec.require_paths = ['lib']
|
|
20
27
|
|
|
21
|
-
spec.required_ruby_version = '>=
|
|
28
|
+
spec.required_ruby_version = '>= 3.0'
|
|
22
29
|
|
|
23
30
|
spec.add_dependency 'delayed_job', '>= 4.1'
|
|
24
31
|
spec.add_dependency 'delayed_job_active_record', '>= 4.1'
|
|
@@ -27,9 +34,10 @@ Gem::Specification.new do |spec|
|
|
|
27
34
|
'for upgrade/installation notes.'
|
|
28
35
|
|
|
29
36
|
spec.add_development_dependency 'appraisal'
|
|
30
|
-
spec.add_dependency 'activerecord', '>=
|
|
37
|
+
spec.add_dependency 'activerecord', '>= 6.1', '< 7.1'
|
|
31
38
|
spec.add_development_dependency 'coveralls_reborn', '>= 0.18.0'
|
|
32
39
|
spec.add_development_dependency 'database_cleaner', '>= 1.2'
|
|
40
|
+
spec.add_development_dependency 'factory_bot_rails'
|
|
33
41
|
spec.add_development_dependency 'mime-types'
|
|
34
42
|
spec.add_development_dependency 'rake'
|
|
35
43
|
spec.add_development_dependency 'rspec', '~> 3'
|
data/gemfiles/rails_6.1.gemfile
CHANGED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Delayed
|
|
4
|
+
module JobGroups
|
|
5
|
+
class CompleteStuckJobGroupsJob
|
|
6
|
+
class << self
|
|
7
|
+
def enqueue(**kwargs)
|
|
8
|
+
Delayed::Job.enqueue(new, **kwargs)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def perform
|
|
13
|
+
Delayed::JobGroups::JobGroup.ready.with_no_open_jobs.find_each do |job_group|
|
|
14
|
+
job_group.check_for_completion(skip_pending_jobs_check: true)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -22,6 +22,11 @@ module Delayed
|
|
|
22
22
|
has_many :queued_jobs, -> { where(failed_at: nil, locked_by: nil) }, class_name: '::Delayed::Job',
|
|
23
23
|
dependent: :delete_all
|
|
24
24
|
|
|
25
|
+
scope :ready, -> { where(queueing_complete: true, blocked: false) }
|
|
26
|
+
scope :with_no_open_jobs, -> do
|
|
27
|
+
where("NOT EXISTS (#{Delayed::Job.where('delayed_jobs.job_group_id = delayed_job_groups.id').to_sql})")
|
|
28
|
+
end
|
|
29
|
+
|
|
25
30
|
def mark_queueing_complete
|
|
26
31
|
with_lock do
|
|
27
32
|
raise 'JobGroup has already completed queueing' if queueing_complete?
|
|
@@ -52,10 +57,14 @@ module Delayed
|
|
|
52
57
|
destroy
|
|
53
58
|
end
|
|
54
59
|
|
|
55
|
-
def
|
|
60
|
+
def check_for_completion(skip_pending_jobs_check: false)
|
|
61
|
+
self.class.check_for_completion(id, skip_pending_jobs_check: skip_pending_jobs_check)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def self.check_for_completion(job_group_id, skip_pending_jobs_check: false)
|
|
56
65
|
# Optimization to avoid loading and locking the JobGroup when the group
|
|
57
66
|
# still has pending jobs
|
|
58
|
-
return if has_pending_jobs?(job_group_id)
|
|
67
|
+
return if !skip_pending_jobs_check && has_pending_jobs?(job_group_id)
|
|
59
68
|
|
|
60
69
|
transaction do
|
|
61
70
|
# The first completed job to notice the job group's queue count has dropped to
|
|
@@ -5,6 +5,7 @@ require 'active_record'
|
|
|
5
5
|
require 'delayed_job'
|
|
6
6
|
require 'delayed_job_active_record'
|
|
7
7
|
require 'delayed/job_groups/compatibility'
|
|
8
|
+
require 'delayed/job_groups/complete_stuck_job_groups_job'
|
|
8
9
|
require 'delayed/job_groups/job_extensions'
|
|
9
10
|
require 'delayed/job_groups/job_group'
|
|
10
11
|
require 'delayed/job_groups/plugin'
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
describe Delayed::JobGroups::CompleteStuckJobGroupsJob do
|
|
4
|
+
describe "#perform" do
|
|
5
|
+
let(:job) { described_class.new }
|
|
6
|
+
|
|
7
|
+
let!(:blocked) { create(:job_group, blocked: true) }
|
|
8
|
+
let!(:not_queueing_complete) { create(:job_group, queueing_complete: false) }
|
|
9
|
+
let!(:ready_without_jobs) { create(:job_group, queueing_complete: true, blocked: false) }
|
|
10
|
+
let!(:ready_with_jobs) do
|
|
11
|
+
create(:job_group, queueing_complete: true, blocked: false).tap do |job_group|
|
|
12
|
+
create(:delayed_job, job_group: job_group)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
before do
|
|
17
|
+
allow(Delayed::JobGroups::JobGroup).to receive(:check_for_completion)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "checks all relevant job groups for completion" do
|
|
21
|
+
job.perform
|
|
22
|
+
|
|
23
|
+
expect(Delayed::JobGroups::JobGroup).to have_received(:check_for_completion)
|
|
24
|
+
.once
|
|
25
|
+
.with(ready_without_jobs.id, skip_pending_jobs_check: true)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
describe "#enqueue" do
|
|
30
|
+
before do
|
|
31
|
+
allow(Delayed::Job).to receive(:enqueue)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "enqueues the job" do
|
|
35
|
+
described_class.enqueue
|
|
36
|
+
|
|
37
|
+
expect(Delayed::Job).to have_received(:enqueue).with(instance_of(described_class))
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
describe "delayed job extensions" do
|
|
4
4
|
it "provides an optional job_group_id" do
|
|
5
|
-
job_group =
|
|
5
|
+
job_group = create(:job_group)
|
|
6
6
|
expect(Delayed::Job.new(job_group_id: job_group.id)).to be_valid
|
|
7
7
|
expect(Delayed::Job.new).to be_valid
|
|
8
8
|
end
|
|
@@ -10,9 +10,12 @@ describe Delayed::JobGroups::JobGroup do
|
|
|
10
10
|
let(:current_time) { Time.utc(2013) }
|
|
11
11
|
|
|
12
12
|
subject(:job_group) do
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
create(
|
|
14
|
+
:job_group,
|
|
15
|
+
on_completion_job: on_completion_job,
|
|
16
|
+
on_completion_job_options: on_completion_job_options,
|
|
17
|
+
blocked: blocked
|
|
18
|
+
)
|
|
16
19
|
end
|
|
17
20
|
|
|
18
21
|
before do
|
|
@@ -24,6 +27,27 @@ describe Delayed::JobGroups::JobGroup do
|
|
|
24
27
|
Timecop.return
|
|
25
28
|
end
|
|
26
29
|
|
|
30
|
+
describe "scopes" do
|
|
31
|
+
describe "ready" do
|
|
32
|
+
let!(:blocked) { create(:job_group, blocked: true) }
|
|
33
|
+
let!(:not_queueing_complete) { create(:job_group, queueing_complete: false) }
|
|
34
|
+
let!(:ready) { create(:job_group, queueing_complete: true, blocked: false) }
|
|
35
|
+
|
|
36
|
+
it "returns the expected job groups" do
|
|
37
|
+
expect(described_class.ready).to match_array(ready)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
describe "with_no_open_jobs" do
|
|
42
|
+
let!(:job_group_with_jobs) { create(:delayed_job).job_group }
|
|
43
|
+
let!(:job_group_without_jobs) { subject }
|
|
44
|
+
|
|
45
|
+
it "returns groups with no jobs" do
|
|
46
|
+
expect(described_class.with_no_open_jobs).to match_array(job_group_without_jobs)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
27
51
|
shared_examples "the job group was completed" do
|
|
28
52
|
it "queues the completion job" do
|
|
29
53
|
expect(Delayed::Job).to have_received(:enqueue).with(on_completion_job, on_completion_job_options)
|
|
@@ -76,7 +100,73 @@ describe Delayed::JobGroups::JobGroup do
|
|
|
76
100
|
end
|
|
77
101
|
end
|
|
78
102
|
|
|
79
|
-
describe "
|
|
103
|
+
describe "instance check_for_completion" do
|
|
104
|
+
let!(:job) { Delayed::Job.create!(job_group_id: job_group.id) }
|
|
105
|
+
|
|
106
|
+
before do
|
|
107
|
+
job_group.mark_queueing_complete
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
shared_context "complete job and check job group complete" do
|
|
111
|
+
before do
|
|
112
|
+
job.destroy!
|
|
113
|
+
job_group.check_for_completion
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
context "when no jobs exist" do
|
|
118
|
+
include_context "complete job and check job group complete"
|
|
119
|
+
|
|
120
|
+
it_behaves_like "the job group was completed"
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
context "when active jobs exist" do
|
|
124
|
+
before do
|
|
125
|
+
Delayed::JobGroups::JobGroup.check_for_completion(job_group.id)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
it_behaves_like "the job group was not completed"
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
context "when on failed jobs exist" do
|
|
132
|
+
before do
|
|
133
|
+
job.update!(failed_at: Time.now)
|
|
134
|
+
Delayed::JobGroups::JobGroup.check_for_completion(job_group.id)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
it_behaves_like "the job group was completed"
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
context "when there are no on_completion_job_options" do
|
|
141
|
+
let(:on_completion_job_options) { nil }
|
|
142
|
+
|
|
143
|
+
include_context "complete job and check job group complete"
|
|
144
|
+
|
|
145
|
+
it "queues the completion job with empty options" do
|
|
146
|
+
expect(Delayed::Job).to have_received(:enqueue).with(on_completion_job, {})
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
it "destroys the job group" do
|
|
150
|
+
expect(job_group).to have_been_destroyed
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
context "when there is no on_completion_job" do
|
|
155
|
+
let(:on_completion_job) { nil }
|
|
156
|
+
|
|
157
|
+
include_context "complete job and check job group complete"
|
|
158
|
+
|
|
159
|
+
it "doesn't queues the non-existent completion job" do
|
|
160
|
+
expect(Delayed::Job).not_to have_received(:enqueue)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
it "destroys the job group" do
|
|
164
|
+
expect(job_group).to have_been_destroyed
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
describe "class check_for_completion" do
|
|
80
170
|
let!(:job) { Delayed::Job.create!(job_group_id: job_group.id) }
|
|
81
171
|
|
|
82
172
|
before do
|
|
@@ -85,7 +175,7 @@ describe Delayed::JobGroups::JobGroup do
|
|
|
85
175
|
|
|
86
176
|
shared_context "complete job and check job group complete" do
|
|
87
177
|
before do
|
|
88
|
-
job.destroy
|
|
178
|
+
job.destroy!
|
|
89
179
|
Delayed::JobGroups::JobGroup.check_for_completion(job_group.id)
|
|
90
180
|
end
|
|
91
181
|
end
|
|
@@ -13,7 +13,7 @@ describe Delayed::JobGroups::Plugin do
|
|
|
13
13
|
Delayed::Worker.max_attempts = @old_max_attempts
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
let!(:job_group) {
|
|
16
|
+
let!(:job_group) { create(:job_group, on_completion_job: TestJobs::CompletionJob.new) }
|
|
17
17
|
|
|
18
18
|
it "runs the completion job after completing other jobs" do
|
|
19
19
|
job_group.enqueue(TestJobs::NoOpJob.new)
|
|
@@ -215,8 +215,11 @@ describe Delayed::JobGroups::Plugin do
|
|
|
215
215
|
|
|
216
216
|
context "when a cancellation job is provided" do
|
|
217
217
|
let!(:job_group) do
|
|
218
|
-
|
|
219
|
-
|
|
218
|
+
create(
|
|
219
|
+
:job_group,
|
|
220
|
+
on_completion_job: TestJobs::CompletionJob.new,
|
|
221
|
+
on_cancellation_job: TestJobs::CancellationJob.new
|
|
222
|
+
)
|
|
220
223
|
end
|
|
221
224
|
|
|
222
225
|
it "runs the cancellation job after a job error causes cancellation" do
|
|
@@ -262,7 +265,7 @@ describe Delayed::JobGroups::Plugin do
|
|
|
262
265
|
end
|
|
263
266
|
|
|
264
267
|
context "when a no completion job is provided" do
|
|
265
|
-
let!(:job_group) {
|
|
268
|
+
let!(:job_group) { create(:job_group) }
|
|
266
269
|
|
|
267
270
|
it "doesn't queue a non-existent completion job" do
|
|
268
271
|
job_group.enqueue(TestJobs::NoOpJob.new)
|
data/spec/spec_helper.rb
CHANGED
|
@@ -14,6 +14,7 @@ end
|
|
|
14
14
|
require 'rspec/its'
|
|
15
15
|
require 'database_cleaner'
|
|
16
16
|
require 'delayed_job_groups_plugin'
|
|
17
|
+
require 'factory_bot'
|
|
17
18
|
require 'yaml'
|
|
18
19
|
require 'timecop'
|
|
19
20
|
|
|
@@ -40,6 +41,7 @@ RSpec.configure do |config|
|
|
|
40
41
|
|
|
41
42
|
config.before(:suite) do
|
|
42
43
|
DatabaseCleaner.clean_with(:truncation)
|
|
44
|
+
FactoryBot.find_definitions
|
|
43
45
|
end
|
|
44
46
|
|
|
45
47
|
config.before do
|
|
@@ -53,4 +55,6 @@ RSpec.configure do |config|
|
|
|
53
55
|
config.after do
|
|
54
56
|
DatabaseCleaner.clean
|
|
55
57
|
end
|
|
58
|
+
|
|
59
|
+
config.include FactoryBot::Syntax::Methods
|
|
56
60
|
end
|
metadata
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
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: 0.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Joel Turkel
|
|
8
8
|
- Randy Burkes
|
|
9
|
-
autorequire:
|
|
9
|
+
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date:
|
|
12
|
+
date: 2023-09-21 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: delayed_job
|
|
@@ -59,20 +59,20 @@ dependencies:
|
|
|
59
59
|
requirements:
|
|
60
60
|
- - ">="
|
|
61
61
|
- !ruby/object:Gem::Version
|
|
62
|
-
version: '
|
|
62
|
+
version: '6.1'
|
|
63
63
|
- - "<"
|
|
64
64
|
- !ruby/object:Gem::Version
|
|
65
|
-
version: '7'
|
|
65
|
+
version: '7.1'
|
|
66
66
|
type: :runtime
|
|
67
67
|
prerelease: false
|
|
68
68
|
version_requirements: !ruby/object:Gem::Requirement
|
|
69
69
|
requirements:
|
|
70
70
|
- - ">="
|
|
71
71
|
- !ruby/object:Gem::Version
|
|
72
|
-
version: '
|
|
72
|
+
version: '6.1'
|
|
73
73
|
- - "<"
|
|
74
74
|
- !ruby/object:Gem::Version
|
|
75
|
-
version: '7'
|
|
75
|
+
version: '7.1'
|
|
76
76
|
- !ruby/object:Gem::Dependency
|
|
77
77
|
name: coveralls_reborn
|
|
78
78
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -101,6 +101,20 @@ dependencies:
|
|
|
101
101
|
- - ">="
|
|
102
102
|
- !ruby/object:Gem::Version
|
|
103
103
|
version: '1.2'
|
|
104
|
+
- !ruby/object:Gem::Dependency
|
|
105
|
+
name: factory_bot_rails
|
|
106
|
+
requirement: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - ">="
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '0'
|
|
111
|
+
type: :development
|
|
112
|
+
prerelease: false
|
|
113
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - ">="
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '0'
|
|
104
118
|
- !ruby/object:Gem::Dependency
|
|
105
119
|
name: mime-types
|
|
106
120
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -237,6 +251,7 @@ extensions: []
|
|
|
237
251
|
extra_rdoc_files: []
|
|
238
252
|
files:
|
|
239
253
|
- ".circleci/config.yml"
|
|
254
|
+
- ".github/CODEOWNERS"
|
|
240
255
|
- ".gitignore"
|
|
241
256
|
- ".rspec"
|
|
242
257
|
- ".rubocop.yml"
|
|
@@ -246,11 +261,12 @@ files:
|
|
|
246
261
|
- LICENSE.txt
|
|
247
262
|
- README.md
|
|
248
263
|
- Rakefile
|
|
264
|
+
- bin/console
|
|
249
265
|
- delayed_job_groups.gemspec
|
|
250
|
-
- gemfiles/rails_5.2.gemfile
|
|
251
|
-
- gemfiles/rails_6.0.gemfile
|
|
252
266
|
- gemfiles/rails_6.1.gemfile
|
|
267
|
+
- gemfiles/rails_7.0.gemfile
|
|
253
268
|
- lib/delayed/job_groups/compatibility.rb
|
|
269
|
+
- lib/delayed/job_groups/complete_stuck_job_groups_job.rb
|
|
254
270
|
- lib/delayed/job_groups/job_extensions.rb
|
|
255
271
|
- lib/delayed/job_groups/job_group.rb
|
|
256
272
|
- lib/delayed/job_groups/plugin.rb
|
|
@@ -262,17 +278,22 @@ files:
|
|
|
262
278
|
- lib/generators/delayed_job_groups_plugin/templates/migration.erb
|
|
263
279
|
- spec/db/database.yml
|
|
264
280
|
- spec/db/schema.rb
|
|
281
|
+
- spec/delayed/job_groups/complete_stuck_job_groups_job_spec.rb
|
|
265
282
|
- spec/delayed/job_groups/job_extensions_spec.rb
|
|
266
283
|
- spec/delayed/job_groups/job_group_spec.rb
|
|
267
284
|
- spec/delayed/job_groups/plugin_spec.rb
|
|
268
285
|
- spec/delayed/job_groups/yaml_loader_spec.rb
|
|
286
|
+
- spec/factories/delayed_jobs.rb
|
|
287
|
+
- spec/factories/job_groups.rb
|
|
269
288
|
- spec/spec_helper.rb
|
|
270
289
|
- spec/support/destroyed_model.rb
|
|
271
290
|
- spec/support/test_jobs.rb
|
|
272
291
|
homepage: https://github.com/salsify/delayed_job_groups_plugin
|
|
273
292
|
licenses:
|
|
274
293
|
- MIT
|
|
275
|
-
metadata:
|
|
294
|
+
metadata:
|
|
295
|
+
allowed_push_host: https://rubygems.org
|
|
296
|
+
rubygems_mfa_required: 'true'
|
|
276
297
|
post_install_message: See https://github.com/salsify/delayed_job_groups_plugin#installation
|
|
277
298
|
for upgrade/installation notes.
|
|
278
299
|
rdoc_options: []
|
|
@@ -282,24 +303,27 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
282
303
|
requirements:
|
|
283
304
|
- - ">="
|
|
284
305
|
- !ruby/object:Gem::Version
|
|
285
|
-
version: '
|
|
306
|
+
version: '3.0'
|
|
286
307
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
287
308
|
requirements:
|
|
288
309
|
- - ">="
|
|
289
310
|
- !ruby/object:Gem::Version
|
|
290
311
|
version: '0'
|
|
291
312
|
requirements: []
|
|
292
|
-
rubygems_version: 3.
|
|
293
|
-
signing_key:
|
|
313
|
+
rubygems_version: 3.3.26
|
|
314
|
+
signing_key:
|
|
294
315
|
specification_version: 4
|
|
295
316
|
summary: Delayed::Job job groups plugin
|
|
296
317
|
test_files:
|
|
318
|
+
- spec/db/database.yml
|
|
319
|
+
- spec/db/schema.rb
|
|
320
|
+
- spec/delayed/job_groups/complete_stuck_job_groups_job_spec.rb
|
|
321
|
+
- spec/delayed/job_groups/job_extensions_spec.rb
|
|
322
|
+
- spec/delayed/job_groups/job_group_spec.rb
|
|
323
|
+
- spec/delayed/job_groups/plugin_spec.rb
|
|
324
|
+
- spec/delayed/job_groups/yaml_loader_spec.rb
|
|
325
|
+
- spec/factories/delayed_jobs.rb
|
|
326
|
+
- spec/factories/job_groups.rb
|
|
297
327
|
- spec/spec_helper.rb
|
|
298
328
|
- spec/support/destroyed_model.rb
|
|
299
329
|
- spec/support/test_jobs.rb
|
|
300
|
-
- spec/delayed/job_groups/plugin_spec.rb
|
|
301
|
-
- spec/delayed/job_groups/job_group_spec.rb
|
|
302
|
-
- spec/delayed/job_groups/yaml_loader_spec.rb
|
|
303
|
-
- spec/delayed/job_groups/job_extensions_spec.rb
|
|
304
|
-
- spec/db/schema.rb
|
|
305
|
-
- spec/db/database.yml
|