good_job 2.15.0 → 2.16.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +41 -1
- data/README.md +46 -0
- data/engine/app/assets/good_job/style.css +5 -0
- data/engine/app/helpers/good_job/application_helper.rb +1 -1
- data/engine/app/views/good_job/shared/_filter.erb +1 -1
- data/lib/good_job/adapter.rb +33 -4
- data/lib/good_job/configuration.rb +4 -0
- data/lib/good_job/version.rb +1 -1
- data/lib/good_job.rb +17 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1811646b013e9432de87917a2be838ee634902b22ecc942bae74a7fefab82487
|
4
|
+
data.tar.gz: 14a301ce871b2761484d1c40d71dc76a64ce40131b6c4f3ff5573c1a2201ee58
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f719e920a42b626b022ea6e90572344a660adf9fac9978fcde0ae98abbb0433d89a32af25f51e14a7250ffa2ffb128accc95fce9121630cc178b957f114e2fdf
|
7
|
+
data.tar.gz: 83265d3360d14d249303179d5ef506e39709ddf9fbbc87df99b42e5b6057d1e9acc0a59712e8b82f0d79df69b868a26b0f5de9ebf0e1adac2e421f0b1288b94f
|
data/CHANGELOG.md
CHANGED
@@ -1,11 +1,52 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [v2.16.0](https://github.com/bensheldon/good_job/tree/v2.16.0) (2022-06-17)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v2.15.2...v2.16.0)
|
6
|
+
|
7
|
+
**Closed issues:**
|
8
|
+
|
9
|
+
- Upgrading zeitwerk to 2.6.0 causes a warning related to good\_job [\#616](https://github.com/bensheldon/good_job/issues/616)
|
10
|
+
|
11
|
+
**Merged pull requests:**
|
12
|
+
|
13
|
+
- Allow inline executor to respect scheduled jobs; deprecate old behavior. Add `GoodJob.perform_inline` [\#615](https://github.com/bensheldon/good_job/pull/615) ([bensheldon](https://github.com/bensheldon))
|
14
|
+
|
15
|
+
## [v2.15.2](https://github.com/bensheldon/good_job/tree/v2.15.2) (2022-06-17)
|
16
|
+
|
17
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v2.15.1...v2.15.2)
|
18
|
+
|
19
|
+
**Closed issues:**
|
20
|
+
|
21
|
+
- ActiveRecord::StatementInvalid PG::ProgramLimitExceeded: ERROR: index row size 3296 exceeds btree version 4 maximum 2704 for index [\#612](https://github.com/bensheldon/good_job/issues/612)
|
22
|
+
|
23
|
+
**Merged pull requests:**
|
24
|
+
|
25
|
+
- Zeitwerk ignore `lib/active_job` [\#617](https://github.com/bensheldon/good_job/pull/617) ([bensheldon](https://github.com/bensheldon))
|
26
|
+
|
27
|
+
## [v2.15.1](https://github.com/bensheldon/good_job/tree/v2.15.1) (2022-05-24)
|
28
|
+
|
29
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v2.15.0...v2.15.1)
|
30
|
+
|
31
|
+
**Closed issues:**
|
32
|
+
|
33
|
+
- dashboard/engine – i18n: Wrong translation scope [\#605](https://github.com/bensheldon/good_job/issues/605)
|
34
|
+
- Concurrency not properly putting jobs in the queue [\#603](https://github.com/bensheldon/good_job/issues/603)
|
35
|
+
- Some dashboard actions have a routing error [\#602](https://github.com/bensheldon/good_job/issues/602)
|
36
|
+
|
37
|
+
**Merged pull requests:**
|
38
|
+
|
39
|
+
- Fix/i18n status scopes [\#607](https://github.com/bensheldon/good_job/pull/607) ([Jay-Schneider](https://github.com/Jay-Schneider))
|
40
|
+
- Make "Live Polling" ToC entry clickable [\#606](https://github.com/bensheldon/good_job/pull/606) ([aried3r](https://github.com/aried3r))
|
41
|
+
- Update readme explaining Concurrency implementation and how to integrate Dashboard with API-only Rails apps [\#604](https://github.com/bensheldon/good_job/pull/604) ([bensheldon](https://github.com/bensheldon))
|
42
|
+
|
3
43
|
## [v2.15.0](https://github.com/bensheldon/good_job/tree/v2.15.0) (2022-05-18)
|
4
44
|
|
5
45
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v2.14.4...v2.15.0)
|
6
46
|
|
7
47
|
**Implemented enhancements:**
|
8
48
|
|
49
|
+
- Remove ability to destroy individual Executions from Dashboard; rename "Toggle" to "Inspect" everywhere [\#601](https://github.com/bensheldon/good_job/pull/601) ([bensheldon](https://github.com/bensheldon))
|
9
50
|
- Adds the ability to delete jobs on the dashboard; add `cleanup_discarded_jobs` option to retain discarded jobs during cleanup [\#597](https://github.com/bensheldon/good_job/pull/597) ([TAGraves](https://github.com/TAGraves))
|
10
51
|
- Dashboard: show more details about jobs [\#575](https://github.com/bensheldon/good_job/pull/575) ([bkeepers](https://github.com/bkeepers))
|
11
52
|
|
@@ -15,7 +56,6 @@
|
|
15
56
|
|
16
57
|
**Merged pull requests:**
|
17
58
|
|
18
|
-
- Remove ability to destroy individual Executions from Dashboard; rename "Toggle" to "Inspect" everywhere [\#601](https://github.com/bensheldon/good_job/pull/601) ([bensheldon](https://github.com/bensheldon))
|
19
59
|
- Disable ActiveRecord Connection Reaper in test [\#600](https://github.com/bensheldon/good_job/pull/600) ([bensheldon](https://github.com/bensheldon))
|
20
60
|
- Update README dashboard screenshot [\#599](https://github.com/bensheldon/good_job/pull/599) ([aried3r](https://github.com/aried3r))
|
21
61
|
|
data/README.md
CHANGED
@@ -39,7 +39,10 @@ For more of the story of GoodJob, read the [introductory blog post](https://isla
|
|
39
39
|
- [Configuration options](#configuration-options)
|
40
40
|
- [Global options](#global-options)
|
41
41
|
- [Dashboard](#dashboard)
|
42
|
+
- [API-only Rails applications](#api-only-rails-applications)
|
43
|
+
- [Live Polling](#live-polling)
|
42
44
|
- [ActiveJob concurrency](#activejob-concurrency)
|
45
|
+
- [How concurrency controls work](#how-concurrency-controls-work)
|
43
46
|
- [Cron-style repeating/recurring jobs](#cron-style-repeatingrecurring-jobs)
|
44
47
|
- [Updating](#updating)
|
45
48
|
- [Upgrading minor versions](#upgrading-minor-versions)
|
@@ -56,6 +59,7 @@ For more of the story of GoodJob, read the [introductory blog post](https://isla
|
|
56
59
|
- [Execute jobs async / in-process](#execute-jobs-async--in-process)
|
57
60
|
- [Migrate to GoodJob from a different ActiveJob backend](#migrate-to-goodjob-from-a-different-activejob-backend)
|
58
61
|
- [Monitor and preserve worked jobs](#monitor-and-preserve-worked-jobs)
|
62
|
+
- [Write tests](#write-tests)
|
59
63
|
- [PgBouncer compatibility](#pgbouncer-compatibility)
|
60
64
|
- [CLI HTTP health check probes](#cli-http-health-check-probes)
|
61
65
|
- [Contribute](#contribute)
|
@@ -273,6 +277,7 @@ Available configuration options are:
|
|
273
277
|
- `cleanup_preserved_jobs_before_seconds_ago` (integer) number of seconds to preserve jobs when using the `$ good_job cleanup_preserved_jobs` CLI command or calling `GoodJob.cleanup_preserved_jobs`. Defaults to `86400` (1 day). Can also be set with the environment variable `GOOD_JOB_CLEANUP_PRESERVED_JOBS_BEFORE_SECONDS_AGO`. _This configuration is only used when {GoodJob.preserve_job_records} is `true`._
|
274
278
|
- `cleanup_interval_jobs` (integer) Number of jobs a Scheduler will execute before cleaning up preserved jobs. Defaults to `nil`. Can also be set with the environment variable `GOOD_JOB_CLEANUP_INTERVAL_JOBS`.
|
275
279
|
- `cleanup_interval_seconds` (integer) Number of seconds a Scheduler will wait before cleaning up preserved jobs. Defaults to `nil`. Can also be set with the environment variable `GOOD_JOB_CLEANUP_INTERVAL_SECONDS`.
|
280
|
+
- `inline_execution_respects_schedule` (boolean) Opt-in to future behavior of inline execution respecting scheduled jobs. Defaults to `false`.
|
276
281
|
- `logger` ([Rails Logger](https://api.rubyonrails.org/classes/ActiveSupport/Logger.html)) lets you set a custom logger for GoodJob. It should be an instance of a Rails `Logger` (Default: `Rails.logger`).
|
277
282
|
- `preserve_job_records` (boolean) keeps job records in your database even after jobs are completed. (Default: `false`)
|
278
283
|
- `retry_on_unhandled_error` (boolean) causes jobs to be re-queued and retried if they raise an instance of `StandardError`. Be advised this may lead to jobs being repeated infinitely ([see below for more on retries](#retries)). Instances of `Exception`, like SIGINT, will *always* be retried, regardless of this attribute’s value. (Default: `true`)
|
@@ -368,6 +373,23 @@ GoodJob includes a Dashboard as a mountable `Rails::Engine`.
|
|
368
373
|
end
|
369
374
|
```
|
370
375
|
|
376
|
+
#### API-only Rails applications
|
377
|
+
|
378
|
+
API-only Rails applications may not have all of the required Rack middleware for the GoodJob Dashboard to function. To re-add the middlware:
|
379
|
+
|
380
|
+
```ruby
|
381
|
+
# config/application.rb
|
382
|
+
module MyApp
|
383
|
+
class Application < Rails::Application
|
384
|
+
#...
|
385
|
+
config.middleware.use Rack::MethodOverride
|
386
|
+
config.middleware.use ActionDispatch::Flash
|
387
|
+
config.middleware.use ActionDispatch::Cookies
|
388
|
+
config.middleware.use ActionDispatch::Session::CookieStore
|
389
|
+
end
|
390
|
+
end
|
391
|
+
```
|
392
|
+
|
371
393
|
#### Live Polling
|
372
394
|
|
373
395
|
The Dashboard can be set to automatically refresh by checking "Live Poll" in the Dashboard header, or by setting `?poll=10` with the interval in seconds (default 30 seconds).
|
@@ -420,6 +442,13 @@ job = MyJob.perform_later("Alice")
|
|
420
442
|
job.good_job_concurrency_key #=> "Unique-Alice"
|
421
443
|
```
|
422
444
|
|
445
|
+
#### How concurrency controls work
|
446
|
+
|
447
|
+
GoodJob's concurrency control strategy for `perform_limit` is "optimistic retry with an incremental backoff". The [code is readable](https://github.com/bensheldon/good_job/blob/main/lib/good_job/active_job_extensions/concurrency.rb).
|
448
|
+
|
449
|
+
- "Optimistic" meaning that the implementation's performance trade-off assumes that collisions are atypical (e.g. two users enqueue the same job at the same time) rather than regular (e.g. the system enqueues thousands of colliding jobs at the same time).
|
450
|
+
- "Retry with an incremental backoff" means that when `perform_limit` is exceeded, the job will raise a `GoodJob::ActiveJobExtensions::Concurrency::ConcurrencyExceededError` which is caught by a `retry_on` handler which re-schedules the job to execute in the near future with an incremental backoff.
|
451
|
+
|
423
452
|
### Cron-style repeating/recurring jobs
|
424
453
|
|
425
454
|
GoodJob can enqueue jobs on a recurring basis that can be used as a replacement for cron.
|
@@ -851,6 +880,23 @@ It is also necessary to destroy these preserved jobs from the database after a c
|
|
851
880
|
bundle exec good_job cleanup_preserved_jobs --before-seconds-ago=86400
|
852
881
|
```
|
853
882
|
|
883
|
+
### Write tests
|
884
|
+
|
885
|
+
By default, GoodJob uses its inline adapter in the test environment; the inline adapter is designed for the test environment. When enquing a job with GoodJob's inline adapter, the job will be executed immediately on the current thread; unhandled exceptions will be raised.
|
886
|
+
|
887
|
+
In GoodJob 2.0, the inline adapter will execute future scheduled jobs immediately. In the next major release, GoodJob 3.0, the inline adapter will not execute future scheduled jobs and instead enqueue them in the database.
|
888
|
+
|
889
|
+
To opt into this behavior immediately set: `config.good_job.inline_execution_respects_schedule = true`
|
890
|
+
|
891
|
+
To perform jobs inline at any time, use `GoodJob.perform_inline`. For example, using time helpers within an integration test:
|
892
|
+
|
893
|
+
```ruby
|
894
|
+
MyJob.set(wait: 10.minutes).perform_later
|
895
|
+
travel_to(15.minutes.from_now) { GoodJob.perform_inline }
|
896
|
+
```
|
897
|
+
|
898
|
+
_Note: Rails `travel`/`travel_to` time helpers do not have millisecond precision, so you must leave at least 1 second between the schedule and time traveling for the job to be executed. This [behavior may change in Rails 7.1](https://github.com/rails/rails/pull/44088)._
|
899
|
+
|
854
900
|
### PgBouncer compatibility
|
855
901
|
|
856
902
|
GoodJob is not compatible with PgBouncer in _transaction_ mode, but is compatible with PgBouncer's _connection_ mode. GoodJob uses connection-based advisory locks and LISTEN/NOTIFY, both of which require full database connections.
|
@@ -41,7 +41,7 @@ module GoodJob
|
|
41
41
|
}.freeze
|
42
42
|
|
43
43
|
def status_badge(status)
|
44
|
-
content_tag :span, status_icon(status, class: "text-white") + t(status, scope: '.status'),
|
44
|
+
content_tag :span, status_icon(status, class: "text-white") + t(status, scope: 'good_job.status'),
|
45
45
|
class: "badge rounded-pill bg-#{STATUS_COLOR.fetch(status)} d-inline-flex gap-2 ps-1 pe-3 align-items-center"
|
46
46
|
end
|
47
47
|
|
@@ -10,7 +10,7 @@
|
|
10
10
|
<% filter.states.each do |name, count| %>
|
11
11
|
<li class="nav-item">
|
12
12
|
<%= link_to url_for({state: name}), class: "nav-link #{"active" if params[:state] == name}" do %>
|
13
|
-
<%= t(name, scope: '.status') %>
|
13
|
+
<%= t(name, scope: 'good_job.status') %>
|
14
14
|
<span class="badge bg-primary rounded-pill <%= "bg-secondary" if count == 0 %>"><%= count %></span>
|
15
15
|
<% end %>
|
16
16
|
</li>
|
data/lib/good_job/adapter.rb
CHANGED
@@ -62,19 +62,48 @@ module GoodJob
|
|
62
62
|
# @param timestamp [Integer, nil] the epoch time to perform the job
|
63
63
|
# @return [GoodJob::Execution]
|
64
64
|
def enqueue_at(active_job, timestamp)
|
65
|
+
scheduled_at = timestamp ? Time.zone.at(timestamp) : nil
|
66
|
+
|
67
|
+
if execute_inline?
|
68
|
+
future_scheduled = (scheduled_at.nil? || scheduled_at > Time.current)
|
69
|
+
will_execute_inline = !future_scheduled || (future_scheduled && !@configuration.inline_execution_respects_schedule?)
|
70
|
+
end
|
71
|
+
|
65
72
|
execution = GoodJob::Execution.enqueue(
|
66
73
|
active_job,
|
67
|
-
scheduled_at:
|
68
|
-
create_with_advisory_lock:
|
74
|
+
scheduled_at: scheduled_at,
|
75
|
+
create_with_advisory_lock: will_execute_inline
|
69
76
|
)
|
70
77
|
|
71
|
-
if
|
78
|
+
if will_execute_inline
|
79
|
+
if future_scheduled && !@configuration.inline_execution_respects_schedule?
|
80
|
+
ActiveSupport::Deprecation.warn(<<~DEPRECATION)
|
81
|
+
In the next major release, GoodJob will not *inline* execute
|
82
|
+
future-scheduled jobs.
|
83
|
+
|
84
|
+
To opt into this behavior immediately set:
|
85
|
+
`config.good_job.inline_execution_respects_schedule = true`
|
86
|
+
|
87
|
+
To perform jobs inline at any time, use `GoodJob.perform_inline`.
|
88
|
+
|
89
|
+
For example, using time helpers within an integration test:
|
90
|
+
|
91
|
+
```
|
92
|
+
MyJob.set(wait: 10.minutes).perform_later
|
93
|
+
travel_to(15.minutes.from_now) { GoodJob.perform_inline }
|
94
|
+
```
|
95
|
+
|
96
|
+
Note: Rails `travel`/`travel_to` time helpers do not have millisecond
|
97
|
+
precision, so you must leave at least 1 second between the schedule
|
98
|
+
and time traveling for the job to be executed.
|
99
|
+
DEPRECATION
|
100
|
+
end
|
101
|
+
|
72
102
|
begin
|
73
103
|
result = execution.perform
|
74
104
|
ensure
|
75
105
|
execution.advisory_unlock
|
76
106
|
end
|
77
|
-
|
78
107
|
raise result.unhandled_error if result.unhandled_error
|
79
108
|
else
|
80
109
|
job_state = { queue_name: execution.queue_name }
|
@@ -119,6 +119,10 @@ module GoodJob
|
|
119
119
|
end
|
120
120
|
end
|
121
121
|
|
122
|
+
def inline_execution_respects_schedule?
|
123
|
+
!!rails_config[:inline_execution_respects_schedule]
|
124
|
+
end
|
125
|
+
|
122
126
|
# The maximum number of future-scheduled jobs to store in memory.
|
123
127
|
# Storing future-scheduled jobs in memory reduces execution latency
|
124
128
|
# at the cost of increased memory usage. 10,000 stored jobs = ~20MB.
|
data/lib/good_job/version.rb
CHANGED
data/lib/good_job.rb
CHANGED
@@ -8,7 +8,9 @@ Zeitwerk::Loader.for_gem.tap do |loader|
|
|
8
8
|
loader.inflector.inflect({
|
9
9
|
"cli" => "CLI",
|
10
10
|
})
|
11
|
-
loader.ignore(
|
11
|
+
loader.ignore("#{__dir__}/generators")
|
12
|
+
loader.ignore("#{__dir__}/active_job")
|
13
|
+
loader.push_dir("#{__dir__}/active_job/queue_adapters", namespace: ActiveJob::QueueAdapters)
|
12
14
|
loader.setup
|
13
15
|
end
|
14
16
|
|
@@ -151,6 +153,20 @@ module GoodJob
|
|
151
153
|
end
|
152
154
|
end
|
153
155
|
|
156
|
+
# Perform all queued jobs in the current thread.
|
157
|
+
# This is primarily intended for usage in a test environment.
|
158
|
+
# Unhandled job errors will be raised.
|
159
|
+
# @param queue_string [String] Queues to execute jobs from
|
160
|
+
# @return [void]
|
161
|
+
def self.perform_inline(queue_string = "*")
|
162
|
+
job_performer = JobPerformer.new(queue_string)
|
163
|
+
loop do
|
164
|
+
result = job_performer.next
|
165
|
+
break unless result
|
166
|
+
raise result.unhandled_error if result.unhandled_error
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
154
170
|
def self._executables
|
155
171
|
[].concat(
|
156
172
|
CronManager.instances,
|
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: 2.
|
4
|
+
version: 2.16.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: 2022-
|
11
|
+
date: 2022-06-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activejob
|