good_job 4.13.3 → 4.18.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +175 -0
- data/README.md +153 -21
- data/app/charts/good_job/performance_index_chart.rb +1 -1
- data/app/charts/good_job/performance_show_chart.rb +1 -1
- data/app/charts/good_job/scheduled_by_queue_chart.rb +21 -4
- data/app/controllers/good_job/cleaner_controller.rb +2 -2
- data/app/controllers/good_job/cron_entries_controller.rb +4 -0
- data/app/controllers/good_job/metrics_controller.rb +6 -1
- data/app/filters/good_job/base_filter.rb +5 -3
- data/app/filters/good_job/jobs_filter.rb +76 -37
- data/app/frontend/good_job/application.js +7 -16
- data/app/frontend/good_job/modules/bootstrap_init.js +21 -0
- data/app/frontend/good_job/modules/chart_controller.js +88 -0
- data/app/frontend/good_job/modules/checkbox_toggle_controller.js +28 -0
- data/app/frontend/good_job/modules/live_poll_controller.js +63 -0
- data/app/frontend/good_job/style.css +35 -0
- data/app/helpers/good_job/application_helper.rb +10 -0
- data/app/models/concerns/good_job/advisory_lockable.rb +461 -150
- data/app/models/concerns/good_job/error_events.rb +13 -6
- data/app/models/concerns/good_job/filterable.rb +34 -10
- data/app/models/good_job/base_record.rb +7 -0
- data/app/models/good_job/batch.rb +277 -2
- data/app/models/good_job/cron_entry.rb +26 -2
- data/app/models/good_job/execution.rb +14 -1
- data/app/models/good_job/execution_result.rb +4 -1
- data/app/models/good_job/job/lockable.rb +118 -0
- data/app/models/good_job/job.rb +198 -40
- data/app/models/good_job/setting.rb +10 -0
- data/app/views/good_job/cron_entries/index.html.erb +8 -6
- data/app/views/good_job/jobs/_executions.erb +2 -0
- data/app/views/good_job/jobs/_table.erb +43 -30
- data/app/views/good_job/shared/_chart_container.erb +8 -4
- data/app/views/good_job/shared/_filter.erb +6 -1
- data/app/views/good_job/shared/_navbar.erb +5 -3
- data/config/brakeman.ignore +57 -40
- data/config/locales/de.yml +2 -0
- data/config/locales/en.yml +2 -0
- data/config/locales/es.yml +2 -0
- data/config/locales/fr.yml +2 -0
- data/config/locales/it.yml +2 -0
- data/config/locales/ja.yml +2 -0
- data/config/locales/ko.yml +2 -0
- data/config/locales/nl.yml +2 -0
- data/config/locales/pt-BR.yml +2 -0
- data/config/locales/ru.yml +2 -0
- data/config/locales/tr.yml +2 -0
- data/config/locales/uk.yml +2 -0
- data/config/locales/zh-CN.yml +2 -0
- data/lib/generators/good_job/templates/install/migrations/create_good_jobs.rb.erb +21 -0
- data/lib/generators/good_job/templates/update/migrations/01_create_good_jobs.rb.erb +5 -0
- data/lib/generators/good_job/templates/update/migrations/07_add_lock_type_to_good_jobs.rb.erb +7 -0
- data/lib/generators/good_job/templates/update/migrations/08_add_index_good_jobs_for_candidate_dequeue_unlocked.rb.erb +14 -0
- data/lib/generators/good_job/templates/update/migrations/09_add_index_good_jobs_priority_scheduled_at.rb.erb +23 -0
- data/lib/generators/good_job/templates/update/migrations/10_add_index_good_jobs_queue_name.rb.erb +12 -0
- data/lib/generators/good_job/templates/update/migrations/11_add_index_good_jobs_created_at.rb.erb +12 -0
- data/lib/generators/good_job/templates/update/migrations/12_add_index_good_jobs_discarded.rb.erb +14 -0
- data/lib/generators/good_job/templates/update/migrations/13_add_index_good_jobs_scheduled_at_and_queue_name.rb.erb +12 -0
- data/lib/generators/good_job/templates/update/migrations/14_add_index_good_jobs_on_unfinished_or_errored.rb.erb +13 -0
- data/lib/good_job/active_job_extensions/concurrency.rb +181 -89
- data/lib/good_job/adapter.rb +85 -33
- data/lib/good_job/capsule_tracker.rb +4 -3
- data/lib/good_job/configuration.rb +24 -0
- data/lib/good_job/current_thread.rb +7 -0
- data/lib/good_job/engine.rb +4 -0
- data/lib/good_job/interrupt_error.rb +4 -1
- data/lib/good_job/interrupted_error.rb +9 -0
- data/lib/good_job/job_performer.rb +1 -1
- data/lib/good_job/notifier/process_heartbeat.rb +2 -4
- data/lib/good_job/notifier.rb +6 -4
- data/lib/good_job/overridable_connection.rb +6 -0
- data/lib/good_job/version.rb +1 -1
- data/lib/good_job.rb +27 -6
- metadata +16 -9
- data/app/frontend/good_job/modules/charts.js +0 -29
- data/app/frontend/good_job/modules/checkbox_toggle.js +0 -51
- data/app/frontend/good_job/modules/html_legend_plugin.js +0 -56
- data/app/frontend/good_job/modules/live_poll.js +0 -81
- data/app/frontend/good_job/modules/popovers.js +0 -7
- data/app/frontend/good_job/modules/toasts.js +0 -8
- data/app/views/good_job/shared/_chart.erb +0 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9d2eb2f4b216901a3f6abb9197facd9d9738c13fa4b61fcca2ec3ef4f2b64a6a
|
|
4
|
+
data.tar.gz: 7324f38c349116f108cbf216bffdc1207c2f0fd06ff136b7c0f0a8e0059d16a3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 62c30d3afcfa5c7f697f5b156cdced9cf45757f2e05e5e53657fbdf11c1fb0bab0e0e63dad516206737e697bb39d3985c4d0b2fcfb0933c6225fc724a6aca4c7
|
|
7
|
+
data.tar.gz: 0d5174c12282329ec248573bfd28a0a86538f7b65b94bf9918c7434bce691a43711017e595b47afb3e200125df5fc33c63b307a545c3d1c7ffd2eb11405741af
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,180 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [v4.18.2](https://github.com/bensheldon/good_job/tree/v4.18.2) (2026-04-20)
|
|
4
|
+
|
|
5
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v4.18.1...v4.18.2)
|
|
6
|
+
|
|
7
|
+
**Implemented enhancements:**
|
|
8
|
+
|
|
9
|
+
- Add dashboard indexes for queue\_name, created\_at, and discarded jobs [\#1754](https://github.com/bensheldon/good_job/pull/1754) ([AliOsm](https://github.com/AliOsm))
|
|
10
|
+
|
|
11
|
+
**Fixed bugs:**
|
|
12
|
+
|
|
13
|
+
- ArgumentError: GoodJob::Job\(...\) is not an ActiveRecord::Relation [\#1759](https://github.com/bensheldon/good_job/issues/1759)
|
|
14
|
+
- Fix ArgumentError when using perform\_throttle without a label [\#1760](https://github.com/bensheldon/good_job/pull/1760) ([bensheldon](https://github.com/bensheldon))
|
|
15
|
+
|
|
16
|
+
**Merged pull requests:**
|
|
17
|
+
|
|
18
|
+
- Speed up succeeded count via complement subtraction [\#1758](https://github.com/bensheldon/good_job/pull/1758) ([AliOsm](https://github.com/AliOsm))
|
|
19
|
+
- Speed up ScheduledByQueueChart with inner-query pushdown and \(scheduled\_at, queue\_name\) index [\#1757](https://github.com/bensheldon/good_job/pull/1757) ([AliOsm](https://github.com/AliOsm))
|
|
20
|
+
|
|
21
|
+
## [v4.18.1](https://github.com/bensheldon/good_job/tree/v4.18.1) (2026-04-18)
|
|
22
|
+
|
|
23
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v4.18.0...v4.18.1)
|
|
24
|
+
|
|
25
|
+
**Implemented enhancements:**
|
|
26
|
+
|
|
27
|
+
- Add an ActiveJob extension for throttling [\#315](https://github.com/bensheldon/good_job/issues/315)
|
|
28
|
+
|
|
29
|
+
**Fixed bugs:**
|
|
30
|
+
|
|
31
|
+
- Make job lifecycle methods lock-strategy-aware [\#1756](https://github.com/bensheldon/good_job/pull/1756) ([bensheldon](https://github.com/bensheldon))
|
|
32
|
+
- Fix concurrent PG connection access in ProcessHeartbeat\#refresh\_process [\#1755](https://github.com/bensheldon/good_job/pull/1755) ([bensheldon](https://github.com/bensheldon))
|
|
33
|
+
|
|
34
|
+
**Closed issues:**
|
|
35
|
+
|
|
36
|
+
- Job stuck in queued state and never picked up [\#1590](https://github.com/bensheldon/good_job/issues/1590)
|
|
37
|
+
- Refactor the Notifier to be more of a generic Reactor [\#811](https://github.com/bensheldon/good_job/issues/811)
|
|
38
|
+
|
|
39
|
+
## [v4.18.0](https://github.com/bensheldon/good_job/tree/v4.18.0) (2026-04-17)
|
|
40
|
+
|
|
41
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v4.17.0...v4.18.0)
|
|
42
|
+
|
|
43
|
+
**Implemented enhancements:**
|
|
44
|
+
|
|
45
|
+
- Make bulk action buttons responsive to job state filter [\#1751](https://github.com/bensheldon/good_job/pull/1751) ([bensheldon](https://github.com/bensheldon))
|
|
46
|
+
- Introduce GoodJob::InterruptedError string for interrupted execution records [\#1750](https://github.com/bensheldon/good_job/pull/1750) ([bensheldon](https://github.com/bensheldon))
|
|
47
|
+
- Add GoodJob.handled\_exceptions config; include NotImplementedError by default [\#1748](https://github.com/bensheldon/good_job/pull/1748) ([bensheldon](https://github.com/bensheldon))
|
|
48
|
+
- Allow adding the currently executing job to a batch [\#1746](https://github.com/bensheldon/good_job/pull/1746) ([bensheldon](https://github.com/bensheldon))
|
|
49
|
+
- Show split enabled/paused badge for cron schedules in navbar [\#1744](https://github.com/bensheldon/good_job/pull/1744) ([bensheldon](https://github.com/bensheldon))
|
|
50
|
+
|
|
51
|
+
**Fixed bugs:**
|
|
52
|
+
|
|
53
|
+
- Replace AR attributes API with explicit accessors; add breaking migration guardrails [\#1753](https://github.com/bensheldon/good_job/pull/1753) ([bensheldon](https://github.com/bensheldon))
|
|
54
|
+
- Fix dashboard pagination omitting jobs created in the same second [\#1749](https://github.com/bensheldon/good_job/pull/1749) ([bensheldon](https://github.com/bensheldon))
|
|
55
|
+
|
|
56
|
+
**Closed issues:**
|
|
57
|
+
|
|
58
|
+
- 4.15.0 crashes Tapioca with direct database access while requiring gems [\#1752](https://github.com/bensheldon/good_job/issues/1752)
|
|
59
|
+
- Dashboard omits jobs on the next page when they were created the same second as the preceding job [\#1725](https://github.com/bensheldon/good_job/issues/1725)
|
|
60
|
+
- `ActiveRecord::DuplicateMigrationVersionError` [\#1708](https://github.com/bensheldon/good_job/issues/1708)
|
|
61
|
+
- Adding to Process\_state in Dashboard [\#1702](https://github.com/bensheldon/good_job/issues/1702)
|
|
62
|
+
- jobs\#index perf Issue: ORDER BY uses wrong column for existing index [\#1675](https://github.com/bensheldon/good_job/issues/1675)
|
|
63
|
+
- `NotImplementedError` is always retried [\#1670](https://github.com/bensheldon/good_job/issues/1670)
|
|
64
|
+
- Job retry action doesn't work [\#1624](https://github.com/bensheldon/good_job/issues/1624)
|
|
65
|
+
- Adapt `AdvisoryLockable` to use more secure algorithm instead of MD5 [\#1623](https://github.com/bensheldon/good_job/issues/1623)
|
|
66
|
+
- Labels based on arguments [\#1622](https://github.com/bensheldon/good_job/issues/1622)
|
|
67
|
+
- How to start batches from cron jobs? \(create a batch within a job and add the current job to the batch\) [\#1611](https://github.com/bensheldon/good_job/issues/1611)
|
|
68
|
+
- What's the best way to auto truncate good\_job\_executions table [\#1607](https://github.com/bensheldon/good_job/issues/1607)
|
|
69
|
+
- Is the description of GOOD\_JOB\_EXECUTION\_MODE in the readme correct? [\#1584](https://github.com/bensheldon/good_job/issues/1584)
|
|
70
|
+
- Cron dashboard does not distinguish enabled/disabled jobs [\#1552](https://github.com/bensheldon/good_job/issues/1552)
|
|
71
|
+
- The Cron count in the Dashboard should show enabled and disabled counts [\#1313](https://github.com/bensheldon/good_job/issues/1313)
|
|
72
|
+
- Recording accurate job durations [\#1053](https://github.com/bensheldon/good_job/issues/1053)
|
|
73
|
+
- Jobs won't start until ActiveRecord loaded - gotcha in development environment [\#930](https://github.com/bensheldon/good_job/issues/930)
|
|
74
|
+
- Feature Request: Setting specific default locale for good job [\#921](https://github.com/bensheldon/good_job/issues/921)
|
|
75
|
+
- Avoid querying through `serialized_params` [\#876](https://github.com/bensheldon/good_job/issues/876)
|
|
76
|
+
- Allow separate \*\_keys for execution/enqueue/rate limits [\#753](https://github.com/bensheldon/good_job/issues/753)
|
|
77
|
+
|
|
78
|
+
**Merged pull requests:**
|
|
79
|
+
|
|
80
|
+
- Document initializer workaround for deferred autoloading in development [\#1747](https://github.com/bensheldon/good_job/pull/1747) ([bensheldon](https://github.com/bensheldon))
|
|
81
|
+
|
|
82
|
+
## [v4.17.0](https://github.com/bensheldon/good_job/tree/v4.17.0) (2026-04-17)
|
|
83
|
+
|
|
84
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v4.16.0...v4.17.0)
|
|
85
|
+
|
|
86
|
+
**Implemented enhancements:**
|
|
87
|
+
|
|
88
|
+
- Introduce advisory lock key customization support methods [\#1722](https://github.com/bensheldon/good_job/pull/1722) ([amkisko](https://github.com/amkisko))
|
|
89
|
+
|
|
90
|
+
**Merged pull requests:**
|
|
91
|
+
|
|
92
|
+
- Convert UI JavaScript modules to Stimulus controllers [\#1743](https://github.com/bensheldon/good_job/pull/1743) ([bensheldon](https://github.com/bensheldon))
|
|
93
|
+
- Development: Strip pg\_catalog namespace from enable\_extension in schema.rb [\#1742](https://github.com/bensheldon/good_job/pull/1742) ([bensheldon](https://github.com/bensheldon))
|
|
94
|
+
- Use system tmpdir for generator specs to eliminate bin/rails rename workaround [\#1740](https://github.com/bensheldon/good_job/pull/1740) ([bensheldon](https://github.com/bensheldon))
|
|
95
|
+
|
|
96
|
+
## [v4.16.0](https://github.com/bensheldon/good_job/tree/v4.16.0) (2026-04-14)
|
|
97
|
+
|
|
98
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v4.15.0...v4.16.0)
|
|
99
|
+
|
|
100
|
+
**Implemented enhancements:**
|
|
101
|
+
|
|
102
|
+
- Allow filtering by label on dashboard [\#1739](https://github.com/bensheldon/good_job/pull/1739) ([bensheldon](https://github.com/bensheldon))
|
|
103
|
+
- Allow multiple concurrency rules per job via labels [\#1700](https://github.com/bensheldon/good_job/pull/1700) ([bscofield](https://github.com/bscofield))
|
|
104
|
+
|
|
105
|
+
**Fixed bugs:**
|
|
106
|
+
|
|
107
|
+
- Fix advisory lock connection stickiness in block contexts [\#1736](https://github.com/bensheldon/good_job/pull/1736) ([bensheldon](https://github.com/bensheldon))
|
|
108
|
+
- Add JRuby 10 to testing matrix [\#1559](https://github.com/bensheldon/good_job/pull/1559) ([bensheldon](https://github.com/bensheldon))
|
|
109
|
+
|
|
110
|
+
**Closed issues:**
|
|
111
|
+
|
|
112
|
+
- Job duration misreported if interrupted [\#1723](https://github.com/bensheldon/good_job/issues/1723)
|
|
113
|
+
|
|
114
|
+
**Merged pull requests:**
|
|
115
|
+
|
|
116
|
+
- Use annotated git tag in release script [\#1741](https://github.com/bensheldon/good_job/pull/1741) ([bensheldon](https://github.com/bensheldon))
|
|
117
|
+
- Double single-thread scheduler integration test timeout on JRuby [\#1738](https://github.com/bensheldon/good_job/pull/1738) ([bensheldon](https://github.com/bensheldon))
|
|
118
|
+
- Fix JRuby test flakes for scheduler timeout and interrupted execution duration [\#1737](https://github.com/bensheldon/good_job/pull/1737) ([bensheldon](https://github.com/bensheldon))
|
|
119
|
+
- Count Advisory Locks and refactor advisory lock lifecycle [\#1735](https://github.com/bensheldon/good_job/pull/1735) ([bensheldon](https://github.com/bensheldon))
|
|
120
|
+
- Show interrupted execution recovery duration in dashboard [\#1733](https://github.com/bensheldon/good_job/pull/1733) ([bensheldon](https://github.com/bensheldon))
|
|
121
|
+
- chore: use `merge` to avoid mutate the query object [\#1717](https://github.com/bensheldon/good_job/pull/1717) ([luizkowalski](https://github.com/luizkowalski))
|
|
122
|
+
|
|
123
|
+
## [v4.15.0](https://github.com/bensheldon/good_job/tree/v4.15.0) (2026-04-09)
|
|
124
|
+
|
|
125
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v4.14.2...v4.15.0)
|
|
126
|
+
|
|
127
|
+
**Implemented enhancements:**
|
|
128
|
+
|
|
129
|
+
- Add opt-in "FOR NO KEY UPDATE SKIP LOCKED" job lock strategy and hybrid strategy for online migration [\#1731](https://github.com/bensheldon/good_job/pull/1731) ([bensheldon](https://github.com/bensheldon))
|
|
130
|
+
- Allow ordering by scheduled\_at instead of created\_at when dequeueing job [\#1645](https://github.com/bensheldon/good_job/pull/1645) ([lsylvester](https://github.com/lsylvester))
|
|
131
|
+
- Allow `GoodJob.preserve_job_records` to take a lambda that is callable after each job executes [\#1640](https://github.com/bensheldon/good_job/pull/1640) ([bensheldon](https://github.com/bensheldon))
|
|
132
|
+
|
|
133
|
+
**Merged pull requests:**
|
|
134
|
+
|
|
135
|
+
- Fix JRuby in development lockfile, with test [\#1734](https://github.com/bensheldon/good_job/pull/1734) ([bensheldon](https://github.com/bensheldon))
|
|
136
|
+
- Add herb to linter [\#1732](https://github.com/bensheldon/good_job/pull/1732) ([bensheldon](https://github.com/bensheldon))
|
|
137
|
+
- Update development dependencies; apply Rubocop to\_h lints [\#1728](https://github.com/bensheldon/good_job/pull/1728) ([bensheldon](https://github.com/bensheldon))
|
|
138
|
+
|
|
139
|
+
## [v4.14.2](https://github.com/bensheldon/good_job/tree/v4.14.2) (2026-04-06)
|
|
140
|
+
|
|
141
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v4.14.1...v4.14.2)
|
|
142
|
+
|
|
143
|
+
**Closed issues:**
|
|
144
|
+
|
|
145
|
+
- Incompatible with permanent\_connection\_checkout = :disallowed [\#1729](https://github.com/bensheldon/good_job/issues/1729)
|
|
146
|
+
|
|
147
|
+
**Merged pull requests:**
|
|
148
|
+
|
|
149
|
+
- Replace Base.connection with lease\_connection and with\_connection throughout [\#1730](https://github.com/bensheldon/good_job/pull/1730) ([bensheldon](https://github.com/bensheldon))
|
|
150
|
+
|
|
151
|
+
## [v4.14.1](https://github.com/bensheldon/good_job/tree/v4.14.1) (2026-04-03)
|
|
152
|
+
|
|
153
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v4.14.0...v4.14.1)
|
|
154
|
+
|
|
155
|
+
**Fixed bugs:**
|
|
156
|
+
|
|
157
|
+
- Fix N+1 queries on cron entries dashboard index page [\#1727](https://github.com/bensheldon/good_job/pull/1727) ([clinejj](https://github.com/clinejj))
|
|
158
|
+
|
|
159
|
+
## [v4.14.0](https://github.com/bensheldon/good_job/tree/v4.14.0) (2026-03-31)
|
|
160
|
+
|
|
161
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v4.13.3...v4.14.0)
|
|
162
|
+
|
|
163
|
+
**Implemented enhancements:**
|
|
164
|
+
|
|
165
|
+
- Consider using pg\_cron for Cron-style repeating/recurring jobs [\#328](https://github.com/bensheldon/good_job/issues/328)
|
|
166
|
+
- Add Batch.enqueue\_all for bulk-enqueuing multiple batches [\#1726](https://github.com/bensheldon/good_job/pull/1726) ([AliOsm](https://github.com/AliOsm))
|
|
167
|
+
- Allow perform\_all\_later to enqueue to Batches [\#1720](https://github.com/bensheldon/good_job/pull/1720) ([bensheldon](https://github.com/bensheldon))
|
|
168
|
+
|
|
169
|
+
**Closed issues:**
|
|
170
|
+
|
|
171
|
+
- perform\_all\_later not intercepted by Batch [\#1719](https://github.com/bensheldon/good_job/issues/1719)
|
|
172
|
+
- Deprecate and drop :on\_unhandled\_error option [\#1706](https://github.com/bensheldon/good_job/issues/1706)
|
|
173
|
+
|
|
174
|
+
**Merged pull requests:**
|
|
175
|
+
|
|
176
|
+
- Bump actions/upload-artifact from 6 to 7 [\#1718](https://github.com/bensheldon/good_job/pull/1718) ([dependabot[bot]](https://github.com/apps/dependabot))
|
|
177
|
+
|
|
3
178
|
## [v4.13.3](https://github.com/bensheldon/good_job/tree/v4.13.3) (2026-02-18)
|
|
4
179
|
|
|
5
180
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v4.13.2...v4.13.3)
|
data/README.md
CHANGED
|
@@ -134,7 +134,15 @@ For more of the story of GoodJob, read the [introductory blog post](https://isla
|
|
|
134
134
|
```
|
|
135
135
|
|
|
136
136
|
1. **In Rails' development environment**, by default, GoodJob's Adapter executes jobs `async` in a background thread pool in `rails server`.
|
|
137
|
-
- Because of Rails deferred autoloading, jobs enqueued via the `rails console` may not begin executing on a separate server process until the Rails application is fully initialized by loading a web page once.
|
|
137
|
+
- Because of Rails deferred autoloading, jobs enqueued via the `rails console` may not begin executing on a separate server process until the Rails application is fully initialized by loading a web page once. To force early initialization (so pre-existing jobs run immediately on boot), add the following to an initializer:
|
|
138
|
+
|
|
139
|
+
```ruby
|
|
140
|
+
# config/initializers/good_job.rb
|
|
141
|
+
Rails.configuration.after_initialize do
|
|
142
|
+
ActiveJob::Base && ActiveRecord::Base
|
|
143
|
+
end
|
|
144
|
+
```
|
|
145
|
+
|
|
138
146
|
- Remember, only Active Job's `perform_later` sends jobs to the queue adapter; Active Job's `perform_now` executes the job immediately and does not invoke the queue adapter. GoodJob is not involved in `perform_now` jobs.
|
|
139
147
|
1. **In Rails' test environment**, by default, GoodJob's Adapter executes jobs `inline` immediately in the current thread.
|
|
140
148
|
- Future-scheduled jobs can be executed with `GoodJob.perform_inline` using a tool like Timecop or `ActiveSupport::Testing::TimeHelpers`.
|
|
@@ -303,13 +311,13 @@ Available configuration options are:
|
|
|
303
311
|
- `cron_graceful_restart_period` (integer) when restarting cron, attempt to re-enqueue jobs that would have been enqueued by cron within this time period (e.g. `1.minute`). This should match the expected downtime during deploys.
|
|
304
312
|
- `enable_listen_notify` (boolean) whether to enqueue and read jobs with Postgres LISTEN/NOTIFY. Defaults to `true`. You can also set this with the environment variable `GOOD_JOB_ENABLE_LISTEN_NOTIFY`.
|
|
305
313
|
- `cron` (hash) cron configuration. Defaults to `{}`. You can also set this as a JSON string with the environment variable `GOOD_JOB_CRON`
|
|
306
|
-
- `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
|
|
307
|
-
- `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 `1209600` (14 days). Can also be set with the environment variable `GOOD_JOB_CLEANUP_PRESERVED_JOBS_BEFORE_SECONDS_AGO`.
|
|
314
|
+
- `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`.
|
|
315
|
+
- `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 `1209600` (14 days). Can also be set with the environment variable `GOOD_JOB_CLEANUP_PRESERVED_JOBS_BEFORE_SECONDS_AGO`.
|
|
308
316
|
- `cleanup_interval_jobs` (integer) Number of jobs a Scheduler will execute before cleaning up preserved jobs. Defaults to `1000`. Disable with `false`. Can also be set with the environment variable `GOOD_JOB_CLEANUP_INTERVAL_JOBS` and disabled with `0`).
|
|
309
317
|
- `cleanup_interval_seconds` (integer) Number of seconds a Scheduler will wait before cleaning up preserved jobs. Defaults to `600` (10 minutes). Disable with `false`. Can also be set with the environment variable `GOOD_JOB_CLEANUP_INTERVAL_SECONDS` and disabled with `0`).
|
|
310
318
|
- `inline_execution_respects_schedule` (boolean) Opt-in to future behavior of inline execution respecting scheduled jobs. Defaults to `false`.
|
|
311
319
|
- `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`).
|
|
312
|
-
- `preserve_job_records` (boolean) keeps job records in your database even after jobs are completed. (Default: `true`)
|
|
320
|
+
- `preserve_job_records` (boolean, symbol, or lambda) keeps job records in your database even after jobs are completed. If set to `true`, all job records are preserved. If set to `:on_unhandled_error`, only jobs that finished with an unhandled error are preserved. If set to a lambda, the lambda will be called with the error_event (e.g., `:discarded`, `:retry_stopped`, or `:unhandled`) and should return a boolean indicating whether to preserve the job. (Default: `true`)
|
|
313
321
|
- `advisory_lock_heartbeat` (boolean) whether to use an advisory lock for the purpose of determining whether an execeution process is active. (Default `true` in Development; `false` in other environments)
|
|
314
322
|
- `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`)
|
|
315
323
|
- `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:
|
|
@@ -375,7 +383,7 @@ GoodJob.active_record_parent_class = "ApplicationRecord"
|
|
|
375
383
|
The following options are also configurable via accessors, but you are encouraged to use the configuration attributes instead because these may be deprecated and removed in the future:
|
|
376
384
|
|
|
377
385
|
- **`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`.
|
|
378
|
-
- **`GoodJob.preserve_job_records`** (boolean) keeps job records in your database even after jobs are completed. (Default: `true`)
|
|
386
|
+
- **`GoodJob.preserve_job_records`** (boolean, symbol, or lambda) keeps job records in your database even after jobs are completed. If set to `true`, all job records are preserved. If set to `:on_unhandled_error`, only jobs that finished with an unhandled error are preserved. If set to a lambda, the lambda will be called with Active Job instance, and if it exists, the exception the error_event (e.g., `:discarded`, `:retry_stopped`, or `:unhandled`) and should return a boolean indicating whether to preserve the job (e.g. `-> (active_job, error, error_event) { !(active_job.is_a(Turbo::Streams::BroadcastStreamJob) || error_event == :retry_stopped`). (Default: `true`)
|
|
379
387
|
- **`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`)
|
|
380
388
|
- **`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.
|
|
381
389
|
|
|
@@ -538,6 +546,78 @@ Labels can be used to search jobs in the Dashboard. For example, to find all job
|
|
|
538
546
|
|
|
539
547
|
GoodJob can extend Active Job 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.
|
|
540
548
|
|
|
549
|
+
```ruby
|
|
550
|
+
class MyJob < ApplicationJob
|
|
551
|
+
include GoodJob::ActiveJobExtensions::Concurrency
|
|
552
|
+
|
|
553
|
+
# Define one or more concurrency rules. Each rule is scoped to a label,
|
|
554
|
+
# which is a value derived from the job's arguments at enqueue time and
|
|
555
|
+
# stored on the job record. Jobs must be enqueued with the matching label
|
|
556
|
+
# via `good_job_labels:` for the rule to apply.
|
|
557
|
+
#
|
|
558
|
+
# Multiple rules can be defined; they are evaluated in order and the first
|
|
559
|
+
# exceeded rule short-circuits the rest.
|
|
560
|
+
good_job_concurrency_rule(
|
|
561
|
+
# A label that scopes this rule. Can be a static String or a Lambda/Proc
|
|
562
|
+
# invoked in the context of the job instance. The rule only applies to jobs
|
|
563
|
+
# that were enqueued with this label in `good_job_labels`.
|
|
564
|
+
label: -> { arguments.first[:user_id] },
|
|
565
|
+
|
|
566
|
+
# Maximum number of unfinished jobs with this label to allow.
|
|
567
|
+
# Can be an Integer or Lambda/Proc invoked in the context of the job.
|
|
568
|
+
total_limit: 1,
|
|
569
|
+
|
|
570
|
+
# Or, if more control is needed:
|
|
571
|
+
# Maximum number of jobs with this label to be concurrently enqueued
|
|
572
|
+
# (excludes performing jobs). Can be an Integer or Lambda/Proc.
|
|
573
|
+
enqueue_limit: 2,
|
|
574
|
+
|
|
575
|
+
# Maximum number of jobs with this label to be concurrently performed
|
|
576
|
+
# (excludes enqueued jobs). Can be an Integer or Lambda/Proc.
|
|
577
|
+
perform_limit: 1,
|
|
578
|
+
|
|
579
|
+
# Maximum number of jobs with this label to be enqueued within the time
|
|
580
|
+
# period, looking backwards from now. Must be [count, period].
|
|
581
|
+
enqueue_throttle: [10, 1.minute],
|
|
582
|
+
|
|
583
|
+
# Maximum number of jobs with this label to be performed within the time
|
|
584
|
+
# period, looking backwards from now. Must be [count, period].
|
|
585
|
+
perform_throttle: [100, 1.hour],
|
|
586
|
+
|
|
587
|
+
# Note: Under heavy load, the total number of jobs may exceed the
|
|
588
|
+
# sum of `enqueue_limit` and `perform_limit` because of race conditions
|
|
589
|
+
# caused by imperfectly disjunctive states. If you need to constrain
|
|
590
|
+
# the total number of jobs, use `total_limit` instead. See #378.
|
|
591
|
+
)
|
|
592
|
+
# Additional rules
|
|
593
|
+
good_job_concurrency_rule(...)
|
|
594
|
+
good_job_concurrency_rule(...)
|
|
595
|
+
|
|
596
|
+
def perform(user_id:)
|
|
597
|
+
# do work
|
|
598
|
+
end
|
|
599
|
+
end
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
Jobs must be enqueued with the matching label for rules to take effect:
|
|
603
|
+
|
|
604
|
+
```ruby
|
|
605
|
+
MyJob.set(good_job_labels: [current_user.id]).perform_later(user_id: current_user.id)
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
#### How concurrency controls work
|
|
609
|
+
|
|
610
|
+
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).
|
|
611
|
+
|
|
612
|
+
- "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). Depending on your concurrency requirements, you may also want to manage concurrency through the number of GoodJob threads and processes that are performing a given queue.
|
|
613
|
+
- "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.
|
|
614
|
+
- First-in-first-out job execution order is not preserved when a job is retried with incremental back-off.
|
|
615
|
+
- For pessimistic usecases that collisions are expected, use number of threads/processes (e.g., `good_job --queues "serial:1;-serial:5"`) to control concurrency. It is also a good idea to use `perform_limit` as backstop.
|
|
616
|
+
|
|
617
|
+
#### Legacy: `good_job_control_concurrency_with`
|
|
618
|
+
|
|
619
|
+
The original concurrency interface uses a single configuration hash and scopes limits to a concurrency _key_ (a string derived from the job) stored on the job record, rather than a label. It remains fully supported.
|
|
620
|
+
|
|
541
621
|
```ruby
|
|
542
622
|
class MyJob < ApplicationJob
|
|
543
623
|
include GoodJob::ActiveJobExtensions::Concurrency
|
|
@@ -568,11 +648,6 @@ class MyJob < ApplicationJob
|
|
|
568
648
|
# with two elements: the number of jobs and the time period.
|
|
569
649
|
perform_throttle: [100, 1.hour],
|
|
570
650
|
|
|
571
|
-
# Note: Under heavy load, the total number of jobs may exceed the
|
|
572
|
-
# sum of `enqueue_limit` and `perform_limit` because of race conditions
|
|
573
|
-
# caused by imperfectly disjunctive states. If you need to constrain
|
|
574
|
-
# the total number of jobs, use `total_limit` instead. See #378.
|
|
575
|
-
|
|
576
651
|
# A unique key to be globally locked against.
|
|
577
652
|
# Can be String or Lambda/Proc that is invoked in the context of the job.
|
|
578
653
|
#
|
|
@@ -606,15 +681,6 @@ job = MyJob.perform_later("Alice", version: 'v1')
|
|
|
606
681
|
job.good_job_concurrency_key #=> "MyJob-default-Alice-v1"
|
|
607
682
|
```
|
|
608
683
|
|
|
609
|
-
#### How concurrency controls work
|
|
610
|
-
|
|
611
|
-
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).
|
|
612
|
-
|
|
613
|
-
- "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). Depending on your concurrency requirements, you may also want to manage concurrency through the number of GoodJob threads and processes that are performing a given queue.
|
|
614
|
-
- "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.
|
|
615
|
-
- First-in-first-out job execution order is not preserved when a job is retried with incremental back-off.
|
|
616
|
-
- For pessimistic usecases that collisions are expected, use number of threads/processes (e.g., `good_job --queues "serial:1;-serial:5"`) to control concurrency. It is also a good idea to use `perform_limit` as backstop.
|
|
617
|
-
|
|
618
684
|
### Cron-style repeating/recurring jobs
|
|
619
685
|
|
|
620
686
|
GoodJob can enqueue Active Job jobs on a recurring basis that can be used as a replacement for cron.
|
|
@@ -1041,7 +1107,7 @@ end
|
|
|
1041
1107
|
|
|
1042
1108
|
Note that when running jobs in `:inline` execution mode, `GoodJob.current_thread_running?` will always be truthy and `GoodJob.current_thread_shutting_down?` will always be falsey.
|
|
1043
1109
|
|
|
1044
|
-
Jobs will be automatically retried if the process is interrupted while performing a job and the job is unable to finish before the timeout or as the result of a `SIGKILL` or power failure.
|
|
1110
|
+
Jobs will be automatically retried if the process is interrupted while performing a job and the job is unable to finish before the timeout or as the result of a `SIGKILL` or power failure. The interrupted execution's error record will show `GoodJob::InterruptedError` to distinguish it from the rescuable `GoodJob::InterruptError` that is raised when the job is retried.
|
|
1045
1111
|
|
|
1046
1112
|
If you need more control over interrupt-caused retries, include the `GoodJob::ActiveJobExtensions::InterruptErrors` extension in your job class. When an interrupted job is retried, the extension will raise a `GoodJob::InterruptError` exception within the job, which allows you to use Active Job's `retry_on` and `discard_on` to control the behavior of the job.
|
|
1047
1113
|
|
|
@@ -1261,6 +1327,21 @@ To explain where this value is used, here is the pseudo-query that GoodJob uses
|
|
|
1261
1327
|
)
|
|
1262
1328
|
```
|
|
1263
1329
|
|
|
1330
|
+
By default, advisory lock keys are derived with `md5`. The string key (e.g. `good_jobs-<active_job_id>`) is hashed and truncated to a 64-bit bigint, which is the key space PostgreSQL advisory locks operate in. `md5` is used for its wide availability — no PostgreSQL extensions required — and good bit distribution, not for any cryptographic property.
|
|
1331
|
+
|
|
1332
|
+
If you need a different hash strategy, set it globally:
|
|
1333
|
+
|
|
1334
|
+
```ruby
|
|
1335
|
+
GoodJob::AdvisoryLockable.hash_function = "sha256"
|
|
1336
|
+
```
|
|
1337
|
+
|
|
1338
|
+
Supported values are `md5`, `sha1`, `sha224`, `sha256`, `sha384`, `sha512`, `hashtext`, and `uuid_v5`.
|
|
1339
|
+
|
|
1340
|
+
- `md5` (default): requires no PostgreSQL extensions.
|
|
1341
|
+
- `hashtext`: PostgreSQL's internal 32-bit hash; requires no extensions.
|
|
1342
|
+
- `sha*` algorithms require the `pgcrypto` extension (`digest()`).
|
|
1343
|
+
- `uuid_v5` requires the `uuid-ossp` extension (`uuid_generate_v5()`).
|
|
1344
|
+
|
|
1264
1345
|
### Execute jobs async / in-process
|
|
1265
1346
|
|
|
1266
1347
|
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:
|
|
@@ -1390,7 +1471,11 @@ To instead delete job records immediately after they are finished:
|
|
|
1390
1471
|
|
|
1391
1472
|
```ruby
|
|
1392
1473
|
# config/initializers/good_job.rb
|
|
1393
|
-
config.good_job.preserve_job_records = false # defaults to true; can also be `false
|
|
1474
|
+
config.good_job.preserve_job_records = false # defaults to true; can also be `false`, `:on_unhandled_error`, or a lambda that takes error_event argument
|
|
1475
|
+
|
|
1476
|
+
# Example of using a lambda to preserve only discarded jobs
|
|
1477
|
+
config.good_job.preserve_job_records = ->(error_event) { error_event == :discarded }
|
|
1478
|
+
|
|
1394
1479
|
```
|
|
1395
1480
|
|
|
1396
1481
|
GoodJob will automatically delete preserved job records after 14 days. The retention period, as well as the frequency GoodJob checks for deletable records can be configured:
|
|
@@ -1433,6 +1518,53 @@ travel_to(15.minutes.from_now) { GoodJob.perform_inline }
|
|
|
1433
1518
|
|
|
1434
1519
|
_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)._
|
|
1435
1520
|
|
|
1521
|
+
### SKIP LOCKED experimental mode
|
|
1522
|
+
|
|
1523
|
+
By default, GoodJob claims jobs using PostgreSQL advisory locks. As an alternative, GoodJob can use `SELECT FOR UPDATE SKIP LOCKED` to claim jobs, which writes the lock state directly to the `good_jobs` table rather than relying on session-level advisory locks.
|
|
1524
|
+
|
|
1525
|
+
Two strategies are available:
|
|
1526
|
+
|
|
1527
|
+
- **`:skiplocked`** — Claims jobs using `SELECT FOR UPDATE SKIP LOCKED` only. No advisory locks are held. Compatible with PgBouncer in transaction mode.
|
|
1528
|
+
- **`:hybrid`** — Claims jobs using `SELECT FOR UPDATE SKIP LOCKED` and _also_ acquires a session-level advisory lock on the job. Intended for rolling deploys where some workers are still using the default `:advisory` strategy.
|
|
1529
|
+
|
|
1530
|
+
Configure the lock strategy in an initializer or via environment variable:
|
|
1531
|
+
|
|
1532
|
+
```ruby
|
|
1533
|
+
# config/initializers/good_job.rb
|
|
1534
|
+
GoodJob.configure do |config|
|
|
1535
|
+
config.lock_strategy = :skiplocked
|
|
1536
|
+
end
|
|
1537
|
+
```
|
|
1538
|
+
|
|
1539
|
+
```bash
|
|
1540
|
+
GOOD_JOB_LOCK_STRATEGY=skiplocked
|
|
1541
|
+
```
|
|
1542
|
+
|
|
1543
|
+
All three strategies (`:advisory`, `:skiplocked`, `:hybrid`) can coexist safely during a rolling deploy — each strategy excludes jobs that are already locked by another worker regardless of which strategy that worker uses.
|
|
1544
|
+
|
|
1545
|
+
#### PgBouncer configuration
|
|
1546
|
+
|
|
1547
|
+
GoodJob's `:skiplocked` mode makes it compatible with PgBouncer in _transaction_ mode. In addition to setting the lock strategy, you must also disable the `LISTEN/NOTIFY` notifier (which requires a persistent connection) and rely on polling instead:
|
|
1548
|
+
|
|
1549
|
+
```ruby
|
|
1550
|
+
# config/initializers/good_job.rb
|
|
1551
|
+
GoodJob.configure do |config|
|
|
1552
|
+
config.lock_strategy = :skiplocked
|
|
1553
|
+
config.enable_listen_notify = false
|
|
1554
|
+
config.advisory_lock_heartbeat = false
|
|
1555
|
+
config.poll_interval = 5 # seconds; tune based on your latency tolerance
|
|
1556
|
+
end
|
|
1557
|
+
```
|
|
1558
|
+
|
|
1559
|
+
```bash
|
|
1560
|
+
GOOD_JOB_LOCK_STRATEGY=skiplocked
|
|
1561
|
+
GOOD_JOB_ENABLE_LISTEN_NOTIFY=false
|
|
1562
|
+
GOOD_JOB_ADVISORY_LOCK_HEARTBEAT=false
|
|
1563
|
+
GOOD_JOB_POLL_INTERVAL=5
|
|
1564
|
+
```
|
|
1565
|
+
|
|
1566
|
+
With these four settings, GoodJob will not hold any session-level state between queries and is safe to use behind PgBouncer in transaction mode.
|
|
1567
|
+
|
|
1436
1568
|
### PgBouncer compatibility
|
|
1437
1569
|
|
|
1438
1570
|
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.
|
|
@@ -23,7 +23,7 @@ module GoodJob
|
|
|
23
23
|
ORDER BY timestamp ASC
|
|
24
24
|
SQL
|
|
25
25
|
|
|
26
|
-
executions_data = GoodJob::Job.
|
|
26
|
+
executions_data = GoodJob::Job.connection_pool.with_connection { |conn| conn.exec_query(GoodJob::Job.pg_or_jdbc_query(sum_query), "GoodJob Performance Chart", start_end_binds) }
|
|
27
27
|
|
|
28
28
|
job_names = executions_data.reject { |d| d['sum'].nil? }.map { |d| d['job_class'] || BaseFilter::EMPTY }.uniq
|
|
29
29
|
labels = []
|
|
@@ -40,7 +40,7 @@ module GoodJob
|
|
|
40
40
|
]
|
|
41
41
|
labels = BUCKET_INTERVALS.map { |interval| GoodJob::ApplicationController.helpers.format_duration(interval) }
|
|
42
42
|
labels[-1] = I18n.t("good_job.performance.show.slow")
|
|
43
|
-
executions_data = GoodJob::Job.
|
|
43
|
+
executions_data = GoodJob::Job.connection_pool.with_connection { |conn| conn.exec_query(GoodJob::Job.pg_or_jdbc_query(sum_query), "GoodJob Performance Job Chart", binds) }
|
|
44
44
|
executions_data = executions_data.to_a.index_by { |data| data["bucket_index"] }
|
|
45
45
|
|
|
46
46
|
bucket_data = 0.upto(BUCKET_INTERVALS.count).map do |bucket_index|
|
|
@@ -8,6 +8,25 @@ module GoodJob
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def data
|
|
11
|
+
binds = start_end_binds
|
|
12
|
+
start_time, end_time = binds.map(&:value)
|
|
13
|
+
|
|
14
|
+
# Align the inner range predicate to the same hour buckets the outer generate_series
|
|
15
|
+
# produces. Doing the truncation in SQL (rather than in Ruby with beginning_of_hour)
|
|
16
|
+
# keeps both sides on the same grid even when the app's time zone has a fractional
|
|
17
|
+
# offset from UTC (e.g. IST +05:30). AR serializes Time binds as UTC, so the outer
|
|
18
|
+
# date_trunc operates in UTC, and this predicate must match that.
|
|
19
|
+
pushdown = <<~SQL.squish
|
|
20
|
+
"good_jobs"."scheduled_at" >= date_trunc('hour', ?::timestamp)
|
|
21
|
+
AND "good_jobs"."scheduled_at" < date_trunc('hour', ?::timestamp) + interval '1 hour'
|
|
22
|
+
SQL
|
|
23
|
+
|
|
24
|
+
inner_sql = @filter.filtered_query
|
|
25
|
+
.except(:select, :order)
|
|
26
|
+
.where(pushdown, start_time, end_time)
|
|
27
|
+
.select(:queue_name, :scheduled_at)
|
|
28
|
+
.to_sql
|
|
29
|
+
|
|
11
30
|
count_query = <<~SQL.squish
|
|
12
31
|
SELECT *
|
|
13
32
|
FROM generate_series(
|
|
@@ -20,15 +39,13 @@ module GoodJob
|
|
|
20
39
|
date_trunc('hour', scheduled_at) AS scheduled_at,
|
|
21
40
|
queue_name,
|
|
22
41
|
count(*) AS count
|
|
23
|
-
FROM (
|
|
24
|
-
#{@filter.filtered_query.except(:select, :order).select(:queue_name, :scheduled_at).to_sql}
|
|
25
|
-
) sources
|
|
42
|
+
FROM (#{inner_sql}) sources
|
|
26
43
|
GROUP BY date_trunc('hour', scheduled_at), queue_name
|
|
27
44
|
) sources ON sources.scheduled_at = timestamp
|
|
28
45
|
ORDER BY timestamp ASC
|
|
29
46
|
SQL
|
|
30
47
|
|
|
31
|
-
executions_data = GoodJob::Job.
|
|
48
|
+
executions_data = GoodJob::Job.connection_pool.with_connection { |conn| conn.exec_query(GoodJob::Job.pg_or_jdbc_query(count_query), "GoodJob Dashboard Chart", binds) }
|
|
32
49
|
|
|
33
50
|
queue_names = executions_data.reject { |d| d['count'].nil? }.map { |d| d['queue_name'] || BaseFilter::EMPTY }.uniq
|
|
34
51
|
labels = []
|
|
@@ -7,7 +7,7 @@ module GoodJob
|
|
|
7
7
|
|
|
8
8
|
@discarded_jobs_grouped_by_exception =
|
|
9
9
|
GoodJob::Job.discarded
|
|
10
|
-
.select(
|
|
10
|
+
.select(<<~SQL.squish)
|
|
11
11
|
SPLIT_PART(error, ': ', 1) AS exception_class,
|
|
12
12
|
count(id) AS failed,
|
|
13
13
|
COUNT(id) FILTER (WHERE "finished_at" > NOW() - INTERVAL '1 HOUR') AS last_1_hour,
|
|
@@ -21,7 +21,7 @@ module GoodJob
|
|
|
21
21
|
|
|
22
22
|
@discarded_jobs_grouped_by_class =
|
|
23
23
|
GoodJob::Job.discarded
|
|
24
|
-
.select(
|
|
24
|
+
.select(<<~SQL.squish)
|
|
25
25
|
job_class,
|
|
26
26
|
count(id) AS failed,
|
|
27
27
|
COUNT(*) FILTER (WHERE "finished_at" > NOW() - INTERVAL '1 HOUR') AS last_1_hour,
|
|
@@ -4,6 +4,10 @@ module GoodJob
|
|
|
4
4
|
class CronEntriesController < GoodJob::ApplicationController
|
|
5
5
|
def index
|
|
6
6
|
@cron_entries = CronEntry.all
|
|
7
|
+
@last_jobs = CronEntry.last_jobs_by_key(@cron_entries)
|
|
8
|
+
@enabled_states = GoodJob::Setting.cron_keys_enabled(
|
|
9
|
+
@cron_entries.map { |entry| [entry.key, entry.enabled_by_default?] }
|
|
10
|
+
)
|
|
7
11
|
end
|
|
8
12
|
|
|
9
13
|
def show
|
|
@@ -5,7 +5,10 @@ module GoodJob
|
|
|
5
5
|
def primary_nav
|
|
6
6
|
jobs_count = GoodJob::Job.count
|
|
7
7
|
batches_count = GoodJob::BatchRecord.all.size
|
|
8
|
-
|
|
8
|
+
cron_entries = GoodJob::CronEntry.all
|
|
9
|
+
cron_entries_count = cron_entries.size
|
|
10
|
+
cron_entries_enabled_count = cron_entries.count(&:enabled?)
|
|
11
|
+
cron_entries_paused_count = cron_entries_count - cron_entries_enabled_count
|
|
9
12
|
pauses_count = GoodJob::Setting.paused.values.sum(&:count)
|
|
10
13
|
processes_count = GoodJob::Process.active.count
|
|
11
14
|
discarded_count = GoodJob::Job.discarded.count
|
|
@@ -14,6 +17,8 @@ module GoodJob
|
|
|
14
17
|
jobs_count: helpers.number_to_human(jobs_count),
|
|
15
18
|
batches_count: helpers.number_to_human(batches_count),
|
|
16
19
|
cron_entries_count: helpers.number_to_human(cron_entries_count),
|
|
20
|
+
cron_entries_enabled_count: helpers.number_to_human(cron_entries_enabled_count),
|
|
21
|
+
cron_entries_paused_count: helpers.number_to_human(cron_entries_paused_count),
|
|
17
22
|
pauses_count: helpers.number_to_human(pauses_count),
|
|
18
23
|
processes_count: helpers.number_to_human(processes_count),
|
|
19
24
|
discarded_count: helpers.number_to_human(discarded_count),
|
|
@@ -13,8 +13,8 @@ module GoodJob
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def records
|
|
16
|
+
after_id = params[:after_id]
|
|
16
17
|
after_at = params[:after_at].present? ? Time.zone.parse(params[:after_at]) : nil
|
|
17
|
-
after_id = params[:after_id] if after_at
|
|
18
18
|
limit = params.fetch(:limit, DEFAULT_LIMIT)
|
|
19
19
|
|
|
20
20
|
query_for_records.display_all(
|
|
@@ -52,6 +52,7 @@ module GoodJob
|
|
|
52
52
|
def to_params(override = {})
|
|
53
53
|
{
|
|
54
54
|
job_class: params[:job_class],
|
|
55
|
+
label: params[:label],
|
|
55
56
|
limit: params[:limit],
|
|
56
57
|
queue_name: params[:queue_name],
|
|
57
58
|
query: params[:query],
|
|
@@ -75,10 +76,11 @@ module GoodJob
|
|
|
75
76
|
|
|
76
77
|
def next_page_params
|
|
77
78
|
order_column = ordered_by.first
|
|
79
|
+
last_record = records.last
|
|
78
80
|
|
|
79
81
|
{
|
|
80
|
-
after_at:
|
|
81
|
-
after_id:
|
|
82
|
+
after_at: last_record&.send(order_column)&.iso8601(6),
|
|
83
|
+
after_id: last_record&.id,
|
|
82
84
|
}.merge(to_params)
|
|
83
85
|
end
|
|
84
86
|
|