good_job 1.11.3 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +65 -0
- data/README.md +94 -5
- data/engine/app/views/good_job/active_jobs/show.html.erb +1 -1
- data/engine/app/views/good_job/dashboards/index.html.erb +2 -2
- data/engine/app/views/{shared → good_job/shared}/_chart.erb +2 -2
- data/engine/app/views/{shared → good_job/shared}/_jobs_table.erb +2 -2
- data/engine/app/views/{shared → good_job/shared}/icons/_check.html.erb +0 -0
- data/engine/app/views/{shared → good_job/shared}/icons/_exclamation.html.erb +0 -0
- data/engine/app/views/{shared → good_job/shared}/icons/_trash.html.erb +0 -0
- data/engine/app/views/layouts/good_job/base.html.erb +2 -2
- data/lib/generators/good_job/templates/install/migrations/create_good_jobs.rb.erb +1 -0
- data/lib/generators/good_job/templates/update/migrations/04_add_retried_good_job_id_to_good_jobs.rb +14 -0
- data/lib/good_job.rb +15 -10
- data/lib/good_job/active_job_extensions/concurrency.rb +7 -7
- data/lib/good_job/adapter.rb +2 -0
- data/lib/good_job/cli.rb +7 -2
- data/lib/good_job/configuration.rb +24 -0
- data/lib/good_job/cron_manager.rb +115 -0
- data/lib/good_job/current_execution.rb +16 -4
- data/lib/good_job/job.rb +59 -21
- data/lib/good_job/log_subscriber.rb +10 -0
- data/lib/good_job/railtie.rb +2 -0
- data/lib/good_job/version.rb +1 -1
- metadata +23 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b9c8c8a0842bb8b25ee147d92186ca5596d4dc0bdc1a9a7638a094dc80c41806
|
4
|
+
data.tar.gz: 30b21a20dbe0b6d81854e29448b1218494359dfeaf8ebc4e0a51d4aaa87ca101
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c2a0663d5af40fd294a9409c249677f73b683f5d50a3d2af58f120749429789de6f650ad58d61cc3704d0fa8a5ccc6681982ed106df37cf2d64856895e63892a
|
7
|
+
data.tar.gz: 0c8f717f2d763ecec47acc6dae85cadf153847d51cce0daf9c5b07f4aa953f4b57b612a79f0ae38335e9ed44192017567286448e761f1bfa688a5a6f04401820
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,70 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [v1.13.0](https://github.com/bensheldon/good_job/tree/v1.13.0) (2021-08-18)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v1.12.2...v1.13.0)
|
6
|
+
|
7
|
+
**Implemented enhancements:**
|
8
|
+
|
9
|
+
- Track if a GoodJob::Job has been subsequently retried [\#331](https://github.com/bensheldon/good_job/pull/331) ([bensheldon](https://github.com/bensheldon))
|
10
|
+
- Wrap and truncate error message, which can be a huge text [\#294](https://github.com/bensheldon/good_job/pull/294) ([morgoth](https://github.com/morgoth))
|
11
|
+
|
12
|
+
**Closed issues:**
|
13
|
+
|
14
|
+
- Add hyphen to lock string. e.g. "table\_name-column" instead of "table\_namecolumn [\#334](https://github.com/bensheldon/good_job/issues/334)
|
15
|
+
- Optimize db indexes in advance of v2.0.0 [\#332](https://github.com/bensheldon/good_job/issues/332)
|
16
|
+
- wait\_until in development? [\#330](https://github.com/bensheldon/good_job/issues/330)
|
17
|
+
- Race conditions in ActiveJob concurrency extension [\#325](https://github.com/bensheldon/good_job/issues/325)
|
18
|
+
- Store in database if a job has been ActiveJob retried [\#321](https://github.com/bensheldon/good_job/issues/321)
|
19
|
+
- Revisit and embrace concurrency control, scheduled jobs, and other extensions of ActiveJob [\#255](https://github.com/bensheldon/good_job/issues/255)
|
20
|
+
- Why 1 million jobs per day? [\#222](https://github.com/bensheldon/good_job/issues/222)
|
21
|
+
|
22
|
+
## [v1.12.2](https://github.com/bensheldon/good_job/tree/v1.12.2) (2021-08-13)
|
23
|
+
|
24
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v1.12.1...v1.12.2)
|
25
|
+
|
26
|
+
**Fixed bugs:**
|
27
|
+
|
28
|
+
- Fixes for race conditions in ActiveJob concurrency extension [\#326](https://github.com/bensheldon/good_job/pull/326) ([codyrobbins](https://github.com/codyrobbins))
|
29
|
+
|
30
|
+
**Merged pull requests:**
|
31
|
+
|
32
|
+
- On gem release, add instructions to author a Github Release [\#324](https://github.com/bensheldon/good_job/pull/324) ([bensheldon](https://github.com/bensheldon))
|
33
|
+
|
34
|
+
## [v1.12.1](https://github.com/bensheldon/good_job/tree/v1.12.1) (2021-08-05)
|
35
|
+
|
36
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v1.12.0...v1.12.1)
|
37
|
+
|
38
|
+
**Fixed bugs:**
|
39
|
+
|
40
|
+
- Ensure CLI can shutdown cleanly with multiple queues and timeout [\#319](https://github.com/bensheldon/good_job/pull/319) ([bensheldon](https://github.com/bensheldon))
|
41
|
+
|
42
|
+
**Closed issues:**
|
43
|
+
|
44
|
+
- Setting a shutdown timeout causes the CLI executor to throw an exception on shutdown. [\#318](https://github.com/bensheldon/good_job/issues/318)
|
45
|
+
- PgBouncer and prepared statements [\#269](https://github.com/bensheldon/good_job/issues/269)
|
46
|
+
- Question about locking internals [\#212](https://github.com/bensheldon/good_job/issues/212)
|
47
|
+
- Encoding::UndefinedConversionError \("\xE2" from ASCII-8BIT to UTF-8\) [\#198](https://github.com/bensheldon/good_job/issues/198)
|
48
|
+
- tools for managing a 'fleet' of processes [\#150](https://github.com/bensheldon/good_job/issues/150)
|
49
|
+
|
50
|
+
**Merged pull requests:**
|
51
|
+
|
52
|
+
- Fix Readme lint warnings [\#320](https://github.com/bensheldon/good_job/pull/320) ([bensheldon](https://github.com/bensheldon))
|
53
|
+
|
54
|
+
## [v1.12.0](https://github.com/bensheldon/good_job/tree/v1.12.0) (2021-07-27)
|
55
|
+
|
56
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v1.11.3...v1.12.0)
|
57
|
+
|
58
|
+
**Implemented enhancements:**
|
59
|
+
|
60
|
+
- Add the ability to schedule repeating / recurring / cron-like jobs [\#53](https://github.com/bensheldon/good_job/issues/53)
|
61
|
+
- Add cron-like support for recurring/repeating jobs [\#297](https://github.com/bensheldon/good_job/pull/297) ([bensheldon](https://github.com/bensheldon))
|
62
|
+
|
63
|
+
**Fixed bugs:**
|
64
|
+
|
65
|
+
- Place Dashboard shared view partials under `good_job` namespace [\#310](https://github.com/bensheldon/good_job/pull/310) ([bensheldon](https://github.com/bensheldon))
|
66
|
+
- Ensure Dashboard inline javascript has CSP nonce for strict Content-Security Policy [\#309](https://github.com/bensheldon/good_job/pull/309) ([bensheldon](https://github.com/bensheldon))
|
67
|
+
|
3
68
|
## [v1.11.3](https://github.com/bensheldon/good_job/tree/v1.11.3) (2021-07-25)
|
4
69
|
|
5
70
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v1.11.2...v1.11.3)
|
data/README.md
CHANGED
@@ -10,7 +10,7 @@ GoodJob is a multithreaded, Postgres-based, ActiveJob backend for Ruby on Rails.
|
|
10
10
|
- **Designed for ActiveJob.** Complete support for [async, queues, delays, priorities, timeouts, and retries](https://edgeguides.rubyonrails.org/active_job_basics.html) with near-zero configuration.
|
11
11
|
- **Built for Rails.** Fully adopts Ruby on Rails [threading and code execution guidelines](https://guides.rubyonrails.org/threading_and_code_execution.html) with [Concurrent::Ruby](https://github.com/ruby-concurrency/concurrent-ruby).
|
12
12
|
- **Backed by Postgres.** Relies upon Postgres integrity, session-level Advisory Locks to provide run-once safety and stay within the limits of `schema.rb`, and LISTEN/NOTIFY to reduce queuing latency.
|
13
|
-
- **For most workloads.** Targets full-stack teams, economy-minded solo developers, and applications that enqueue
|
13
|
+
- **For most workloads.** Targets full-stack teams, economy-minded solo developers, and applications that enqueue 1-million jobs/day and more.
|
14
14
|
|
15
15
|
For more of the story of GoodJob, read the [introductory blog post](https://island94.org/2020/07/introducing-goodjob-1-0).
|
16
16
|
|
@@ -38,7 +38,8 @@ For more of the story of GoodJob, read the [introductory blog post](https://isla
|
|
38
38
|
- [Configuration options](#configuration-options)
|
39
39
|
- [Global options](#global-options)
|
40
40
|
- [Dashboard](#dashboard)
|
41
|
-
- [ActiveJob
|
41
|
+
- [ActiveJob concurrency](#activejob-concurrency)
|
42
|
+
- [Cron-style repeating/recurring jobs](#cron-style-repeatingrecurring-jobs)
|
42
43
|
- [Updating](#updating)
|
43
44
|
- [Go deeper](#go-deeper)
|
44
45
|
- [Exceptions, retries, and reliability](#exceptions-retries-and-reliability)
|
@@ -51,6 +52,7 @@ For more of the story of GoodJob, read the [introductory blog post](https://isla
|
|
51
52
|
- [Execute jobs async / in-process](#execute-jobs-async--in-process)
|
52
53
|
- [Migrate to GoodJob from a different ActiveJob backend](#migrate-to-goodjob-from-a-different-activejob-backend)
|
53
54
|
- [Monitor and preserve worked jobs](#monitor-and-preserve-worked-jobs)
|
55
|
+
- [PgBouncer compatibility](#pgbouncer-compatibility)
|
54
56
|
- [Contribute](#contribute)
|
55
57
|
- [Gem development](#gem-development)
|
56
58
|
- [Release](#release)
|
@@ -156,6 +158,7 @@ Options:
|
|
156
158
|
[--poll-interval=SECONDS] # Interval between polls for available jobs in seconds (env var: GOOD_JOB_POLL_INTERVAL, default: 1)
|
157
159
|
[--max-cache=COUNT] # Maximum number of scheduled jobs to cache in memory (env var: GOOD_JOB_MAX_CACHE, default: 10000)
|
158
160
|
[--shutdown-timeout=SECONDS] # Number of seconds to wait for jobs to finish when shutting down before stopping the thread. (env var: GOOD_JOB_SHUTDOWN_TIMEOUT, default: -1 (forever))
|
161
|
+
[--enable-cron] # Whether to run cron process (default: false)
|
159
162
|
[--daemonize] # Run as a background daemon (default: false)
|
160
163
|
[--pidfile=PIDFILE] # Path to write daemonized Process ID (env var: GOOD_JOB_PIDFILE, default: tmp/pids/good_job.pid)
|
161
164
|
|
@@ -212,7 +215,8 @@ config.good_job.execution_mode = :async_server
|
|
212
215
|
config.good_job.max_threads = 5
|
213
216
|
config.good_job.poll_interval = 30 # seconds
|
214
217
|
config.good_job.shutdown_timeout = 25 # seconds
|
215
|
-
|
218
|
+
config.good_job.enable_cron = true
|
219
|
+
config.good_job.cron = { example: { cron: '0 * * * *', class: 'ExampleJob' } }
|
216
220
|
|
217
221
|
# ...or all at once.
|
218
222
|
config.good_job = {
|
@@ -220,6 +224,13 @@ config.good_job = {
|
|
220
224
|
max_threads: 5,
|
221
225
|
poll_interval: 30,
|
222
226
|
shutdown_timeout: 25,
|
227
|
+
enable_cron: true,
|
228
|
+
cron: {
|
229
|
+
example: {
|
230
|
+
cron: '0 * * * *',
|
231
|
+
class: 'ExampleJob'
|
232
|
+
},
|
233
|
+
},
|
223
234
|
}
|
224
235
|
```
|
225
236
|
|
@@ -235,6 +246,8 @@ Available configuration options are:
|
|
235
246
|
- `poll_interval` (integer) sets the number of seconds between polls for jobs when `execution_mode` is set to `:async` or `:async_server`. You can also set this with the environment variable `GOOD_JOB_POLL_INTERVAL`.
|
236
247
|
- `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`.
|
237
248
|
- `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`.
|
249
|
+
- `enable_cron` (boolean) whether to run cron process. Defaults to `false`. You can also set this with the environment variable `GOOD_JOB_ENABLE_CRON`.
|
250
|
+
- `cron` (hash) cron configuration. Defaults to `{}`. You can also set this as a JSON string with the environment variable `GOOD_JOB_CRON`
|
238
251
|
|
239
252
|
By default, GoodJob configures the following execution modes per environment:
|
240
253
|
|
@@ -320,9 +333,9 @@ GoodJob includes a Dashboard as a mountable `Rails::Engine`.
|
|
320
333
|
end
|
321
334
|
```
|
322
335
|
|
323
|
-
### ActiveJob
|
336
|
+
### ActiveJob concurrency
|
324
337
|
|
325
|
-
GoodJob can extend ActiveJob to provide limits on concurrently running jobs, either at time of _enqueue_ or at _perform_.
|
338
|
+
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.
|
326
339
|
|
327
340
|
**Note:** Limiting concurrency at _enqueue_ requires Rails 6.0+ because Rails 5.2 does not support `throw :abort` in ActiveJob callbacks.
|
328
341
|
|
@@ -349,6 +362,45 @@ class MyJob < ApplicationJob
|
|
349
362
|
end
|
350
363
|
```
|
351
364
|
|
365
|
+
When testing, the resulting concurrency key value can be inspected:
|
366
|
+
|
367
|
+
```ruby
|
368
|
+
job = MyJob.perform_later("Alice")
|
369
|
+
job.good_job_concurrency_key #=> "Unique-Alice"
|
370
|
+
```
|
371
|
+
|
372
|
+
### Cron-style repeating/recurring jobs
|
373
|
+
|
374
|
+
GoodJob can enqueue jobs on a recurring basis that can be used as a replacement for cron.
|
375
|
+
|
376
|
+
Cron-style jobs are run on every GoodJob process (e.g. CLI or `async` execution mode) when `config.good_job.enable_cron = true`; use GoodJob's [ActiveJob concurrency](#activejob-concurrency) extension to limit the number of jobs that are enqueued.
|
377
|
+
|
378
|
+
Cron-format is parsed by the [`fugit`](https://github.com/floraison/fugit) gem, which has support for seconds-level resolution (e.g. `* * * * * *`).
|
379
|
+
|
380
|
+
```ruby
|
381
|
+
# config/environments/application.rb or a specific environment e.g. production.rb
|
382
|
+
|
383
|
+
# Enable cron in this process; e.g. only run on the first Heroku worker process
|
384
|
+
config.good_job.enable_cron = ENV['DYNO'] == 'worker.1' # or `true` or via $GOOD_JOB_ENABLE_CRON
|
385
|
+
|
386
|
+
# Configure cron with a hash that has a unique key for each recurring job
|
387
|
+
config.good_job.cron = {
|
388
|
+
# Every 15 minutes, enqueue `ExampleJob.set(priority: -10).perform_later(52, name: "Alice")`
|
389
|
+
frequent_task: { # each recurring job must have a unique key
|
390
|
+
cron: "*/15 * * * *", # cron-style scheduling format by fugit gem
|
391
|
+
class: "ExampleJob", # reference the Job class with a string
|
392
|
+
args: [42, { name: "Alice" }], # arguments to pass; can also be a proc e.g. `-> { { when: Time.now } }`
|
393
|
+
set: { priority: -10 }, # additional ActiveJob properties; can also be a lambda/proc e.g. `-> { { priority: [1,2].sample } }`
|
394
|
+
description: "Something helpful", # optional description that appears in Dashboard (coming soon!)
|
395
|
+
},
|
396
|
+
another_task: {
|
397
|
+
cron: "0 0,12 * * *",
|
398
|
+
class: "AnotherJob",
|
399
|
+
},
|
400
|
+
# etc.
|
401
|
+
}
|
402
|
+
```
|
403
|
+
|
352
404
|
### Updating
|
353
405
|
|
354
406
|
GoodJob follows semantic versioning, though updates may be encouraged through deprecation warnings in minor versions.
|
@@ -636,6 +688,43 @@ It is also necessary to delete these preserved jobs from the database after a ce
|
|
636
688
|
$ bundle exec good_job cleanup_preserved_jobs --before-seconds-ago=86400
|
637
689
|
```
|
638
690
|
|
691
|
+
### PgBouncer compatibility
|
692
|
+
|
693
|
+
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.
|
694
|
+
|
695
|
+
A workaround to this limitation is to make a direct database connection available to GoodJob. With Rails 6.0's support for [multiple databases](https://guides.rubyonrails.org/active_record_multiple_databases.html), a direct connection to the database can be configured:
|
696
|
+
|
697
|
+
1. Define a direct connection to your database that is not proxied through PgBouncer, for example:
|
698
|
+
|
699
|
+
```yml
|
700
|
+
# config/database.yml
|
701
|
+
|
702
|
+
production:
|
703
|
+
primary:
|
704
|
+
url: postgres://pgbouncer_host/my_database
|
705
|
+
primary_direct:
|
706
|
+
url: postgres://database_host/my_database
|
707
|
+
```
|
708
|
+
|
709
|
+
1. Create a new ActiveRecord base class that uses the direct database connection
|
710
|
+
|
711
|
+
```ruby
|
712
|
+
# app/models/application_direct_record.rb
|
713
|
+
|
714
|
+
class ApplicationDirectRecord < ActiveRecord::Base
|
715
|
+
self.abstract_class = true
|
716
|
+
connects_to database: :primary_direct
|
717
|
+
end
|
718
|
+
```
|
719
|
+
|
720
|
+
1. Configure GoodJob to use the newly created ActiveRecord base class:
|
721
|
+
|
722
|
+
```ruby
|
723
|
+
# config/initializers/good_job.rb
|
724
|
+
|
725
|
+
GoodJob.active_record_parent_class = "ApplicationDirectRecord"
|
726
|
+
```
|
727
|
+
|
639
728
|
## Contribute
|
640
729
|
|
641
730
|
Contributions are welcomed and appreciated 🙏
|
@@ -1 +1 @@
|
|
1
|
-
<%= render 'shared/jobs_table', jobs: @jobs %>
|
1
|
+
<%= render 'good_job/shared/jobs_table', jobs: @jobs %>
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<div class="card my-3 p-6">
|
2
|
-
<%= render 'shared/chart', chart_data: @chart %>
|
2
|
+
<%= render 'good_job/shared/chart', chart_data: @chart %>
|
3
3
|
</div>
|
4
4
|
|
5
5
|
<div class='card mb-2'>
|
@@ -38,7 +38,7 @@
|
|
38
38
|
</div>
|
39
39
|
|
40
40
|
<% if @filter.jobs.present? %>
|
41
|
-
<%= render 'shared/jobs_table', jobs: @filter.jobs %>
|
41
|
+
<%= render 'good_job/shared/jobs_table', jobs: @filter.jobs %>
|
42
42
|
|
43
43
|
<nav aria-label="Job pagination" class="mt-3">
|
44
44
|
<ul class="pagination">
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<div id="chart"></div>
|
2
2
|
|
3
|
-
|
3
|
+
<%= javascript_tag nonce: true do %>
|
4
4
|
new Chartist.Line('#chart', <%== chart_data.to_json %>, {
|
5
5
|
height: '300px',
|
6
6
|
fullWidth: true,
|
@@ -49,4 +49,4 @@
|
|
49
49
|
tooltipEl.style.left = (event.offsetX || event.originalEvent.layerX) + tooltipEl.offsetWidth + 10 + 'px';
|
50
50
|
tooltipEl.style.top = (event.offsetY || event.originalEvent.layerY) + tooltipEl.offsetHeight - 20 + 'px';
|
51
51
|
}, true);
|
52
|
-
|
52
|
+
<% end %>
|
@@ -19,7 +19,7 @@
|
|
19
19
|
<td><%= job.serialized_params['job_class'] %></td>
|
20
20
|
<td><%= job.queue_name %></td>
|
21
21
|
<td><%= job.scheduled_at || job.created_at %></td>
|
22
|
-
<td><%= job.error %></td>
|
22
|
+
<td class="text-break"><%= truncate(job.error, length: 1_000) %></td>
|
23
23
|
<td>
|
24
24
|
<%= tag.button "Preview", type: "button", class: "btn btn-sm btn-outline-primary", role: "button",
|
25
25
|
data: {bs_toggle: "collapse", bs_target: "##{dom_id(job, 'params')}"},
|
@@ -29,7 +29,7 @@
|
|
29
29
|
</td>
|
30
30
|
<td>
|
31
31
|
<%= button_to job_path(job.id), method: :delete, class: "btn btn-sm btn-outline-danger", title: "Delete job" do %>
|
32
|
-
<%= render "shared/icons/trash" %>
|
32
|
+
<%= render "good_job/shared/icons/trash" %>
|
33
33
|
<% end %>
|
34
34
|
</td>
|
35
35
|
</tr>
|
File without changes
|
File without changes
|
File without changes
|
@@ -53,13 +53,13 @@
|
|
53
53
|
|
54
54
|
<% if notice %>
|
55
55
|
<div class="alert alert-success alert-dismissible fade show d-flex align-items-center offset-md-3 col-6" role="alert">
|
56
|
-
<%= render "shared/icons/check", class: "flex-shrink-0 me-2" %>
|
56
|
+
<%= render "good_job/shared/icons/check", class: "flex-shrink-0 me-2" %>
|
57
57
|
<div><%= notice %></div>
|
58
58
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
59
59
|
</div>
|
60
60
|
<% elsif alert %>
|
61
61
|
<div class="alert alert-warning alert-dismissible fade show d-flex align-items-center offset-md-3 col-6" role="alert">
|
62
|
-
<%= render "shared/icons/check", class: "flex-shrink-0 me-2" %>
|
62
|
+
<%= render "good_job/shared/icons/check", class: "flex-shrink-0 me-2" %>
|
63
63
|
<div><%= alert %></div>
|
64
64
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
65
65
|
</div>
|
@@ -16,6 +16,7 @@ class CreateGoodJobs < ActiveRecord::Migration[5.2]
|
|
16
16
|
t.uuid :active_job_id
|
17
17
|
t.text :concurrency_key
|
18
18
|
t.text :cron_key
|
19
|
+
t.uuid :retried_good_job_id
|
19
20
|
end
|
20
21
|
|
21
22
|
add_index :good_jobs, :scheduled_at, where: "(finished_at IS NULL)", name: "index_good_jobs_on_scheduled_at"
|
data/lib/generators/good_job/templates/update/migrations/04_add_retried_good_job_id_to_good_jobs.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
class AddRetriedGoodJobIdToGoodJobs < ActiveRecord::Migration[5.2]
|
3
|
+
def change
|
4
|
+
reversible do |dir|
|
5
|
+
dir.up do
|
6
|
+
# Ensure this incremental update migration is idempotent
|
7
|
+
# with monolithic install migration.
|
8
|
+
return if connection.column_exists?(:good_jobs, :retried_good_job_id)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
add_column :good_jobs, :retried_good_job_id, :uuid
|
13
|
+
end
|
14
|
+
end
|
data/lib/good_job.rb
CHANGED
@@ -106,16 +106,13 @@ module GoodJob
|
|
106
106
|
wait ? -1 : nil
|
107
107
|
end
|
108
108
|
|
109
|
-
|
110
|
-
_shutdown_all(executables, timeout: timeout)
|
109
|
+
_shutdown_all(_executables, timeout: timeout)
|
111
110
|
end
|
112
111
|
|
113
112
|
# Tests whether jobs have stopped executing.
|
114
113
|
# @return [Boolean] whether background threads are shut down
|
115
114
|
def self.shutdown?
|
116
|
-
|
117
|
-
Poller.instances.all?(&:shutdown?) &&
|
118
|
-
Scheduler.instances.all?(&:shutdown?)
|
115
|
+
_executables.all?(&:shutdown?)
|
119
116
|
end
|
120
117
|
|
121
118
|
# Stops and restarts executing jobs.
|
@@ -126,17 +123,16 @@ module GoodJob
|
|
126
123
|
# @param timeout [Numeric, nil] Seconds to wait for active threads to finish.
|
127
124
|
# @return [void]
|
128
125
|
def self.restart(timeout: -1)
|
129
|
-
|
130
|
-
_shutdown_all(executables, :restart, timeout: timeout)
|
126
|
+
_shutdown_all(_executables, :restart, timeout: timeout)
|
131
127
|
end
|
132
128
|
|
133
|
-
# Sends +#shutdown+ or +#restart+ to executable objects ({GoodJob::Notifier}, {GoodJob::Poller}, {GoodJob::Scheduler})
|
134
|
-
# @param executables [Array<Notifier, Poller, Scheduler, MultiScheduler>] Objects to shut down.
|
129
|
+
# Sends +#shutdown+ or +#restart+ to executable objects ({GoodJob::Notifier}, {GoodJob::Poller}, {GoodJob::Scheduler}, {GoodJob::MultiScheduler}, {GoodJob::CronManager})
|
130
|
+
# @param executables [Array<Notifier, Poller, Scheduler, MultiScheduler, CronManager>] Objects to shut down.
|
135
131
|
# @param method_name [:symbol] Method to call, e.g. +:shutdown+ or +:restart+.
|
136
132
|
# @param timeout [nil,Numeric]
|
137
133
|
# @return [void]
|
138
134
|
def self._shutdown_all(executables, method_name = :shutdown, timeout: -1)
|
139
|
-
if timeout.positive?
|
135
|
+
if timeout.is_a?(Numeric) && timeout.positive?
|
140
136
|
executables.each { |executable| executable.send(method_name, timeout: nil) }
|
141
137
|
|
142
138
|
stop_at = Time.current + timeout
|
@@ -146,5 +142,14 @@ module GoodJob
|
|
146
142
|
end
|
147
143
|
end
|
148
144
|
|
145
|
+
def self._executables
|
146
|
+
[].concat(
|
147
|
+
CronManager.instances,
|
148
|
+
Notifier.instances,
|
149
|
+
Poller.instances,
|
150
|
+
Scheduler.instances
|
151
|
+
)
|
152
|
+
end
|
153
|
+
|
149
154
|
ActiveSupport.run_load_hooks(:good_job, self)
|
150
155
|
end
|
@@ -9,21 +9,21 @@ module GoodJob
|
|
9
9
|
included do
|
10
10
|
class_attribute :good_job_concurrency_config, instance_accessor: false, default: {}
|
11
11
|
|
12
|
-
|
12
|
+
around_enqueue do |job, block|
|
13
13
|
# Always allow jobs to be retried because the current job's execution will complete momentarily
|
14
|
-
next if CurrentExecution.active_job_id == job.job_id
|
14
|
+
next(block.call) if CurrentExecution.active_job_id == job.job_id
|
15
15
|
|
16
16
|
limit = job.class.good_job_concurrency_config.fetch(:enqueue_limit, Float::INFINITY)
|
17
|
-
next if limit.blank? || (0...Float::INFINITY).exclude?(limit)
|
17
|
+
next(block.call) if limit.blank? || (0...Float::INFINITY).exclude?(limit)
|
18
18
|
|
19
19
|
key = job.good_job_concurrency_key
|
20
|
-
next if key.blank?
|
20
|
+
next(block.call) if key.blank?
|
21
21
|
|
22
22
|
GoodJob::Job.new.with_advisory_lock(key: key, function: "pg_advisory_lock") do
|
23
23
|
# TODO: Why is `unscoped` necessary? Nested scope is bleeding into subsequent query?
|
24
24
|
enqueue_concurrency = GoodJob::Job.unscoped.where(concurrency_key: key).unfinished.count
|
25
25
|
# The job has not yet been enqueued, so check if adding it will go over the limit
|
26
|
-
|
26
|
+
block.call unless enqueue_concurrency + 1 > limit
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
@@ -41,9 +41,9 @@ module GoodJob
|
|
41
41
|
next if key.blank?
|
42
42
|
|
43
43
|
GoodJob::Job.new.with_advisory_lock(key: key, function: "pg_advisory_lock") do
|
44
|
-
|
44
|
+
allowed_active_job_ids = GoodJob::Job.unscoped.where(concurrency_key: key).advisory_locked.order(Arel.sql("COALESCE(performed_at, scheduled_at, created_at) ASC")).limit(limit).pluck(:active_job_id)
|
45
45
|
# The current job has already been locked and will appear in the previous query
|
46
|
-
raise GoodJob::ActiveJobExtensions::Concurrency::ConcurrencyExceededError
|
46
|
+
raise GoodJob::ActiveJobExtensions::Concurrency::ConcurrencyExceededError unless allowed_active_job_ids.include? job.job_id
|
47
47
|
end
|
48
48
|
end
|
49
49
|
end
|
data/lib/good_job/adapter.rb
CHANGED
@@ -57,6 +57,8 @@ module GoodJob
|
|
57
57
|
@scheduler = GoodJob::Scheduler.from_configuration(@configuration, warm_cache_on_initialize: Rails.application.initialized?)
|
58
58
|
@notifier.recipients << [@scheduler, :create_thread]
|
59
59
|
@poller.recipients << [@scheduler, :create_thread]
|
60
|
+
|
61
|
+
@cron_manager = GoodJob::CronManager.new(@configuration.cron, start_on_initialize: Rails.application.initialized?) if @configuration.enable_cron?
|
60
62
|
end
|
61
63
|
end
|
62
64
|
|
data/lib/good_job/cli.rb
CHANGED
@@ -70,12 +70,16 @@ module GoodJob
|
|
70
70
|
type: :numeric,
|
71
71
|
banner: 'SECONDS',
|
72
72
|
desc: "Number of seconds to wait for jobs to finish when shutting down before stopping the thread. (env var: GOOD_JOB_SHUTDOWN_TIMEOUT, default: -1 (forever))"
|
73
|
+
method_option :enable_cron,
|
74
|
+
type: :boolean,
|
75
|
+
desc: "Whether to run cron process (default: false)"
|
73
76
|
method_option :daemonize,
|
74
77
|
type: :boolean,
|
75
78
|
desc: "Run as a background daemon (default: false)"
|
76
79
|
method_option :pidfile,
|
77
80
|
type: :string,
|
78
81
|
desc: "Path to write daemonized Process ID (env var: GOOD_JOB_PIDFILE, default: tmp/pids/good_job.pid)"
|
82
|
+
|
79
83
|
def start
|
80
84
|
set_up_application!
|
81
85
|
configuration = GoodJob::Configuration.new(options)
|
@@ -87,7 +91,7 @@ module GoodJob
|
|
87
91
|
scheduler = GoodJob::Scheduler.from_configuration(configuration, warm_cache_on_initialize: true)
|
88
92
|
notifier.recipients << [scheduler, :create_thread]
|
89
93
|
poller.recipients << [scheduler, :create_thread]
|
90
|
-
|
94
|
+
cron_manager = GoodJob::CronManager.new(configuration.cron, start_on_initialize: true) if configuration.enable_cron?
|
91
95
|
@stop_good_job_executable = false
|
92
96
|
%w[INT TERM].each do |signal|
|
93
97
|
trap(signal) { @stop_good_job_executable = true }
|
@@ -98,7 +102,7 @@ module GoodJob
|
|
98
102
|
break if @stop_good_job_executable || scheduler.shutdown? || notifier.shutdown?
|
99
103
|
end
|
100
104
|
|
101
|
-
executors = [notifier, poller, scheduler]
|
105
|
+
executors = [notifier, poller, cron_manager, scheduler].compact
|
102
106
|
GoodJob._shutdown_all(executors, timeout: configuration.shutdown_timeout)
|
103
107
|
end
|
104
108
|
|
@@ -124,6 +128,7 @@ module GoodJob
|
|
124
128
|
type: :numeric,
|
125
129
|
banner: 'SECONDS',
|
126
130
|
desc: "Delete records finished more than this many seconds ago (env var: GOOD_JOB_CLEANUP_PRESERVED_JOBS_BEFORE_SECONDS_AGO, default: 86400)"
|
131
|
+
|
127
132
|
def cleanup_preserved_jobs
|
128
133
|
set_up_application!
|
129
134
|
|
@@ -18,6 +18,8 @@ module GoodJob
|
|
18
18
|
DEFAULT_CLEANUP_PRESERVED_JOBS_BEFORE_SECONDS_AGO = 24 * 60 * 60
|
19
19
|
# Default to always wait for jobs to finish for {Adapter#shutdown}
|
20
20
|
DEFAULT_SHUTDOWN_TIMEOUT = -1
|
21
|
+
# Default to not running cron
|
22
|
+
DEFAULT_ENABLE_CRON = false
|
21
23
|
|
22
24
|
# The options that were explicitly set when initializing +Configuration+.
|
23
25
|
# @return [Hash]
|
@@ -129,6 +131,28 @@ module GoodJob
|
|
129
131
|
).to_f
|
130
132
|
end
|
131
133
|
|
134
|
+
# Whether to run cron
|
135
|
+
# @return [Boolean]
|
136
|
+
def enable_cron
|
137
|
+
value = ActiveModel::Type::Boolean.new.cast(
|
138
|
+
options[:enable_cron] ||
|
139
|
+
rails_config[:enable_cron] ||
|
140
|
+
env['GOOD_JOB_ENABLE_CRON'] ||
|
141
|
+
false
|
142
|
+
)
|
143
|
+
value && cron.size.positive?
|
144
|
+
end
|
145
|
+
alias enable_cron? enable_cron
|
146
|
+
|
147
|
+
def cron
|
148
|
+
env_cron = JSON.parse(ENV['GOOD_JOB_CRON']) if ENV['GOOD_JOB_CRON'].present?
|
149
|
+
|
150
|
+
options[:cron] ||
|
151
|
+
rails_config[:cron] ||
|
152
|
+
env_cron ||
|
153
|
+
{}
|
154
|
+
end
|
155
|
+
|
132
156
|
# Number of seconds to preserve jobs when using the +good_job cleanup_preserved_jobs+ CLI command.
|
133
157
|
# This configuration is only used when {GoodJob.preserve_job_records} is +true+.
|
134
158
|
# @return [Integer]
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "concurrent/hash"
|
3
|
+
require "concurrent/scheduled_task"
|
4
|
+
require "fugit"
|
5
|
+
|
6
|
+
module GoodJob # :nodoc:
|
7
|
+
#
|
8
|
+
# CronManagers enqueue jobs on a repeating schedule.
|
9
|
+
#
|
10
|
+
class CronManager
|
11
|
+
# @!attribute [r] instances
|
12
|
+
# @!scope class
|
13
|
+
# List of all instantiated CronManagers in the current process.
|
14
|
+
# @return [Array<GoodJob::CronManagers>, nil]
|
15
|
+
cattr_reader :instances, default: [], instance_reader: false
|
16
|
+
|
17
|
+
# Task observer for cron task
|
18
|
+
# @param time [Time]
|
19
|
+
# @param output [Object]
|
20
|
+
# @param thread_error [Exception]
|
21
|
+
def self.task_observer(time, output, thread_error) # rubocop:disable Lint/UnusedMethodArgument
|
22
|
+
return if thread_error.is_a? Concurrent::CancelledOperationError
|
23
|
+
|
24
|
+
GoodJob.on_thread_error.call(thread_error) if thread_error && GoodJob.on_thread_error.respond_to?(:call)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Job configuration to be scheduled
|
28
|
+
# @return [Hash]
|
29
|
+
attr_reader :schedules
|
30
|
+
|
31
|
+
# @param schedules [Hash]
|
32
|
+
# @param start_on_initialize [Boolean]
|
33
|
+
def initialize(schedules = {}, start_on_initialize: false)
|
34
|
+
@running = false
|
35
|
+
@schedules = schedules
|
36
|
+
@tasks = Concurrent::Hash.new
|
37
|
+
|
38
|
+
self.class.instances << self
|
39
|
+
|
40
|
+
start if start_on_initialize
|
41
|
+
end
|
42
|
+
|
43
|
+
# Schedule tasks that will enqueue jobs based on their schedule
|
44
|
+
def start
|
45
|
+
ActiveSupport::Notifications.instrument("cron_manager_start.good_job", cron_jobs: @schedules) do
|
46
|
+
@running = true
|
47
|
+
schedules.each_key { |cron_key| create_task(cron_key) }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Stop/cancel any scheduled tasks
|
52
|
+
# @param timeout [Numeric, nil] Unused but retained for compatibility
|
53
|
+
def shutdown(timeout: nil) # rubocop:disable Lint/UnusedMethodArgument
|
54
|
+
@running = false
|
55
|
+
@tasks.each do |_cron_key, task|
|
56
|
+
task.cancel
|
57
|
+
end
|
58
|
+
@tasks.clear
|
59
|
+
end
|
60
|
+
|
61
|
+
# Stop and restart
|
62
|
+
# @param timeout [Numeric, nil] Unused but retained for compatibility
|
63
|
+
def restart(timeout: nil) # rubocop:disable Lint/UnusedMethodArgument
|
64
|
+
shutdown
|
65
|
+
start
|
66
|
+
end
|
67
|
+
|
68
|
+
# Tests whether the manager is running.
|
69
|
+
# @return [Boolean, nil]
|
70
|
+
def running?
|
71
|
+
@running
|
72
|
+
end
|
73
|
+
|
74
|
+
# Tests whether the manager is shutdown.
|
75
|
+
# @return [Boolean, nil]
|
76
|
+
def shutdown?
|
77
|
+
!running?
|
78
|
+
end
|
79
|
+
|
80
|
+
# Enqueues a scheduled task
|
81
|
+
# @param cron_key [Symbol, String] the key within the schedule to use
|
82
|
+
def create_task(cron_key)
|
83
|
+
schedule = @schedules[cron_key]
|
84
|
+
return false if schedule.blank?
|
85
|
+
|
86
|
+
fugit = Fugit::Cron.parse(schedule.fetch(:cron))
|
87
|
+
delay = [(fugit.next_time - Time.current).to_f, 0].max
|
88
|
+
|
89
|
+
future = Concurrent::ScheduledTask.new(delay, args: [self, cron_key]) do |thr_scheduler, thr_cron_key|
|
90
|
+
# Re-schedule the next cron task before executing the current task
|
91
|
+
thr_scheduler.create_task(thr_cron_key)
|
92
|
+
|
93
|
+
CurrentExecution.reset
|
94
|
+
CurrentExecution.cron_key = thr_cron_key
|
95
|
+
|
96
|
+
Rails.application.executor.wrap do
|
97
|
+
schedule = thr_scheduler.schedules.fetch(thr_cron_key).with_indifferent_access
|
98
|
+
job_class = schedule.fetch(:class).constantize
|
99
|
+
|
100
|
+
job_set_value = schedule.fetch(:set, {})
|
101
|
+
job_set = job_set_value.respond_to?(:call) ? job_set_value.call : job_set_value
|
102
|
+
|
103
|
+
job_args_value = schedule.fetch(:args, [])
|
104
|
+
job_args = job_args_value.respond_to?(:call) ? job_args_value.call : job_args_value
|
105
|
+
|
106
|
+
job_class.set(job_set).perform_later(*job_args)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
@tasks[cron_key] = future
|
111
|
+
future.add_observer(self.class, :task_observer)
|
112
|
+
future.execute
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -5,11 +5,11 @@ module GoodJob
|
|
5
5
|
# Thread-local attributes for passing values from Instrumentation.
|
6
6
|
# (Cannot use ActiveSupport::CurrentAttributes because ActiveJob resets it)
|
7
7
|
module CurrentExecution
|
8
|
-
# @!attribute [rw]
|
8
|
+
# @!attribute [rw] cron_key
|
9
9
|
# @!scope class
|
10
|
-
#
|
10
|
+
# Cron Key
|
11
11
|
# @return [String, nil]
|
12
|
-
thread_mattr_accessor :
|
12
|
+
thread_mattr_accessor :cron_key
|
13
13
|
|
14
14
|
# @!attribute [rw] error_on_discard
|
15
15
|
# @!scope class
|
@@ -23,14 +23,26 @@ module GoodJob
|
|
23
23
|
# @return [Exception, nil]
|
24
24
|
thread_mattr_accessor :error_on_retry
|
25
25
|
|
26
|
+
# @!attribute [rw] good_job
|
27
|
+
# @!scope class
|
28
|
+
# Cron Key
|
29
|
+
# @return [GoodJob::Job, nil]
|
30
|
+
thread_mattr_accessor :good_job
|
31
|
+
|
26
32
|
# Resets attributes
|
27
33
|
# @return [void]
|
28
34
|
def self.reset
|
29
|
-
self.
|
35
|
+
self.cron_key = nil
|
36
|
+
self.good_job = nil
|
30
37
|
self.error_on_discard = nil
|
31
38
|
self.error_on_retry = nil
|
32
39
|
end
|
33
40
|
|
41
|
+
# @return [String] UUID of the currently executing GoodJob::Job
|
42
|
+
def self.active_job_id
|
43
|
+
good_job&.active_job_id
|
44
|
+
end
|
45
|
+
|
34
46
|
# @return [Integer] Current process ID
|
35
47
|
def self.process_id
|
36
48
|
Process.pid
|
data/lib/good_job/job.rb
CHANGED
@@ -52,6 +52,20 @@ module GoodJob
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
+
def self._migration_pending_warning
|
56
|
+
ActiveSupport::Deprecation.warn(<<~DEPRECATION)
|
57
|
+
GoodJob has pending database migrations. To create the migration files, run:
|
58
|
+
|
59
|
+
rails generate good_job:update
|
60
|
+
|
61
|
+
To apply the migration files, run:
|
62
|
+
|
63
|
+
rails db:migrate
|
64
|
+
|
65
|
+
DEPRECATION
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
|
55
69
|
# Get Jobs with given class name
|
56
70
|
# @!method with_job_class
|
57
71
|
# @!scope class
|
@@ -110,6 +124,18 @@ module GoodJob
|
|
110
124
|
# @return [ActiveRecord::Relation]
|
111
125
|
scope :running, -> { where.not(performed_at: nil).where(finished_at: nil) }
|
112
126
|
|
127
|
+
# Get Jobs that do not have subsequent retries
|
128
|
+
# @!method running
|
129
|
+
# @!scope class
|
130
|
+
# @return [ActiveRecord::Relation]
|
131
|
+
scope :head, -> { where(retried_good_job_id: nil) }
|
132
|
+
|
133
|
+
# Get Jobs have errored that will not be retried further
|
134
|
+
# @!method running
|
135
|
+
# @!scope class
|
136
|
+
# @return [ActiveRecord::Relation]
|
137
|
+
scope :dead, -> { head.where.not(error: nil) }
|
138
|
+
|
113
139
|
# Get Jobs on queues that match the given queue string.
|
114
140
|
# @!method queue_string(string)
|
115
141
|
# @!scope class
|
@@ -209,31 +235,23 @@ module GoodJob
|
|
209
235
|
if column_names.include?('active_job_id')
|
210
236
|
good_job_args[:active_job_id] = active_job.job_id
|
211
237
|
else
|
212
|
-
|
213
|
-
GoodJob has pending database migrations. To create the migration files, run:
|
214
|
-
|
215
|
-
rails generate good_job:update
|
216
|
-
|
217
|
-
To apply the migration files, run:
|
218
|
-
|
219
|
-
rails db:migrate
|
220
|
-
|
221
|
-
DEPRECATION
|
238
|
+
_migration_pending_warning
|
222
239
|
end
|
223
240
|
|
224
241
|
if column_names.include?('concurrency_key')
|
225
242
|
good_job_args[:concurrency_key] = active_job.good_job_concurrency_key if active_job.respond_to?(:good_job_concurrency_key)
|
226
243
|
else
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
rails generate good_job:update
|
231
|
-
|
232
|
-
To apply the migration files, run:
|
233
|
-
|
234
|
-
rails db:migrate
|
244
|
+
_migration_pending_warning
|
245
|
+
end
|
235
246
|
|
236
|
-
|
247
|
+
if column_names.include?('cron_key')
|
248
|
+
if CurrentExecution.cron_key
|
249
|
+
good_job_args[:cron_key] = CurrentExecution.cron_key
|
250
|
+
elsif CurrentExecution.active_job_id == active_job.job_id
|
251
|
+
good_job_args[:cron_key] = CurrentExecution.good_job.cron_key
|
252
|
+
end
|
253
|
+
else
|
254
|
+
_migration_pending_warning
|
237
255
|
end
|
238
256
|
|
239
257
|
good_job = GoodJob::Job.new(**good_job_args)
|
@@ -243,6 +261,12 @@ module GoodJob
|
|
243
261
|
good_job.save!
|
244
262
|
active_job.provider_job_id = good_job.id
|
245
263
|
|
264
|
+
if column_names.include?('retried_good_job_id')
|
265
|
+
CurrentExecution.good_job.retried_good_job_id = good_job.id if CurrentExecution.good_job && CurrentExecution.good_job.active_job_id == active_job.job_id
|
266
|
+
else
|
267
|
+
_migration_pending_warning
|
268
|
+
end
|
269
|
+
|
246
270
|
good_job
|
247
271
|
end
|
248
272
|
end
|
@@ -282,7 +306,21 @@ module GoodJob
|
|
282
306
|
end
|
283
307
|
|
284
308
|
def active_job_id
|
285
|
-
|
309
|
+
if self.class.column_names.include?('active_job_id')
|
310
|
+
super
|
311
|
+
else
|
312
|
+
self.class._migration_pending_warning
|
313
|
+
serialized_params['job_id']
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
def cron_key
|
318
|
+
if self.class.column_names.include?('cron_key')
|
319
|
+
super
|
320
|
+
else
|
321
|
+
self.class._migration_pending_warning
|
322
|
+
nil
|
323
|
+
end
|
286
324
|
end
|
287
325
|
|
288
326
|
private
|
@@ -294,7 +332,7 @@ module GoodJob
|
|
294
332
|
)
|
295
333
|
|
296
334
|
GoodJob::CurrentExecution.reset
|
297
|
-
GoodJob::CurrentExecution.
|
335
|
+
GoodJob::CurrentExecution.good_job = self
|
298
336
|
ActiveSupport::Notifications.instrument("perform_job.good_job", { good_job: self, process_id: GoodJob::CurrentExecution.process_id, thread_name: GoodJob::CurrentExecution.thread_name }) do
|
299
337
|
value = ActiveJob::Base.execute(params)
|
300
338
|
|
@@ -57,6 +57,16 @@ module GoodJob
|
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
60
|
+
# @!macro notification_responder
|
61
|
+
def cron_manager_start(event)
|
62
|
+
cron_jobs = event.payload[:cron_jobs]
|
63
|
+
cron_jobs_count = cron_jobs.size
|
64
|
+
|
65
|
+
info do
|
66
|
+
"GoodJob started cron with #{cron_jobs_count} #{'jobs'.pluralize(cron_jobs_count)}."
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
60
70
|
# @!macro notification_responder
|
61
71
|
def scheduler_shutdown_start(event)
|
62
72
|
process_id = event.payload[:process_id]
|
data/lib/good_job/railtie.rb
CHANGED
@@ -3,6 +3,7 @@ module GoodJob
|
|
3
3
|
# Ruby on Rails integration.
|
4
4
|
class Railtie < ::Rails::Railtie
|
5
5
|
config.good_job = ActiveSupport::OrderedOptions.new
|
6
|
+
config.good_job.cron = {}
|
6
7
|
|
7
8
|
initializer "good_job.logger" do |_app|
|
8
9
|
ActiveSupport.on_load(:good_job) do
|
@@ -23,6 +24,7 @@ module GoodJob
|
|
23
24
|
|
24
25
|
config.after_initialize do
|
25
26
|
GoodJob::Scheduler.instances.each(&:warm_cache)
|
27
|
+
GoodJob::CronManager.instances.each(&:start)
|
26
28
|
end
|
27
29
|
end
|
28
30
|
end
|
data/lib/good_job/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: good_job
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.13.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Sheldon
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-08-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activejob
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: 1.0.2
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: fugit
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.1'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.1'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: railties
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -345,12 +359,12 @@ files:
|
|
345
359
|
- engine/app/helpers/good_job/application_helper.rb
|
346
360
|
- engine/app/views/good_job/active_jobs/show.html.erb
|
347
361
|
- engine/app/views/good_job/dashboards/index.html.erb
|
362
|
+
- engine/app/views/good_job/shared/_chart.erb
|
363
|
+
- engine/app/views/good_job/shared/_jobs_table.erb
|
364
|
+
- engine/app/views/good_job/shared/icons/_check.html.erb
|
365
|
+
- engine/app/views/good_job/shared/icons/_exclamation.html.erb
|
366
|
+
- engine/app/views/good_job/shared/icons/_trash.html.erb
|
348
367
|
- engine/app/views/layouts/good_job/base.html.erb
|
349
|
-
- engine/app/views/shared/_chart.erb
|
350
|
-
- engine/app/views/shared/_jobs_table.erb
|
351
|
-
- engine/app/views/shared/icons/_check.html.erb
|
352
|
-
- engine/app/views/shared/icons/_exclamation.html.erb
|
353
|
-
- engine/app/views/shared/icons/_trash.html.erb
|
354
368
|
- engine/config/routes.rb
|
355
369
|
- engine/lib/good_job/engine.rb
|
356
370
|
- exe/good_job
|
@@ -360,6 +374,7 @@ files:
|
|
360
374
|
- lib/generators/good_job/templates/update/migrations/01_create_good_jobs.rb
|
361
375
|
- lib/generators/good_job/templates/update/migrations/02_add_active_job_id_concurrency_key_cron_key_to_good_jobs.rb
|
362
376
|
- lib/generators/good_job/templates/update/migrations/03_add_active_job_id_index_and_concurrency_key_index_to_good_jobs.rb
|
377
|
+
- lib/generators/good_job/templates/update/migrations/04_add_retried_good_job_id_to_good_jobs.rb
|
363
378
|
- lib/generators/good_job/update_generator.rb
|
364
379
|
- lib/good_job.rb
|
365
380
|
- lib/good_job/active_job_extensions.rb
|
@@ -367,6 +382,7 @@ files:
|
|
367
382
|
- lib/good_job/adapter.rb
|
368
383
|
- lib/good_job/cli.rb
|
369
384
|
- lib/good_job/configuration.rb
|
385
|
+
- lib/good_job/cron_manager.rb
|
370
386
|
- lib/good_job/current_execution.rb
|
371
387
|
- lib/good_job/daemon.rb
|
372
388
|
- lib/good_job/execution_result.rb
|