good_job 0.6.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/CHANGELOG.md +20 -0
- data/README.md +88 -9
- data/lib/active_job/queue_adapters/good_job_adapter.rb +12 -6
- data/lib/good_job.rb +3 -1
- data/lib/good_job/adapter.rb +26 -6
- data/lib/good_job/cli.rb +20 -2
- data/lib/good_job/job.rb +65 -21
- data/lib/good_job/lockable.rb +7 -2
- data/lib/good_job/performer.rb +12 -0
- data/lib/good_job/scheduler.rb +8 -8
- data/lib/good_job/version.rb +1 -1
- metadata +33 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bddc60fa297293af01b1334d4adf51981bfb8b314ba4577a7b3bff1d7385bd5a
|
4
|
+
data.tar.gz: 3533f21ffee4dba606558fa0fadd1f37a943aa285b4f6bebed4bb3b89045b61e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 74c0d3df2df08e78d06baa8982401d633af536277231a17d87687ff3c7ba1553d4123f7c1ce49ae23094bd781db5ee23eb9584eeba94aae49fb8cc83da0c68e0
|
7
|
+
data.tar.gz: 7df20f03621cb66b864a2c2950e4a11f60199cd4954ad5a622a1631254fa04bab1243ee376567600a2aed294a8c14fa9802fd1024855dc7d473a7ff8f7166515
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,25 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [v0.9.0](https://github.com/bensheldon/good_job/tree/v0.9.0) (2020-07-20)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v0.6.0...v0.9.0)
|
6
|
+
|
7
|
+
**Closed issues:**
|
8
|
+
|
9
|
+
- Always store a default priority \(0\) and scheduled\_at\(Time.current\) [\#30](https://github.com/bensheldon/good_job/issues/30)
|
10
|
+
- Add a job timeout configuration to time out jobs that have run too long [\#19](https://github.com/bensheldon/good_job/issues/19)
|
11
|
+
|
12
|
+
**Merged pull requests:**
|
13
|
+
|
14
|
+
- Allow preservation of finished job records [\#46](https://github.com/bensheldon/good_job/pull/46) ([bensheldon](https://github.com/bensheldon))
|
15
|
+
- Run Github Action tests on PRs from forks [\#44](https://github.com/bensheldon/good_job/pull/44) ([bensheldon](https://github.com/bensheldon))
|
16
|
+
- Fix Rubygems homepage URL [\#43](https://github.com/bensheldon/good_job/pull/43) ([joshmn](https://github.com/joshmn))
|
17
|
+
- Move where\(scheduled\_at: Time.current\) into dynamic part of GoodJob::Job::Performer [\#42](https://github.com/bensheldon/good_job/pull/42) ([bensheldon](https://github.com/bensheldon))
|
18
|
+
- Replace Adapter inline boolean kwarg with execution\_mode instead [\#41](https://github.com/bensheldon/good_job/pull/41) ([bensheldon](https://github.com/bensheldon))
|
19
|
+
- Add more examples to Readme [\#39](https://github.com/bensheldon/good_job/pull/39) ([bensheldon](https://github.com/bensheldon))
|
20
|
+
- Add additional Rubocops and lint [\#38](https://github.com/bensheldon/good_job/pull/38) ([bensheldon](https://github.com/bensheldon))
|
21
|
+
- Always store a default queue\_name, priority and scheduled\_at; index by queue\_name and scheduled\_at [\#37](https://github.com/bensheldon/good_job/pull/37) ([bensheldon](https://github.com/bensheldon))
|
22
|
+
|
3
23
|
## [v0.6.0](https://github.com/bensheldon/good_job/tree/v0.6.0) (2020-07-15)
|
4
24
|
|
5
25
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v0.5.0...v0.6.0)
|
data/README.md
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
# GoodJob
|
2
2
|
|
3
|
-
GoodJob is a multithreaded, Postgres-based ActiveJob backend for Ruby on Rails.
|
3
|
+
GoodJob is a multithreaded, Postgres-based, ActiveJob backend for Ruby on Rails.
|
4
4
|
|
5
|
-
Inspired by [Delayed::Job](https://github.com/collectiveidea/delayed_job) and [Que](https://github.com/que-rb/que), GoodJob
|
5
|
+
**Inspired by [Delayed::Job](https://github.com/collectiveidea/delayed_job) and [Que](https://github.com/que-rb/que), GoodJob is designed for maximum compatibility with Ruby on Rails, ActiveJob, and Postgres to be simple and performant for most workloads.**
|
6
6
|
|
7
|
-
-
|
8
|
-
-
|
9
|
-
-
|
10
|
-
-
|
7
|
+
- **Designed for ActiveJob.** Complete support for [async, queues, delays, priorities, timeouts, and retries](https://edgeguides.rubyonrails.org/active_job_basics.html) with near-zero configuration.
|
8
|
+
- **Built for Rails.** Fully adopts Ruby on Rails [threading and code execution guidelines](https://guides.rubyonrails.org/threading_and_code_execution.html) with [Concurrent::Ruby](https://github.com/ruby-concurrency/concurrent-ruby).
|
9
|
+
- **Backed by Postgres.** Relies upon Postgres integrity and session-level Advisory Locks to provide run-once safety and stay within the limits of `schema.rb`.
|
10
|
+
- **For most workloads.** Targets full-stack teams, economy-minded solo developers, and applications that enqueue less than 1-million jobs/day.
|
11
11
|
|
12
12
|
## Installation
|
13
13
|
|
@@ -43,6 +43,12 @@ $ bundle install
|
|
43
43
|
t.integer :priority
|
44
44
|
t.jsonb :serialized_params
|
45
45
|
t.timestamp :scheduled_at
|
46
|
+
t.timestamp :performed_at
|
47
|
+
t.timestamp :finished_at
|
48
|
+
t.text :error
|
49
|
+
|
50
|
+
add_index :good_jobs, :scheduled_at, where: "(finished_at IS NULL)"
|
51
|
+
add_index :good_jobs, [:queue_name, :scheduled_at], where: "(finished_at IS NULL)"
|
46
52
|
end
|
47
53
|
end
|
48
54
|
end
|
@@ -64,13 +70,18 @@ $ bundle install
|
|
64
70
|
|
65
71
|
```ruby
|
66
72
|
# config/environments/development.rb
|
67
|
-
config.active_job.queue_adapter = GoodJob::Adapter.new(
|
73
|
+
config.active_job.queue_adapter = GoodJob::Adapter.new(execution_mode: :inline)
|
68
74
|
|
69
75
|
# config/environments/test.rb
|
70
|
-
config.active_job.queue_adapter = GoodJob::Adapter.new(
|
76
|
+
config.active_job.queue_adapter = GoodJob::Adapter.new(execution_mode: :inline)
|
71
77
|
|
72
78
|
# config/environments/production.rb
|
73
|
-
config.active_job.queue_adapter = GoodJob::Adapter.new
|
79
|
+
config.active_job.queue_adapter = GoodJob::Adapter.new(execution_mode: :external)
|
80
|
+
```
|
81
|
+
|
82
|
+
1. Queue your job 🎉:
|
83
|
+
```ruby
|
84
|
+
YourJob.set(queue: :some_queue, wait: 5.minutes, priority: 10).perform_later
|
74
85
|
```
|
75
86
|
|
76
87
|
1. In production, the scheduler is designed to run in its own process:
|
@@ -90,6 +101,25 @@ $ bundle install
|
|
90
101
|
# [--queues=queue1,queue2] # Queues to work from. Separate multiple queues with commas (default: *)
|
91
102
|
# [--poll-interval=N] # Interval between polls for available jobs in seconds (default: 1)
|
92
103
|
```
|
104
|
+
|
105
|
+
### Taking advantage of ActiveJob
|
106
|
+
|
107
|
+
ActiveJob has a rich set of built-in functionality for timeouts, error handling, and retrying. For example:
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
class ApplicationJob < ActiveJob::Base
|
111
|
+
# Retry errors an infinite number of times with exponential back-off
|
112
|
+
retry_on StandardError, wait: :exponentially_longer, attempts: Float::INFINITY
|
113
|
+
|
114
|
+
# Timeout jobs after 10 minutes
|
115
|
+
JobTimeoutError = Class.new(StandardError)
|
116
|
+
around_perform do |_job, block|
|
117
|
+
Timeout.timeout(10.minutes, JobTimeoutError) do
|
118
|
+
block.call
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
```
|
93
123
|
|
94
124
|
### Configuring Job Execution Threads
|
95
125
|
|
@@ -102,6 +132,55 @@ GoodJob executes enqueued jobs using threads. There is a lot than can be said ab
|
|
102
132
|
3. `$ RAILS_MAX_THREADS=4 bundle exec good_job`
|
103
133
|
4. Implicitly via Rails's database connection pool size (`ActiveRecord::Base.connection_pool.size`)
|
104
134
|
|
135
|
+
### Migrating to GoodJob from a different ActiveJob backend
|
136
|
+
|
137
|
+
If your application is already using an ActiveJob backend, you will need to install GoodJob to enqueue and perform newly created jobs _and_ finish performing pre-existing jobs on the previous backend.
|
138
|
+
|
139
|
+
1. Enqueue newly created jobs on GoodJob either entirely by setting `ActiveJob::Base.queue_adapter = :good_job` or progressively via individual job classes:
|
140
|
+
|
141
|
+
```ruby
|
142
|
+
# jobs/specific_job.rb
|
143
|
+
class SpecificJob < ApplicationJob
|
144
|
+
self.queue_adapter = :good_job
|
145
|
+
# ...
|
146
|
+
end
|
147
|
+
```
|
148
|
+
|
149
|
+
1. Continue running executors for both backends. For example, on Heroku it's possible to run [two processes](https://help.heroku.com/CTFS2TJK/how-do-i-run-multiple-processes-on-a-dyno) within the same dyno:
|
150
|
+
```procfile
|
151
|
+
# Procfile
|
152
|
+
# ...
|
153
|
+
worker: bundle exec que ./config/environment.rb & bundle exec good_job & wait -n
|
154
|
+
```
|
155
|
+
|
156
|
+
1. Once you are confident that no unperformed jobs remain in the previous ActiveJob backend, code and configuration for that backend can be completely removed.
|
157
|
+
|
158
|
+
### Monitoring and preserving worked jobs
|
159
|
+
|
160
|
+
GoodJob is fully instrumented with [`ActiveSupport::Notifications`](https://edgeguides.rubyonrails.org/active_support_instrumentation.html#introduction-to-instrumentation).
|
161
|
+
|
162
|
+
By default, GoodJob will delete job records after they are run, regardless of whether they succeed or not (raising a kind of `StandardError`), unless they are interrupted (raising a kind of `Exception`).
|
163
|
+
|
164
|
+
To preserve job records for later inspection, set an initializer:
|
165
|
+
|
166
|
+
```ruby
|
167
|
+
# config/initializers/good_job.rb
|
168
|
+
GoodJob.preserve_job_records = true
|
169
|
+
```
|
170
|
+
|
171
|
+
It is also necessary to delete these preserved jobs from the database after a certain time period:
|
172
|
+
|
173
|
+
- For example, in a Rake task:
|
174
|
+
|
175
|
+
```ruby
|
176
|
+
# GoodJob::Job.finished(1.day.ago).delete_all
|
177
|
+
```
|
178
|
+
- For example, using the `good_job` command-line utility:
|
179
|
+
|
180
|
+
```bash
|
181
|
+
$ bundle exec good_job cleanup_preserved_jobs --before-seconds-ago=86400
|
182
|
+
```
|
183
|
+
|
105
184
|
## Development
|
106
185
|
|
107
186
|
To run tests:
|
@@ -1,12 +1,18 @@
|
|
1
1
|
module ActiveJob
|
2
2
|
module QueueAdapters
|
3
3
|
class GoodJobAdapter < GoodJob::Adapter
|
4
|
-
def initialize
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
4
|
+
def initialize(execution_mode: nil)
|
5
|
+
execution_mode = if execution_mode
|
6
|
+
execution_mode
|
7
|
+
elsif ENV['GOOD_JOB_EXECUTION_MODE'].present?
|
8
|
+
ENV['GOOD_JOB_EXECUTION_MODE'].to_sym
|
9
|
+
elsif Rails.env.development? || Rails.env.test?
|
10
|
+
:inline
|
11
|
+
else
|
12
|
+
:external
|
13
|
+
end
|
14
|
+
|
15
|
+
super(execution_mode: execution_mode)
|
10
16
|
end
|
11
17
|
end
|
12
18
|
end
|
data/lib/good_job.rb
CHANGED
@@ -4,13 +4,15 @@ require 'good_job/railtie'
|
|
4
4
|
require 'good_job/logging'
|
5
5
|
require 'good_job/lockable'
|
6
6
|
require 'good_job/job'
|
7
|
-
require
|
7
|
+
require 'good_job/scheduler'
|
8
8
|
require 'good_job/adapter'
|
9
9
|
require 'good_job/pg_locks'
|
10
|
+
require 'good_job/performer'
|
10
11
|
|
11
12
|
require 'active_job/queue_adapters/good_job_adapter'
|
12
13
|
|
13
14
|
module GoodJob
|
15
|
+
mattr_accessor :preserve_job_records, default: false
|
14
16
|
include Logging
|
15
17
|
|
16
18
|
ActiveSupport.run_load_hooks(:good_job, self)
|
data/lib/good_job/adapter.rb
CHANGED
@@ -1,7 +1,18 @@
|
|
1
1
|
module GoodJob
|
2
2
|
class Adapter
|
3
|
-
|
4
|
-
|
3
|
+
EXECUTION_MODES = [:inline, :external].freeze # TODO: async
|
4
|
+
|
5
|
+
def initialize(execution_mode: nil, inline: false)
|
6
|
+
if inline
|
7
|
+
ActiveSupport::Deprecation.warn('GoodJob::Adapter#new(inline: true) is deprecated; use GoodJob::Adapter.new(execution_mode: :inline) instead')
|
8
|
+
@execution_mode = :inline
|
9
|
+
elsif execution_mode
|
10
|
+
raise ArgumentError, "execution_mode: must be one of #{EXECUTION_MODES.join(', ')}." unless EXECUTION_MODES.include?(execution_mode)
|
11
|
+
|
12
|
+
@execution_mode = execution_mode
|
13
|
+
else
|
14
|
+
@execution_mode = :external
|
15
|
+
end
|
5
16
|
end
|
6
17
|
|
7
18
|
def enqueue(active_job)
|
@@ -11,11 +22,11 @@ module GoodJob
|
|
11
22
|
def enqueue_at(active_job, timestamp)
|
12
23
|
good_job = GoodJob::Job.enqueue(
|
13
24
|
active_job,
|
14
|
-
scheduled_at: timestamp ? Time.at(timestamp) : nil,
|
15
|
-
create_with_advisory_lock:
|
25
|
+
scheduled_at: timestamp ? Time.zone.at(timestamp) : nil,
|
26
|
+
create_with_advisory_lock: execute_inline?
|
16
27
|
)
|
17
28
|
|
18
|
-
if
|
29
|
+
if execute_inline?
|
19
30
|
begin
|
20
31
|
good_job.perform
|
21
32
|
ensure
|
@@ -30,8 +41,17 @@ module GoodJob
|
|
30
41
|
nil
|
31
42
|
end
|
32
43
|
|
44
|
+
def execute_inline?
|
45
|
+
@execution_mode == :inline
|
46
|
+
end
|
47
|
+
|
33
48
|
def inline?
|
34
|
-
|
49
|
+
ActiveSupport::Deprecation.warn('GoodJob::Adapter::inline? is deprecated; use GoodJob::Adapter::execute_inline? instead')
|
50
|
+
execute_inline?
|
51
|
+
end
|
52
|
+
|
53
|
+
def execute_externally?
|
54
|
+
@execution_mode == :external
|
35
55
|
end
|
36
56
|
end
|
37
57
|
end
|
data/lib/good_job/cli.rb
CHANGED
@@ -36,11 +36,16 @@ module GoodJob
|
|
36
36
|
ENV['GOOD_JOB_POLL_INTERVAL']
|
37
37
|
).to_i
|
38
38
|
|
39
|
-
job_query = GoodJob::Job.all
|
39
|
+
job_query = GoodJob::Job.all.priority_ordered
|
40
40
|
queue_names_without_all = queue_names.reject { |q| q == '*' }
|
41
41
|
job_query = job_query.where(queue_name: queue_names_without_all) unless queue_names_without_all.size.zero?
|
42
42
|
|
43
|
-
|
43
|
+
performer_method = if GoodJob.preserve_job_records
|
44
|
+
:perform_with_advisory_lock_and_preserve_job_records
|
45
|
+
else
|
46
|
+
:perform_with_advisory_lock_and_destroy_job_records
|
47
|
+
end
|
48
|
+
job_performer = GoodJob::Performer.new(job_query, performer_method)
|
44
49
|
|
45
50
|
$stdout.puts "GoodJob worker starting with max_threads=#{max_threads} on queues=#{queue_names.join(',')}"
|
46
51
|
|
@@ -68,6 +73,19 @@ module GoodJob
|
|
68
73
|
$stdout.puts "GoodJob's jobs finished, exiting..."
|
69
74
|
end
|
70
75
|
|
76
|
+
desc :cleanup_preserved_jobs, "Delete preserved job records"
|
77
|
+
method_option :before_seconds_ago,
|
78
|
+
type: :numeric,
|
79
|
+
default: 24 * 60 * 60,
|
80
|
+
desc: "Delete records finished more than this many seconds ago"
|
81
|
+
def cleanup_preserved_jobs
|
82
|
+
require RAILS_ENVIRONMENT_RB
|
83
|
+
|
84
|
+
timestamp = Time.current - options[:before_seconds_ago]
|
85
|
+
result = GoodJob::Job.finished(timestamp).delete_all
|
86
|
+
$stdout.puts "Deleted #{result} preserved #{'job'.pluralize(result)} finished before #{timestamp}."
|
87
|
+
end
|
88
|
+
|
71
89
|
default_task :start
|
72
90
|
end
|
73
91
|
end
|
data/lib/good_job/job.rb
CHANGED
@@ -2,39 +2,56 @@ module GoodJob
|
|
2
2
|
class Job < ActiveRecord::Base
|
3
3
|
include Lockable
|
4
4
|
|
5
|
-
|
5
|
+
PreviouslyPerformedError = Class.new(StandardError)
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
scope :to_performer, -> { Performer.new(self) }
|
7
|
+
DEFAULT_QUEUE_NAME = 'default'.freeze
|
8
|
+
DEFAULT_PRIORITY = 0
|
10
9
|
|
11
|
-
|
12
|
-
def initialize(query)
|
13
|
-
@query = query
|
14
|
-
end
|
10
|
+
self.table_name = 'good_jobs'.freeze
|
15
11
|
|
16
|
-
|
17
|
-
|
12
|
+
scope :unfinished, (lambda do
|
13
|
+
if column_names.include?('finished_at')
|
14
|
+
where(finished_at: nil)
|
15
|
+
else
|
16
|
+
ActiveSupport::Deprecation.warn('GoodJob expects a good_jobs.finished_at column to exist. Please see the GoodJob README.md for migration instructions.')
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
end)
|
20
|
+
scope :only_scheduled, -> { where(arel_table['scheduled_at'].lteq(Time.current)).or(where(scheduled_at: nil)) }
|
21
|
+
scope :priority_ordered, -> { order(priority: :desc) }
|
22
|
+
scope :finished, ->(timestamp = nil) { timestamp ? where(arel_table['finished_at'].lteq(timestamp)) : where.not(finished_at: nil) }
|
18
23
|
|
19
|
-
|
20
|
-
|
21
|
-
|
24
|
+
def self.perform_with_advisory_lock(destroy_after: !GoodJob.preserve_job_records)
|
25
|
+
good_job = nil
|
26
|
+
result = nil
|
27
|
+
error = nil
|
22
28
|
|
23
|
-
|
24
|
-
|
29
|
+
unfinished.only_scheduled.limit(1).with_advisory_lock do |good_jobs|
|
30
|
+
good_job = good_jobs.first
|
31
|
+
break unless good_job
|
25
32
|
|
26
|
-
good_job
|
33
|
+
result, error = good_job.perform(destroy_after: destroy_after)
|
27
34
|
end
|
35
|
+
|
36
|
+
[good_job, result, error] if good_job
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.perform_with_advisory_lock_and_preserve_job_records
|
40
|
+
perform_with_advisory_lock(destroy_after: false)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.perform_with_advisory_lock_and_destroy_job_records
|
44
|
+
perform_with_advisory_lock(destroy_after: true)
|
28
45
|
end
|
29
46
|
|
30
47
|
def self.enqueue(active_job, scheduled_at: nil, create_with_advisory_lock: false)
|
31
48
|
good_job = nil
|
32
49
|
ActiveSupport::Notifications.instrument("enqueue_job.good_job", { active_job: active_job, scheduled_at: scheduled_at, create_with_advisory_lock: create_with_advisory_lock }) do |instrument_payload|
|
33
50
|
good_job = GoodJob::Job.new(
|
34
|
-
queue_name: active_job.queue_name,
|
35
|
-
priority: active_job.priority,
|
51
|
+
queue_name: active_job.queue_name.presence || DEFAULT_QUEUE_NAME,
|
52
|
+
priority: active_job.priority || DEFAULT_PRIORITY,
|
36
53
|
serialized_params: active_job.serialize,
|
37
|
-
scheduled_at: scheduled_at,
|
54
|
+
scheduled_at: scheduled_at || Time.current,
|
38
55
|
create_with_advisory_lock: create_with_advisory_lock
|
39
56
|
)
|
40
57
|
|
@@ -47,16 +64,43 @@ module GoodJob
|
|
47
64
|
good_job
|
48
65
|
end
|
49
66
|
|
50
|
-
def perform
|
67
|
+
def perform(destroy_after: true)
|
68
|
+
raise PreviouslyPerformedError, 'Cannot perform a job that has already been performed' if finished_at
|
69
|
+
|
70
|
+
result = nil
|
71
|
+
error = nil
|
72
|
+
|
51
73
|
ActiveSupport::Notifications.instrument("before_perform_job.good_job", { good_job: self })
|
74
|
+
self.performed_at = Time.current
|
75
|
+
save! unless destroy_after
|
76
|
+
|
52
77
|
ActiveSupport::Notifications.instrument("perform_job.good_job", { good_job: self }) do
|
53
78
|
params = serialized_params.merge(
|
54
79
|
"provider_job_id" => id
|
55
80
|
)
|
56
|
-
|
81
|
+
begin
|
82
|
+
result = ActiveJob::Base.execute(params)
|
83
|
+
rescue StandardError => e
|
84
|
+
error = e
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
if error.nil? && result.is_a?(Exception)
|
89
|
+
error = result
|
90
|
+
result = nil
|
91
|
+
end
|
57
92
|
|
93
|
+
error_message = "#{error.class}: #{error.message}" if error
|
94
|
+
self.error = error_message
|
95
|
+
self.finished_at = Time.current
|
96
|
+
|
97
|
+
if destroy_after
|
58
98
|
destroy!
|
99
|
+
else
|
100
|
+
save!
|
59
101
|
end
|
102
|
+
|
103
|
+
[result, error]
|
60
104
|
end
|
61
105
|
end
|
62
106
|
end
|
data/lib/good_job/lockable.rb
CHANGED
@@ -37,14 +37,17 @@ module GoodJob
|
|
37
37
|
scope :owns_advisory_locked, -> { joins_advisory_locks.where('"pg_locks"."pid" = pg_backend_pid()') }
|
38
38
|
|
39
39
|
attr_accessor :create_with_advisory_lock
|
40
|
+
|
40
41
|
after_create -> { advisory_lock }, if: :create_with_advisory_lock
|
41
42
|
end
|
42
43
|
|
43
44
|
class_methods do
|
44
|
-
def with_advisory_lock
|
45
|
+
def with_advisory_lock
|
46
|
+
raise ArgumentError, "Must provide a block" unless block_given?
|
47
|
+
|
45
48
|
records = advisory_lock.to_a
|
46
49
|
begin
|
47
|
-
|
50
|
+
yield(records)
|
48
51
|
ensure
|
49
52
|
records.each(&:advisory_unlock)
|
50
53
|
end
|
@@ -73,6 +76,8 @@ module GoodJob
|
|
73
76
|
end
|
74
77
|
|
75
78
|
def with_advisory_lock
|
79
|
+
raise ArgumentError, "Must provide a block" unless block_given?
|
80
|
+
|
76
81
|
advisory_lock!
|
77
82
|
yield
|
78
83
|
ensure
|
data/lib/good_job/scheduler.rb
CHANGED
@@ -59,21 +59,21 @@ module GoodJob
|
|
59
59
|
|
60
60
|
def create_thread
|
61
61
|
future = Concurrent::Future.new(args: [@performer], executor: @pool) do |performer|
|
62
|
-
|
63
|
-
Rails.application.executor.wrap {
|
64
|
-
|
62
|
+
output = nil
|
63
|
+
Rails.application.executor.wrap { output = performer.next }
|
64
|
+
output
|
65
65
|
end
|
66
66
|
future.add_observer(self, :task_observer)
|
67
67
|
future.execute
|
68
68
|
end
|
69
69
|
|
70
|
-
def timer_observer(time, executed_task,
|
71
|
-
ActiveSupport::Notifications.instrument("finished_timer_task.good_job", { result: executed_task, error:
|
70
|
+
def timer_observer(time, executed_task, thread_error)
|
71
|
+
ActiveSupport::Notifications.instrument("finished_timer_task.good_job", { result: executed_task, error: thread_error, time: time })
|
72
72
|
end
|
73
73
|
|
74
|
-
def task_observer(time,
|
75
|
-
ActiveSupport::Notifications.instrument("finished_job_task.good_job", { result:
|
76
|
-
create_thread if
|
74
|
+
def task_observer(time, output, thread_error)
|
75
|
+
ActiveSupport::Notifications.instrument("finished_job_task.good_job", { result: output, error: thread_error, time: time })
|
76
|
+
create_thread if output
|
77
77
|
end
|
78
78
|
end
|
79
79
|
end
|
data/lib/good_job/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: good_job
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Sheldon
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-07-
|
11
|
+
date: 2020-07-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -178,6 +178,34 @@ dependencies:
|
|
178
178
|
- - ">="
|
179
179
|
- !ruby/object:Gem::Version
|
180
180
|
version: '0'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: rubocop-performance
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - ">="
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '0'
|
188
|
+
type: :development
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - ">="
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '0'
|
195
|
+
- !ruby/object:Gem::Dependency
|
196
|
+
name: rubocop-rails
|
197
|
+
requirement: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - ">="
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: '0'
|
202
|
+
type: :development
|
203
|
+
prerelease: false
|
204
|
+
version_requirements: !ruby/object:Gem::Requirement
|
205
|
+
requirements:
|
206
|
+
- - ">="
|
207
|
+
- !ruby/object:Gem::Version
|
208
|
+
version: '0'
|
181
209
|
- !ruby/object:Gem::Dependency
|
182
210
|
name: rubocop-rspec
|
183
211
|
requirement: !ruby/object:Gem::Requirement
|
@@ -214,18 +242,19 @@ files:
|
|
214
242
|
- lib/good_job/job.rb
|
215
243
|
- lib/good_job/lockable.rb
|
216
244
|
- lib/good_job/logging.rb
|
245
|
+
- lib/good_job/performer.rb
|
217
246
|
- lib/good_job/pg_locks.rb
|
218
247
|
- lib/good_job/railtie.rb
|
219
248
|
- lib/good_job/scheduler.rb
|
220
249
|
- lib/good_job/version.rb
|
221
|
-
homepage: https://github.com/
|
250
|
+
homepage: https://github.com/bensheldon/good_job
|
222
251
|
licenses:
|
223
252
|
- MIT
|
224
253
|
metadata:
|
225
254
|
bug_tracker_uri: https://github.com/bensheldon/good_job/issues
|
226
255
|
changelog_uri: https://github.com/bensheldon/good_job/blob/master/CHANGELOG.md
|
227
256
|
documentation_uri: https://rdoc.info/github/bensheldon/good_job
|
228
|
-
homepage_uri: https://github.com/
|
257
|
+
homepage_uri: https://github.com/bensheldon/good_job
|
229
258
|
source_code_uri: https://github.com/bensheldon/good_job
|
230
259
|
post_install_message:
|
231
260
|
rdoc_options:
|