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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1d98ed0a03595d8d2a70f2a811296cf01d1b0a4d9ff480d0a480fb91591ecafd
4
- data.tar.gz: 90307d30a72de7f7af5d33a7d64ec9eaa67bb42fc8e8179e48c96f22a0440b5e
3
+ metadata.gz: 3cdc97553bd567b0286d79c9a41187cf6a91478855f3469af986453a6c311f72
4
+ data.tar.gz: ef18533f656f9d1b2a4f131e5e745760ea8976a94f1c7f46a195c0042c9a35ff
5
5
  SHA512:
6
- metadata.gz: 710500b2704938b319863efe46d462296afa19f11177fb7e03f64e3cfa32872b1d1c8e793a58e6561e0f15a36125b4c3485be99edf6d1e2f8d9953da408ecb3b
7
- data.tar.gz: 6d0ae65d53a40340e846cd393a3936fe6ece5b7f5e2ccfdf17ee99f628f528135af11bb2714ee223f1e800b4ced333acc7616a153774d85d9889a0e17753a162
6
+ metadata.gz: d411c12b7b7b16c7787942d4a5998f319b44fea819a2caf756513d8b7e9f2c6ae44b8afe41576299d3c841aabda50c0669e087cfb13c885e86225fc25192d927
7
+ data.tar.gz: 0df610fb8995d52e24b55984399bbe59b645eb570891fdc262faf6482d324e42c647bdd33c18d7025323f99d57f5a6bd5455be75b3ccf60b31bfdcf65b0a4137
data/CHANGELOG.md CHANGED
@@ -1,17 +1,55 @@
1
1
  # Changelog
2
2
 
3
- ## [v3.5.1](https://github.com/bensheldon/good_job/tree/v3.5.1) (2022-10-20)
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.0...v3.5.1)
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
- - Assert cancelled jobs [\#724](https://github.com/bensheldon/good_job/issues/724)
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
- **Merged pull requests:**
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) ([tedhexaflow](https://github.com/tedhexaflow))
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) ([tedhexaflow](https://github.com/tedhexaflow))
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) ([tedhexaflow](https://github.com/tedhexaflow))
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) ([tedhexaflow](https://github.com/tedhexaflow))
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) ([tedhexaflow](https://github.com/tedhexaflow))
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) ([tedhexaflow](https://github.com/tedhexaflow))
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) ([tedhexaflow](https://github.com/tedhexaflow))
1606
- - Fix JRuby version number [\#193](https://github.com/bensheldon/good_job/pull/193) ([tedhexaflow](https://github.com/tedhexaflow))
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` (float) 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`.
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 middlware:
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 unecessary jobs from being enqueued, or race conditions when performing, for example when interacting with 3rd-party APIs.
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 enqeued at the given time interval.
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 to `v1.99.x` to `v2.0.x` again following the _minor_ upgrade process.
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 pid-based monitoring
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 enquing a job with GoodJob's inline adapter, the job will be executed immediately on the current thread; unhandled exceptions will be raised.
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 Issue or contribute to an [existing Issue](https://github.com/bensheldon/good_job/issues). Questions or suggestions are fantastic.
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
- destroy!
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.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  module GoodJob
3
3
  # GoodJob gem version.
4
- VERSION = '3.5.1'
4
+ VERSION = '3.6.1'
5
5
  end
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.5.1
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-10-20 00:00:00.000000000 Z
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