good_job 3.5.1 → 3.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +51 -16
- data/README.md +54 -13
- data/app/models/good_job/execution.rb +15 -5
- data/app/models/good_job/execution_result.rb +5 -1
- data/app/models/good_job/lockable.rb +4 -3
- data/config/locales/ua.yml +107 -0
- data/lib/generators/good_job/templates/install/migrations/create_good_jobs.rb.erb +2 -0
- data/lib/generators/good_job/templates/update/migrations/03_create_index_good_jobs_jobs_on_priority_created_at_when_unfinished.rb.erb +18 -0
- data/lib/good_job/cli.rb +5 -0
- data/lib/good_job/configuration.rb +13 -0
- data/lib/good_job/job_performer.rb +1 -1
- data/lib/good_job/version.rb +1 -1
- data/lib/good_job.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3cdc97553bd567b0286d79c9a41187cf6a91478855f3469af986453a6c311f72
|
4
|
+
data.tar.gz: ef18533f656f9d1b2a4f131e5e745760ea8976a94f1c7f46a195c0042c9a35ff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d411c12b7b7b16c7787942d4a5998f319b44fea819a2caf756513d8b7e9f2c6ae44b8afe41576299d3c841aabda50c0669e087cfb13c885e86225fc25192d927
|
7
|
+
data.tar.gz: 0df610fb8995d52e24b55984399bbe59b645eb570891fdc262faf6482d324e42c647bdd33c18d7025323f99d57f5a6bd5455be75b3ccf60b31bfdcf65b0a4137
|
data/CHANGELOG.md
CHANGED
@@ -1,17 +1,55 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
-
## [v3.
|
3
|
+
## [v3.6.1](https://github.com/bensheldon/good_job/tree/v3.6.1) (2022-11-30)
|
4
4
|
|
5
|
-
[Full Changelog](https://github.com/bensheldon/good_job/compare/v3.
|
5
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v3.6.0...v3.6.1)
|
6
|
+
|
7
|
+
**Implemented enhancements:**
|
8
|
+
|
9
|
+
- add UA to i18n [\#747](https://github.com/bensheldon/good_job/pull/747) ([yshmarov](https://github.com/yshmarov))
|
6
10
|
|
7
11
|
**Closed issues:**
|
8
12
|
|
9
|
-
-
|
13
|
+
- Minor Cron Concurrency Clarification [\#743](https://github.com/bensheldon/good_job/issues/743)
|
14
|
+
- Materialized CTE performance bottleneck [\#720](https://github.com/bensheldon/good_job/issues/720)
|
15
|
+
- Calculating database connections [\#669](https://github.com/bensheldon/good_job/issues/669)
|
10
16
|
|
11
17
|
**Merged pull requests:**
|
12
18
|
|
19
|
+
- Update gems, rubocop, and development ruby \(v2.7.7\) [\#752](https://github.com/bensheldon/good_job/pull/752) ([bensheldon](https://github.com/bensheldon))
|
20
|
+
- Add chrome/chromdriver to Codespace configuration [\#746](https://github.com/bensheldon/good_job/pull/746) ([bensheldon](https://github.com/bensheldon))
|
21
|
+
- Add GitHub Codespaces / devcontainer configuration [\#745](https://github.com/bensheldon/good_job/pull/745) ([bensheldon](https://github.com/bensheldon))
|
22
|
+
- Bump fugit from 1.7.1 to 1.7.2 [\#742](https://github.com/bensheldon/good_job/pull/742) ([dependabot[bot]](https://github.com/apps/dependabot))
|
23
|
+
- Bundle update and Rubocop lints [\#740](https://github.com/bensheldon/good_job/pull/740) ([bensheldon](https://github.com/bensheldon))
|
24
|
+
- Add dependabot to update development dependencies [\#734](https://github.com/bensheldon/good_job/pull/734) ([bensheldon](https://github.com/bensheldon))
|
25
|
+
- Fix misspellings and grammar in README [\#732](https://github.com/bensheldon/good_job/pull/732) ([aisayo](https://github.com/aisayo))
|
26
|
+
|
27
|
+
## [v3.6.0](https://github.com/bensheldon/good_job/tree/v3.6.0) (2022-10-22)
|
28
|
+
|
29
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v3.5.1...v3.6.0)
|
30
|
+
|
31
|
+
**Implemented enhancements:**
|
32
|
+
|
33
|
+
- Add configurable limit \(`queue_select_limit`\) when querying candidate jobs [\#727](https://github.com/bensheldon/good_job/pull/727) ([mitchellhenke](https://github.com/mitchellhenke))
|
34
|
+
- Add index to `good_jobs` to improve querying candidate jobs [\#726](https://github.com/bensheldon/good_job/pull/726) ([mitchellhenke](https://github.com/mitchellhenke))
|
35
|
+
|
36
|
+
**Fixed bugs:**
|
37
|
+
|
38
|
+
- 3.4.8 release breaks job retrying [\#728](https://github.com/bensheldon/good_job/issues/728)
|
39
|
+
- Redo: When not preserving job records, ensure all prior executions are deleted after successful retry [\#730](https://github.com/bensheldon/good_job/pull/730) ([bensheldon](https://github.com/bensheldon))
|
40
|
+
|
41
|
+
## [v3.5.1](https://github.com/bensheldon/good_job/tree/v3.5.1) (2022-10-20)
|
42
|
+
|
43
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v3.5.0...v3.5.1)
|
44
|
+
|
45
|
+
**Fixed bugs:**
|
46
|
+
|
13
47
|
- Revert "When not preserving job records, ensure all prior executions are deleted after successful retry" because some retry patterns stopped working [\#729](https://github.com/bensheldon/good_job/pull/729) ([bensheldon](https://github.com/bensheldon))
|
14
48
|
|
49
|
+
**Closed issues:**
|
50
|
+
|
51
|
+
- Assert cancelled jobs [\#724](https://github.com/bensheldon/good_job/issues/724)
|
52
|
+
|
15
53
|
## [v3.5.0](https://github.com/bensheldon/good_job/tree/v3.5.0) (2022-10-18)
|
16
54
|
|
17
55
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v3.4.8...v3.5.0)
|
@@ -26,7 +64,7 @@
|
|
26
64
|
|
27
65
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v3.4.7...v3.4.8)
|
28
66
|
|
29
|
-
**
|
67
|
+
**Fixed bugs:**
|
30
68
|
|
31
69
|
- When not preserving job records, ensure all prior executions are deleted after successful retry [\#719](https://github.com/bensheldon/good_job/pull/719) ([ylansegal](https://github.com/ylansegal))
|
32
70
|
|
@@ -110,6 +148,7 @@
|
|
110
148
|
**Fixed bugs:**
|
111
149
|
|
112
150
|
- Jobs enqueued via dashboard ignores app default\_locale [\#697](https://github.com/bensheldon/good_job/issues/697)
|
151
|
+
- Enqueues Cron jobs with I18n default locale [\#698](https://github.com/bensheldon/good_job/pull/698) ([esasse](https://github.com/esasse))
|
113
152
|
- Include better exception log messages, including class and backtrace [\#693](https://github.com/bensheldon/good_job/pull/693) ([bensheldon](https://github.com/bensheldon))
|
114
153
|
|
115
154
|
**Closed issues:**
|
@@ -119,10 +158,6 @@
|
|
119
158
|
- ActiveRecord::StatementInvalid: PG::ConnectionBad: PQsocket\(\) can't get socket descriptor every 30 minutes aprox. [\#579](https://github.com/bensheldon/good_job/issues/579)
|
120
159
|
- Handle assets in dashboard when rails app is behind proxy path [\#424](https://github.com/bensheldon/good_job/issues/424)
|
121
160
|
|
122
|
-
**Merged pull requests:**
|
123
|
-
|
124
|
-
- Enqueues Cron jobs with I18n default locale [\#698](https://github.com/bensheldon/good_job/pull/698) ([esasse](https://github.com/esasse))
|
125
|
-
|
126
161
|
## [v3.4.1](https://github.com/bensheldon/good_job/tree/v3.4.1) (2022-08-06)
|
127
162
|
|
128
163
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v3.4.0...v3.4.1)
|
@@ -1008,7 +1043,7 @@
|
|
1008
1043
|
- Warn in Readme that configuration should not go into `config/initializers/*.rb` [\#418](https://github.com/bensheldon/good_job/pull/418) ([bensheldon](https://github.com/bensheldon))
|
1009
1044
|
- Replace worker wording [\#409](https://github.com/bensheldon/good_job/pull/409) ([Hugo-Hache](https://github.com/Hugo-Hache))
|
1010
1045
|
- Improve Readme's "Optimize queues, threads, processes" section [\#405](https://github.com/bensheldon/good_job/pull/405) ([Hugo-Hache](https://github.com/Hugo-Hache))
|
1011
|
-
- Update GH Test Matrix with more PG versions [\#401](https://github.com/bensheldon/good_job/pull/401) ([
|
1046
|
+
- Update GH Test Matrix with more PG versions [\#401](https://github.com/bensheldon/good_job/pull/401) ([tedthetnaungsoe](https://github.com/tedthetnaungsoe))
|
1012
1047
|
- Extract cron configuration hash into CronEntry ActiveModel objects [\#400](https://github.com/bensheldon/good_job/pull/400) ([bensheldon](https://github.com/bensheldon))
|
1013
1048
|
- Remove errant copy-paste from app.json [\#397](https://github.com/bensheldon/good_job/pull/397) ([morgoth](https://github.com/morgoth))
|
1014
1049
|
|
@@ -1048,7 +1083,7 @@
|
|
1048
1083
|
|
1049
1084
|
**Merged pull requests:**
|
1050
1085
|
|
1051
|
-
- Update GH Test Matrix with latest JRuby 9.3.0.0 [\#387](https://github.com/bensheldon/good_job/pull/387) ([
|
1086
|
+
- Update GH Test Matrix with latest JRuby 9.3.0.0 [\#387](https://github.com/bensheldon/good_job/pull/387) ([tedthetnaungsoe](https://github.com/tedthetnaungsoe))
|
1052
1087
|
- Improve test support's ShellOut command's process termination and add test logs [\#385](https://github.com/bensheldon/good_job/pull/385) ([bensheldon](https://github.com/bensheldon))
|
1053
1088
|
- @bensheldon Add Rails 7 alpha to Appraisal; update development dependencies [\#384](https://github.com/bensheldon/good_job/pull/384) ([bensheldon](https://github.com/bensheldon))
|
1054
1089
|
|
@@ -1307,7 +1342,7 @@
|
|
1307
1342
|
|
1308
1343
|
- Have prettier Dashboard asset urls e.g. `bootstrap.css` instead of `bootstrap_css.css` [\#306](https://github.com/bensheldon/good_job/pull/306) ([bensheldon](https://github.com/bensheldon))
|
1309
1344
|
- Create dashboard demo app on Heroku [\#305](https://github.com/bensheldon/good_job/pull/305) ([bensheldon](https://github.com/bensheldon))
|
1310
|
-
- Add Frozen String Literal to all files [\#302](https://github.com/bensheldon/good_job/pull/302) ([
|
1345
|
+
- Add Frozen String Literal to all files [\#302](https://github.com/bensheldon/good_job/pull/302) ([tedthetnaungsoe](https://github.com/tedthetnaungsoe))
|
1311
1346
|
|
1312
1347
|
## [v1.11.2](https://github.com/bensheldon/good_job/tree/v1.11.2) (2021-07-20)
|
1313
1348
|
|
@@ -1365,7 +1400,7 @@
|
|
1365
1400
|
|
1366
1401
|
**Merged pull requests:**
|
1367
1402
|
|
1368
|
-
- Update GH Test Matrix with latest JRuby 9.2.19.0 [\#283](https://github.com/bensheldon/good_job/pull/283) ([
|
1403
|
+
- Update GH Test Matrix with latest JRuby 9.2.19.0 [\#283](https://github.com/bensheldon/good_job/pull/283) ([tedthetnaungsoe](https://github.com/tedthetnaungsoe))
|
1369
1404
|
|
1370
1405
|
## [v1.10.0](https://github.com/bensheldon/good_job/tree/v1.10.0) (2021-06-29)
|
1371
1406
|
|
@@ -1515,11 +1550,11 @@
|
|
1515
1550
|
- Move executable flags from constants to accessors on GoodJob::CLI [\#234](https://github.com/bensheldon/good_job/pull/234) ([bensheldon](https://github.com/bensheldon))
|
1516
1551
|
- Add custom Scheduler::TimerSet [\#232](https://github.com/bensheldon/good_job/pull/232) ([bensheldon](https://github.com/bensheldon))
|
1517
1552
|
- Fix assorted constant references in YARD documentation [\#231](https://github.com/bensheldon/good_job/pull/231) ([bensheldon](https://github.com/bensheldon))
|
1518
|
-
- Update GH Test Matrix with latest JRuby 9.2.17.0 [\#228](https://github.com/bensheldon/good_job/pull/228) ([
|
1553
|
+
- Update GH Test Matrix with latest JRuby 9.2.17.0 [\#228](https://github.com/bensheldon/good_job/pull/228) ([tedthetnaungsoe](https://github.com/tedthetnaungsoe))
|
1519
1554
|
- Update gem dependencies [\#227](https://github.com/bensheldon/good_job/pull/227) ([bensheldon](https://github.com/bensheldon))
|
1520
1555
|
- Remove leftover text from Readme [\#226](https://github.com/bensheldon/good_job/pull/226) ([weh](https://github.com/weh))
|
1521
1556
|
- Fix appraisal and bundler version CI conflicts [\#224](https://github.com/bensheldon/good_job/pull/224) ([bensheldon](https://github.com/bensheldon))
|
1522
|
-
- Update GH Test Matrix with latest JRuby [\#223](https://github.com/bensheldon/good_job/pull/223) ([
|
1557
|
+
- Update GH Test Matrix with latest JRuby [\#223](https://github.com/bensheldon/good_job/pull/223) ([tedthetnaungsoe](https://github.com/tedthetnaungsoe))
|
1523
1558
|
|
1524
1559
|
## [v1.8.0](https://github.com/bensheldon/good_job/tree/v1.8.0) (2021-03-04)
|
1525
1560
|
|
@@ -1602,8 +1637,8 @@
|
|
1602
1637
|
**Merged pull requests:**
|
1603
1638
|
|
1604
1639
|
- Update bundler version to 2.2.5 [\#200](https://github.com/bensheldon/good_job/pull/200) ([bensheldon](https://github.com/bensheldon))
|
1605
|
-
- Update GH Test Matrix with minimum & latest JRuby version [\#197](https://github.com/bensheldon/good_job/pull/197) ([
|
1606
|
-
- Fix JRuby version number [\#193](https://github.com/bensheldon/good_job/pull/193) ([
|
1640
|
+
- Update GH Test Matrix with minimum & latest JRuby version [\#197](https://github.com/bensheldon/good_job/pull/197) ([tedthetnaungsoe](https://github.com/tedthetnaungsoe))
|
1641
|
+
- Fix JRuby version number [\#193](https://github.com/bensheldon/good_job/pull/193) ([tedthetnaungsoe](https://github.com/tedthetnaungsoe))
|
1607
1642
|
|
1608
1643
|
## [v1.4.1](https://github.com/bensheldon/good_job/tree/v1.4.1) (2021-01-09)
|
1609
1644
|
|
data/README.md
CHANGED
@@ -57,6 +57,7 @@ For more of the story of GoodJob, read the [introductory blog post](https://isla
|
|
57
57
|
- [Optimize queues, threads, and processes](#optimize-queues-threads-and-processes)
|
58
58
|
- [Database connections](#database-connections)
|
59
59
|
- [Production setup](#production-setup)
|
60
|
+
- [Queue performance with Queue Select Limit](#queue-performance-with-queue-select-limit)
|
60
61
|
- [Execute jobs async / in-process](#execute-jobs-async--in-process)
|
61
62
|
- [Migrate to GoodJob from a different ActiveJob backend](#migrate-to-goodjob-from-a-different-activejob-backend)
|
62
63
|
- [Monitor and preserve worked jobs](#monitor-and-preserve-worked-jobs)
|
@@ -152,7 +153,7 @@ For more of the story of GoodJob, read the [introductory blog post](https://isla
|
|
152
153
|
|
153
154
|
### Command-line options
|
154
155
|
|
155
|
-
There several top-level commands available through the `good_job` command-line tool.
|
156
|
+
There are several top-level commands available through the `good_job` command-line tool.
|
156
157
|
|
157
158
|
Configuration options are available with `help`.
|
158
159
|
|
@@ -176,6 +177,7 @@ Options:
|
|
176
177
|
[--daemonize] # Run as a background daemon (default: false)
|
177
178
|
[--pidfile=PIDFILE] # Path to write daemonized Process ID (env var: GOOD_JOB_PIDFILE, default: tmp/pids/good_job.pid)
|
178
179
|
[--probe-port=PORT] # Port for http health check (env var: GOOD_JOB_PROBE_PORT, default: nil)
|
180
|
+
[--queue-select-limit=COUNT] # The number of queued jobs to select when polling for a job to run. (env var: GOOD_JOB_QUEUE_SELECT_LIMIT, default: nil)"
|
179
181
|
|
180
182
|
Executes queued jobs.
|
181
183
|
|
@@ -273,7 +275,7 @@ Available configuration options are:
|
|
273
275
|
- `max_threads` (integer) sets the default number of threads per pool to use for working jobs. You can also set this with the environment variable `GOOD_JOB_MAX_THREADS`.
|
274
276
|
- `poll_interval` (integer) sets the number of seconds between polls for jobs when `execution_mode` is set to `:async`. You can also set this with the environment variable `GOOD_JOB_POLL_INTERVAL`. A poll interval of `-1` disables polling completely.
|
275
277
|
- `max_cache` (integer) sets the maximum number of scheduled jobs that will be stored in memory to reduce execution latency when also polling for scheduled jobs. Caching 10,000 scheduled jobs uses approximately 20MB of memory. You can also set this with the environment variable `GOOD_JOB_MAX_CACHE`.
|
276
|
-
- `shutdown_timeout` (
|
278
|
+
- `shutdown_timeout` (integer) number of seconds to wait for jobs to finish when shutting down before stopping the thread. Defaults to forever: `-1`. You can also set this with the environment variable `GOOD_JOB_SHUTDOWN_TIMEOUT`.
|
277
279
|
- `enable_cron` (boolean) whether to run cron process. Defaults to `false`. You can also set this with the environment variable `GOOD_JOB_ENABLE_CRON`.
|
278
280
|
- `cron` (hash) cron configuration. Defaults to `{}`. You can also set this as a JSON string with the environment variable `GOOD_JOB_CRON`
|
279
281
|
- `cleanup_discarded_jobs` (boolean) whether to destroy discarded jobs when cleaning up preserved jobs using the `$ good_job cleanup_preserved_jobs` CLI command or calling `GoodJob.cleanup_preserved_jobs`. Defaults to `true`. Can also be set with the environment variable `GOOD_JOB_CLEANUP_DISCARDED_JOBS`. _This configuration is only used when {GoodJob.preserve_job_records} is `true`._
|
@@ -284,7 +286,7 @@ Available configuration options are:
|
|
284
286
|
- `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`).
|
285
287
|
- `preserve_job_records` (boolean) keeps job records in your database even after jobs are completed. (Default: `true`)
|
286
288
|
- `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: `false`)
|
287
|
-
- `on_thread_error` (proc, lambda, or callable) will be called when an Exception. It can be useful for logging errors to bug tracking services, like Sentry or Airbrake. Example:
|
289
|
+
- `on_thread_error` (proc, lambda, or callable) will be called when there is an Exception. It can be useful for logging errors to bug tracking services, like Sentry or Airbrake. Example:
|
288
290
|
|
289
291
|
```ruby
|
290
292
|
config.good_job.on_thread_error = -> (exception) { Sentry.capture_exception(exception) }
|
@@ -325,7 +327,7 @@ The following options are also configurable via accessors, but you are encourage
|
|
325
327
|
- **`GoodJob.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`.
|
326
328
|
- **`GoodJob.preserve_job_records`** (boolean) keeps job records in your database even after jobs are completed. (Default: `true`)
|
327
329
|
- **`GoodJob.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: `false`)
|
328
|
-
- **`GoodJob.on_thread_error`** (proc, lambda, or callable) will be called when an Exception. It can be useful for logging errors to bug tracking services, like Sentry or Airbrake.
|
330
|
+
- **`GoodJob.on_thread_error`** (proc, lambda, or callable) will be called when there is an Exception. It can be useful for logging errors to bug tracking services, like Sentry or Airbrake.
|
329
331
|
|
330
332
|
### Dashboard
|
331
333
|
|
@@ -377,7 +379,7 @@ require 'good_job/engine' # <= Add this line
|
|
377
379
|
|
378
380
|
#### API-only Rails applications
|
379
381
|
|
380
|
-
API-only Rails applications may not have all of the required Rack middleware for the GoodJob Dashboard to function. To re-add the
|
382
|
+
API-only Rails applications may not have all of the required Rack middleware for the GoodJob Dashboard to function. To re-add the middleware:
|
381
383
|
|
382
384
|
```ruby
|
383
385
|
# config/application.rb
|
@@ -398,7 +400,7 @@ The Dashboard can be set to automatically refresh by checking "Live Poll" in the
|
|
398
400
|
|
399
401
|
### ActiveJob concurrency
|
400
402
|
|
401
|
-
GoodJob can extend ActiveJob to provide limits on concurrently running jobs, either at time of _enqueue_ or at _perform_. Limiting concurrency can help prevent duplicate, double or
|
403
|
+
GoodJob can extend ActiveJob to provide limits on concurrently running jobs, either at time of _enqueue_ or at _perform_. Limiting concurrency can help prevent duplicate, double or unnecessary jobs from being enqueued, or race conditions when performing, for example when interacting with 3rd-party APIs.
|
402
404
|
|
403
405
|
```ruby
|
404
406
|
class MyJob < ApplicationJob
|
@@ -458,7 +460,7 @@ GoodJob's concurrency control strategy for `perform_limit` is "optimistic retry
|
|
458
460
|
|
459
461
|
GoodJob can enqueue jobs on a recurring basis that can be used as a replacement for cron.
|
460
462
|
|
461
|
-
Cron-style jobs are run on every GoodJob process (e.g. CLI or `async` execution mode) when `config.good_job.enable_cron = true`, but GoodJob's cron uses unique indexes to ensure that only a single job is
|
463
|
+
Cron-style jobs are run on every GoodJob process (e.g. CLI or `async` execution mode) when `config.good_job.enable_cron = true`, but GoodJob's cron uses unique indexes to ensure that only a single job is enqueued at the given time interval.
|
462
464
|
|
463
465
|
Cron-format is parsed by the [`fugit`](https://github.com/floraison/fugit) gem, which has support for seconds-level resolution (e.g. `* * * * * *`) and natural language parsing (e.g. `every second`).
|
464
466
|
|
@@ -540,7 +542,7 @@ GoodJob v2 introduces a new Advisory Lock key format that is operationally diffe
|
|
540
542
|
|
541
543
|
1. Upgrade your production environment to `v1.99.x` following the minor version upgrade process, including database migrations. `v1.99` is a transitional release that is safely compatible with both `v1.x` and `v2.0.0` because it uses both `v1`- and `v2`-formatted advisory locks.
|
542
544
|
1. Address any deprecation warnings generated by `v1.99`.
|
543
|
-
1. Upgrade your production environment
|
545
|
+
1. Upgrade your production environment from `v1.99.x` to `v2.0.x` again following the _minor_ upgrade process.
|
544
546
|
|
545
547
|
Notable changes:
|
546
548
|
|
@@ -746,17 +748,56 @@ The queue process will not crash if the connections pool is exhausted, instead i
|
|
746
748
|
|
747
749
|
When running GoodJob in a production environment, you should be mindful of:
|
748
750
|
|
749
|
-
- [Execution mode](execute-jobs-async--in-process)
|
751
|
+
- [Execution mode](#execute-jobs-async--in-process)
|
750
752
|
- [Database connection pool size](#database-connections)
|
751
753
|
- [Health check probes](#cli-http-health-check-probes) and potentially the [instrumentation support](#monitor-and-preserve-worked-jobs)
|
752
754
|
|
753
755
|
The recommended way to monitor the queue in production is:
|
754
756
|
|
755
|
-
- have an exception notifier callback (see `on_thread_error`)
|
756
|
-
- if possible, run the queue as a dedicated instance and use available HTTP health check probes instead of
|
757
|
+
- have an exception notifier callback (see [`on_thread_error`](#configuration-options))
|
758
|
+
- if possible, run the queue as a dedicated instance and use available HTTP health check probes instead of PID-based monitoring
|
757
759
|
- keep an eye on the number of jobs in the queue (abnormal high number of unscheduled jobs means the queue could be underperforming)
|
758
760
|
- consider performance monitoring services which support the built-in Rails instrumentation (eg. Sentry, Skylight, etc.)
|
759
761
|
|
762
|
+
#### Queue performance with Queue Select Limit
|
763
|
+
|
764
|
+
GoodJob’s advisory locking strategy uses a materialized CTE (Common Table Expression). This strategy can be non-performant when querying a very large queue of executable jobs (100,000+) because the database query must materialize all executable jobs before acquiring an advisory lock.
|
765
|
+
|
766
|
+
GoodJob offers an optional optimization to limit the number of jobs that are queried: Queue Select Limit.
|
767
|
+
|
768
|
+
```none
|
769
|
+
# CLI option
|
770
|
+
--queue-select-limit=1000
|
771
|
+
|
772
|
+
# Rails configuration
|
773
|
+
config.good_job.queue_select_limit = 1000
|
774
|
+
|
775
|
+
# Environment Variable
|
776
|
+
GOOD_JOB_QUEUE_SELECT_LIMIT=1000
|
777
|
+
```
|
778
|
+
|
779
|
+
The Queue Select Limit value should be set to a rough upper-bound that exceeds all GoodJob execution threads / database connections. `1000` is a number that likely exceeds the available database connections on most PaaS offerings, but still offers a performance boost for GoodJob when executing very large queues.
|
780
|
+
|
781
|
+
To explain where this value is used, here is the pseudo-query that GoodJob uses to find executable jobs:
|
782
|
+
|
783
|
+
```sql
|
784
|
+
SELECT *
|
785
|
+
FROM good_jobs
|
786
|
+
WHERE id IN (
|
787
|
+
WITH rows AS MATERIALIZED (
|
788
|
+
SELECT id, active_job_id
|
789
|
+
FROM good_jobs
|
790
|
+
WHERE (scheduled_at <= NOW() OR scheduled_at IS NULL) AND finished_at IS NULL
|
791
|
+
ORDER BY priority DESC NULLS LAST, created_at ASC
|
792
|
+
[LIMIT 1000] -- <= introduced when queue_select_limit is set
|
793
|
+
)
|
794
|
+
SELECT id
|
795
|
+
FROM rows
|
796
|
+
WHERE pg_try_advisory_lock(('x' || substr(md5('good_jobs' || '-' || active_job_id::text), 1, 16))::bit(64)::bigint)
|
797
|
+
LIMIT 1
|
798
|
+
)
|
799
|
+
```
|
800
|
+
|
760
801
|
### Execute jobs async / in-process
|
761
802
|
|
762
803
|
GoodJob can execute jobs "async" in the same process as the web server (e.g. `bin/rails s`). GoodJob's async execution mode offers benefits of economy by not requiring a separate job worker process, but with the tradeoff of increased complexity. Async mode can be configured in two ways:
|
@@ -912,7 +953,7 @@ It is also possible to manually trigger a cleanup of preserved job records:
|
|
912
953
|
|
913
954
|
### Write tests
|
914
955
|
|
915
|
-
By default, GoodJob uses its inline adapter in the test environment; the inline adapter is designed for the test environment. When
|
956
|
+
By default, GoodJob uses its inline adapter in the test environment; the inline adapter is designed for the test environment. When enqueuing a job with GoodJob's inline adapter, the job will be executed immediately on the current thread; unhandled exceptions will be raised.
|
916
957
|
|
917
958
|
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.
|
918
959
|
|
@@ -1026,7 +1067,7 @@ spec:
|
|
1026
1067
|
All contributions, from feedback to code and beyond, are welcomed and appreciated 🙏
|
1027
1068
|
|
1028
1069
|
- Review the [Prioritized Project Backlog](https://github.com/bensheldon/good_job/projects/1).
|
1029
|
-
- Open a new
|
1070
|
+
- Open a new issue or contribute to an [existing Issue](https://github.com/bensheldon/good_job/issues). Questions or suggestions are fantastic.
|
1030
1071
|
- Participate according to our [Code of Conduct](/CODE_OF_CONDUCT.md).
|
1031
1072
|
- Financially support the project via [Sponsorship](https://github.com/sponsors/bensheldon).
|
1032
1073
|
|
@@ -66,6 +66,7 @@ module GoodJob
|
|
66
66
|
end
|
67
67
|
|
68
68
|
belongs_to :job, class_name: 'GoodJob::Job', foreign_key: 'active_job_id', primary_key: 'active_job_id', optional: true, inverse_of: :executions
|
69
|
+
after_destroy -> { self.class.active_job_id(active_job_id).delete_all }, if: -> { @_destroy_job }
|
69
70
|
|
70
71
|
# Get executions with given ActiveJob ID
|
71
72
|
# @!method active_job_id
|
@@ -203,10 +204,10 @@ module GoodJob
|
|
203
204
|
# return value for the job's +#perform+ method, and the exception the job
|
204
205
|
# raised, if any (if the job raised, then the second array entry will be
|
205
206
|
# +nil+). If there were no jobs to execute, returns +nil+.
|
206
|
-
def self.perform_with_advisory_lock(parsed_queues: nil)
|
207
|
+
def self.perform_with_advisory_lock(parsed_queues: nil, queue_select_limit: nil)
|
207
208
|
execution = nil
|
208
209
|
result = nil
|
209
|
-
unfinished.dequeueing_ordered(parsed_queues).only_scheduled.limit(1).with_advisory_lock(unlock_session: true) do |executions|
|
210
|
+
unfinished.dequeueing_ordered(parsed_queues).only_scheduled.limit(1).with_advisory_lock(unlock_session: true, select_limit: queue_select_limit) do |executions|
|
210
211
|
execution = executions.first
|
211
212
|
break if execution.blank?
|
212
213
|
break :unlocked unless execution&.executable?
|
@@ -296,13 +297,14 @@ module GoodJob
|
|
296
297
|
job_error = result.handled_error || result.unhandled_error
|
297
298
|
self.error = [job_error.class, ERROR_MESSAGE_SEPARATOR, job_error.message].join if job_error
|
298
299
|
|
300
|
+
reenqueued = result.retried? || retried_good_job_id.present?
|
299
301
|
if result.unhandled_error && GoodJob.retry_on_unhandled_error
|
300
302
|
save!
|
301
|
-
elsif GoodJob.preserve_job_records == true || (result.unhandled_error && GoodJob.preserve_job_records == :on_unhandled_error)
|
303
|
+
elsif GoodJob.preserve_job_records == true || reenqueued || (result.unhandled_error && GoodJob.preserve_job_records == :on_unhandled_error)
|
302
304
|
self.finished_at = Time.current
|
303
305
|
save!
|
304
306
|
else
|
305
|
-
|
307
|
+
destroy_job
|
306
308
|
end
|
307
309
|
|
308
310
|
result
|
@@ -354,6 +356,14 @@ module GoodJob
|
|
354
356
|
(finished_at || Time.zone.now) - performed_at if performed_at
|
355
357
|
end
|
356
358
|
|
359
|
+
# Destroys this execution and all executions within the same job
|
360
|
+
def destroy_job
|
361
|
+
@_destroy_job = true
|
362
|
+
destroy!
|
363
|
+
ensure
|
364
|
+
@_destroy_job = false
|
365
|
+
end
|
366
|
+
|
357
367
|
private
|
358
368
|
|
359
369
|
def active_job_data
|
@@ -379,7 +389,7 @@ module GoodJob
|
|
379
389
|
end
|
380
390
|
handled_error ||= current_thread.error_on_retry || current_thread.error_on_discard
|
381
391
|
|
382
|
-
ExecutionResult.new(value: value, handled_error: handled_error)
|
392
|
+
ExecutionResult.new(value: value, handled_error: handled_error, retried: current_thread.error_on_retry.present?)
|
383
393
|
rescue StandardError => e
|
384
394
|
ExecutionResult.new(value: nil, unhandled_error: e)
|
385
395
|
end
|
@@ -8,14 +8,18 @@ module GoodJob
|
|
8
8
|
attr_reader :handled_error
|
9
9
|
# @return [Exception, nil]
|
10
10
|
attr_reader :unhandled_error
|
11
|
+
# @return [Exception, nil]
|
12
|
+
attr_reader :retried
|
13
|
+
alias retried? retried
|
11
14
|
|
12
15
|
# @param value [Object, nil]
|
13
16
|
# @param handled_error [Exception, nil]
|
14
17
|
# @param unhandled_error [Exception, nil]
|
15
|
-
def initialize(value:, handled_error: nil, unhandled_error: nil)
|
18
|
+
def initialize(value:, handled_error: nil, unhandled_error: nil, retried: false)
|
16
19
|
@value = value
|
17
20
|
@handled_error = handled_error
|
18
21
|
@unhandled_error = unhandled_error
|
22
|
+
@retried = retried
|
19
23
|
end
|
20
24
|
end
|
21
25
|
end
|
@@ -37,11 +37,12 @@ module GoodJob
|
|
37
37
|
# @param function [String, Symbol] Postgres Advisory Lock function name to use
|
38
38
|
# @return [ActiveRecord::Relation]
|
39
39
|
# A relation selecting only the records that were locked.
|
40
|
-
scope :advisory_lock, (lambda do |column: _advisory_lockable_column, function: advisory_lockable_function|
|
40
|
+
scope :advisory_lock, (lambda do |column: _advisory_lockable_column, function: advisory_lockable_function, select_limit: nil|
|
41
41
|
original_query = self
|
42
42
|
|
43
43
|
cte_table = Arel::Table.new(:rows)
|
44
44
|
cte_query = original_query.select(primary_key, column).except(:limit)
|
45
|
+
cte_query = cte_query.limit(select_limit) if select_limit
|
45
46
|
cte_type = if supports_cte_materialization_specifiers?
|
46
47
|
'MATERIALIZED'
|
47
48
|
else
|
@@ -154,10 +155,10 @@ module GoodJob
|
|
154
155
|
# MyLockableRecord.order(created_at: :asc).limit(2).with_advisory_lock do |record|
|
155
156
|
# do_something_with record
|
156
157
|
# end
|
157
|
-
def with_advisory_lock(column: _advisory_lockable_column, function: advisory_lockable_function, unlock_session: false)
|
158
|
+
def with_advisory_lock(column: _advisory_lockable_column, function: advisory_lockable_function, unlock_session: false, select_limit: nil)
|
158
159
|
raise ArgumentError, "Must provide a block" unless block_given?
|
159
160
|
|
160
|
-
records = advisory_lock(column: column, function: function).to_a
|
161
|
+
records = advisory_lock(column: column, function: function, select_limit: select_limit).to_a
|
161
162
|
|
162
163
|
begin
|
163
164
|
unscoped { yield(records) }
|
@@ -0,0 +1,107 @@
|
|
1
|
+
---
|
2
|
+
ua:
|
3
|
+
datetime:
|
4
|
+
distance_in_words:
|
5
|
+
about_x_hours:
|
6
|
+
few: близько %{count} години
|
7
|
+
many: близько %{count} годин
|
8
|
+
one: близько 1 години
|
9
|
+
other: близько %{count} години
|
10
|
+
about_x_months:
|
11
|
+
few: близько %{count} місяців
|
12
|
+
many: близько %{count} місяців
|
13
|
+
one: близько 1 місяцю
|
14
|
+
other: близько %{count} місяцю
|
15
|
+
about_x_years:
|
16
|
+
few: близько %{count} років
|
17
|
+
many: близько %{count} років
|
18
|
+
one: близько 1 року
|
19
|
+
other: близько %{count} років
|
20
|
+
almost_x_years:
|
21
|
+
few: майже %{count} роки
|
22
|
+
many: майже %{count} років
|
23
|
+
one: майже 1 рік
|
24
|
+
other: майже %{count} років
|
25
|
+
half_a_minute: пів хвилини
|
26
|
+
less_than_x_minutes:
|
27
|
+
few: менше %{count} хвилин
|
28
|
+
many: менше %{count} хвилин
|
29
|
+
one: менше 1 хвилини
|
30
|
+
other: менше %{count} хвилини
|
31
|
+
less_than_x_seconds:
|
32
|
+
few: менше %{count} секунд
|
33
|
+
many: менше %{count} секунд
|
34
|
+
one: менше 1 секунди
|
35
|
+
other: менше %{count} секунди
|
36
|
+
over_x_years:
|
37
|
+
few: більше %{count} років
|
38
|
+
many: більше %{count} років
|
39
|
+
one: більше 1 року
|
40
|
+
other: більше %{count} років
|
41
|
+
x_days:
|
42
|
+
few: "%{count} дня"
|
43
|
+
many: "%{count} днів"
|
44
|
+
one: 1 день
|
45
|
+
other: "%{count} дня"
|
46
|
+
x_minutes:
|
47
|
+
few: "%{count} хвилини"
|
48
|
+
many: "%{count} хвилин"
|
49
|
+
one: 1 хвилину
|
50
|
+
other: "%{count} хвилини"
|
51
|
+
x_months:
|
52
|
+
few: "%{count} місяцю"
|
53
|
+
many: "%{count} місяців"
|
54
|
+
one: 1 місяць
|
55
|
+
other: "%{count} місяцю"
|
56
|
+
x_seconds:
|
57
|
+
few: "%{count} секунди"
|
58
|
+
many: "%{count} секунд"
|
59
|
+
one: 1 секунду
|
60
|
+
other: "%{count} секунди"
|
61
|
+
x_years:
|
62
|
+
few: "%{count} року"
|
63
|
+
many: "%{count} років"
|
64
|
+
one: 1 рік
|
65
|
+
other: "%{count} року"
|
66
|
+
duration:
|
67
|
+
hours: "%{hour}h %{min}m"
|
68
|
+
less_than_10_seconds: "%{sec}s"
|
69
|
+
milliseconds: "%{ms}мс"
|
70
|
+
minutes: "%{min}м %{sec}с"
|
71
|
+
seconds: "%{sec}s"
|
72
|
+
good_job:
|
73
|
+
shared:
|
74
|
+
footer:
|
75
|
+
last_update_html: Оснаннє оновлення <time id="page-updated-at" datetime="%{time}">%{time}</time>
|
76
|
+
wording: Запам'ятай, ти теж робиш Good Job!
|
77
|
+
navbar:
|
78
|
+
cron_schedules: Cron
|
79
|
+
jobs: Задачі
|
80
|
+
live_poll: Живе Опитування
|
81
|
+
name: "GoodJob 👍"
|
82
|
+
processes: Процеси
|
83
|
+
status:
|
84
|
+
discarded: Відхилено
|
85
|
+
queued: В черзі
|
86
|
+
retried: Повторна спроба
|
87
|
+
running: Біг
|
88
|
+
scheduled: за розкладом
|
89
|
+
succeeded: вдалося
|
90
|
+
number:
|
91
|
+
format:
|
92
|
+
delimiter: " "
|
93
|
+
separator: ","
|
94
|
+
human:
|
95
|
+
decimal_units:
|
96
|
+
format: "%n%u"
|
97
|
+
units:
|
98
|
+
billion: Блн
|
99
|
+
million: Млн
|
100
|
+
quadrillion: Q
|
101
|
+
thousand: Тис
|
102
|
+
trillion: Трил
|
103
|
+
unit: ''
|
104
|
+
format:
|
105
|
+
delimiter: " "
|
106
|
+
precision: 3
|
107
|
+
separator: ","
|
@@ -41,5 +41,7 @@ class CreateGoodJobs < ActiveRecord::Migration<%= migration_version %>
|
|
41
41
|
add_index :good_jobs, [:cron_key, :cron_at], name: :index_good_jobs_on_cron_key_and_cron_at, unique: true
|
42
42
|
add_index :good_jobs, [:active_job_id], name: :index_good_jobs_on_active_job_id
|
43
43
|
add_index :good_jobs, [:finished_at], where: "retried_good_job_id IS NULL AND finished_at IS NOT NULL", name: :index_good_jobs_jobs_on_finished_at
|
44
|
+
add_index :good_jobs, [:priority, :created_at], order: { priority: "DESC NULLS LAST", created_at: :asc },
|
45
|
+
where: "finished_at IS NULL", name: :index_good_jobs_jobs_on_priority_created_at_when_unfinished
|
44
46
|
end
|
45
47
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
class CreateIndexGoodJobsJobsOnPriorityCreatedAtWhenUnfinished < ActiveRecord::Migration<%= migration_version %>
|
3
|
+
disable_ddl_transaction!
|
4
|
+
|
5
|
+
def change
|
6
|
+
reversible do |dir|
|
7
|
+
dir.up do
|
8
|
+
# Ensure this incremental update migration is idempotent
|
9
|
+
# with monolithic install migration.
|
10
|
+
return if connection.index_name_exists?(:good_jobs, :index_good_jobs_jobs_on_priority_created_at_when_unfinished)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
add_index :good_jobs, [:priority, :created_at], order: { priority: "DESC NULLS LAST", created_at: :asc },
|
15
|
+
where: "finished_at IS NULL", name: :index_good_jobs_jobs_on_priority_created_at_when_unfinished,
|
16
|
+
algorithm: :concurrently
|
17
|
+
end
|
18
|
+
end
|
data/lib/good_job/cli.rb
CHANGED
@@ -81,7 +81,12 @@ module GoodJob
|
|
81
81
|
desc: "Path to write daemonized Process ID (env var: GOOD_JOB_PIDFILE, default: tmp/pids/good_job.pid)"
|
82
82
|
method_option :probe_port,
|
83
83
|
type: :numeric,
|
84
|
+
banner: 'PORT',
|
84
85
|
desc: "Port for http health check (env var: GOOD_JOB_PROBE_PORT, default: nil)"
|
86
|
+
method_option :queue_select_limit,
|
87
|
+
type: :numeric,
|
88
|
+
banner: 'COUNT',
|
89
|
+
desc: "The number of queued jobs to select when polling for a job to run. (env var: GOOD_JOB_QUEUE_SELECT_LIMIT, default: nil)"
|
85
90
|
|
86
91
|
def start
|
87
92
|
set_up_application!
|
@@ -208,6 +208,19 @@ module GoodJob
|
|
208
208
|
cron.map { |cron_key, params| GoodJob::CronEntry.new(params.merge(key: cron_key)) }
|
209
209
|
end
|
210
210
|
|
211
|
+
# The number of queued jobs to select when polling for a job to run.
|
212
|
+
# This limit is intended to avoid locking a large number of rows when selecting eligible jobs
|
213
|
+
# from the queue. This value should be higher than the total number of threads across all good_job
|
214
|
+
# processes to ensure a thread can retrieve an eligible and unlocked job.
|
215
|
+
# @return [Integer, nil]
|
216
|
+
def queue_select_limit
|
217
|
+
(
|
218
|
+
options[:queue_select_limit] ||
|
219
|
+
rails_config[:queue_select_limit] ||
|
220
|
+
env['GOOD_JOB_QUEUE_SELECT_LIMIT']
|
221
|
+
)&.to_i
|
222
|
+
end
|
223
|
+
|
211
224
|
# Whether to destroy discarded jobs when cleaning up preserved jobs.
|
212
225
|
# This configuration is only used when {GoodJob.preserve_job_records} is +true+.
|
213
226
|
# @return [Boolean]
|
@@ -24,7 +24,7 @@ module GoodJob
|
|
24
24
|
# Perform the next eligible job
|
25
25
|
# @return [Object, nil] Returns job result or +nil+ if no job was found
|
26
26
|
def next
|
27
|
-
job_query.perform_with_advisory_lock(parsed_queues: parsed_queues)
|
27
|
+
job_query.perform_with_advisory_lock(parsed_queues: parsed_queues, queue_select_limit: GoodJob.configuration.queue_select_limit)
|
28
28
|
end
|
29
29
|
|
30
30
|
# Tests whether this performer should be used in GoodJob's current state.
|
data/lib/good_job/version.rb
CHANGED
data/lib/good_job.rb
CHANGED
@@ -57,7 +57,7 @@ module GoodJob
|
|
57
57
|
# By default, GoodJob deletes job records after the job is completed successfully.
|
58
58
|
# If you want to preserve jobs for latter inspection, set this to +true+.
|
59
59
|
# If you want to preserve only jobs that finished with error for latter inspection, set this to +:on_unhandled_error+.
|
60
|
-
# @return [Boolean, nil]
|
60
|
+
# @return [Boolean, Symbol, nil]
|
61
61
|
mattr_accessor :preserve_job_records, default: true
|
62
62
|
|
63
63
|
# @!attribute [rw] retry_on_unhandled_error
|
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: 3.
|
4
|
+
version: 3.6.1
|
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-11-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activejob
|
@@ -395,6 +395,7 @@ files:
|
|
395
395
|
- config/locales/es.yml
|
396
396
|
- config/locales/nl.yml
|
397
397
|
- config/locales/ru.yml
|
398
|
+
- config/locales/ua.yml
|
398
399
|
- config/routes.rb
|
399
400
|
- exe/good_job
|
400
401
|
- lib/active_job/queue_adapters/good_job_adapter.rb
|
@@ -402,6 +403,7 @@ files:
|
|
402
403
|
- lib/generators/good_job/templates/install/migrations/create_good_jobs.rb.erb
|
403
404
|
- lib/generators/good_job/templates/update/migrations/01_create_good_jobs.rb.erb
|
404
405
|
- lib/generators/good_job/templates/update/migrations/02_create_good_job_settings.rb.erb
|
406
|
+
- lib/generators/good_job/templates/update/migrations/03_create_index_good_jobs_jobs_on_priority_created_at_when_unfinished.rb.erb
|
405
407
|
- lib/generators/good_job/update_generator.rb
|
406
408
|
- lib/good_job.rb
|
407
409
|
- lib/good_job/active_job_extensions/concurrency.rb
|