good_job 2.7.1 → 2.8.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 +61 -0
- data/README.md +49 -32
- data/engine/app/charts/good_job/scheduled_by_queue_chart.rb +4 -1
- data/engine/app/controllers/good_job/processes_controller.rb +8 -0
- data/engine/app/views/good_job/processes/index.html.erb +40 -0
- data/engine/app/views/layouts/good_job/base.html.erb +7 -16
- data/engine/config/routes.rb +9 -6
- data/lib/generators/good_job/templates/install/migrations/create_good_jobs.rb.erb +5 -0
- data/lib/generators/good_job/templates/update/migrations/04_create_good_job_processes.rb.erb +17 -0
- data/lib/good_job/active_job_job.rb +1 -4
- data/lib/good_job/adapter.rb +34 -11
- data/lib/good_job/assignable_connection.rb +38 -0
- data/lib/good_job/base_record.rb +20 -0
- data/lib/good_job/cleanup_tracker.rb +38 -0
- data/lib/good_job/cli.rb +1 -0
- data/lib/good_job/configuration.rb +42 -18
- data/lib/good_job/cron_manager.rb +1 -1
- data/lib/good_job/current_thread.rb +1 -1
- data/lib/good_job/daemon.rb +3 -3
- data/lib/good_job/execution.rb +2 -15
- data/lib/good_job/job_performer.rb +6 -0
- data/lib/good_job/lockable.rb +14 -4
- data/lib/good_job/notifier/process_registration.rb +34 -0
- data/lib/good_job/notifier.rb +54 -26
- data/lib/good_job/poller.rb +1 -1
- data/lib/good_job/probe_server.rb +1 -1
- data/lib/good_job/process.rb +82 -0
- data/lib/good_job/railtie.rb +16 -4
- data/lib/good_job/scheduler.rb +41 -6
- data/lib/good_job/version.rb +1 -1
- data/lib/good_job.rb +11 -2
- metadata +10 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ae78ac0322802107488f4a8b55963665cf9b3f173eb05174382bf05afdcef5a6
|
|
4
|
+
data.tar.gz: 2f41dd00281bcf2d0a2c29f6124de36964dcc730436f1cbd2f4c740c873263ed
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0ec2d40fdd87f293f8372e27040c7b14f8668645f50bc968ce91ec758202a400bb7c4a9ecb2e31a5827d59717dc3069840d5402981e1dc9f98c113120e259258
|
|
7
|
+
data.tar.gz: 0dd784cee33ab996ad332d1bfc371c357ffe3fb9727629096cff7189cc391195c109400240569444221a2ebf3340e2ab0d2d1c7bbd2830ee1c5b2ada9117d995
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,66 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [v2.8.0](https://github.com/bensheldon/good_job/tree/v2.8.0) (2021-12-31)
|
|
4
|
+
|
|
5
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v2.7.4...v2.8.0)
|
|
6
|
+
|
|
7
|
+
**Implemented enhancements:**
|
|
8
|
+
|
|
9
|
+
- GoodJob should automatically clean up after itself and delete old job records [\#412](https://github.com/bensheldon/good_job/issues/412)
|
|
10
|
+
- Track processes in the database and on the Dashboard [\#472](https://github.com/bensheldon/good_job/pull/472) ([bensheldon](https://github.com/bensheldon))
|
|
11
|
+
- Allow Scheduler to automatically clean up preserved jobs every N jobs or seconds [\#465](https://github.com/bensheldon/good_job/pull/465) ([bensheldon](https://github.com/bensheldon))
|
|
12
|
+
|
|
13
|
+
**Closed issues:**
|
|
14
|
+
|
|
15
|
+
- Is there a way to show how many worker/process is running currently [\#471](https://github.com/bensheldon/good_job/issues/471)
|
|
16
|
+
- Jobs stuck in the unfinished state [\#448](https://github.com/bensheldon/good_job/issues/448)
|
|
17
|
+
|
|
18
|
+
**Merged pull requests:**
|
|
19
|
+
|
|
20
|
+
- Doublequote Ruby 3.0 in testing matrix [\#473](https://github.com/bensheldon/good_job/pull/473) ([bensheldon](https://github.com/bensheldon))
|
|
21
|
+
- Have demo CleanupJob use GoodJob.cleanup\_preserved\_jobs [\#470](https://github.com/bensheldon/good_job/pull/470) ([bensheldon](https://github.com/bensheldon))
|
|
22
|
+
- Test with Rails 7.0.0 [\#469](https://github.com/bensheldon/good_job/pull/469) ([aried3r](https://github.com/aried3r))
|
|
23
|
+
|
|
24
|
+
## [v2.7.4](https://github.com/bensheldon/good_job/tree/v2.7.4) (2021-12-16)
|
|
25
|
+
|
|
26
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v2.7.3...v2.7.4)
|
|
27
|
+
|
|
28
|
+
**Fixed bugs:**
|
|
29
|
+
|
|
30
|
+
- Add nonce: true to javascript\_include\_tag in dashboard [\#468](https://github.com/bensheldon/good_job/pull/468) ([bouk](https://github.com/bouk))
|
|
31
|
+
|
|
32
|
+
**Closed issues:**
|
|
33
|
+
|
|
34
|
+
- Add nonce: true to engine views [\#467](https://github.com/bensheldon/good_job/issues/467)
|
|
35
|
+
- Updating good\_job breaks my Rails 7 alpha 2 local development [\#462](https://github.com/bensheldon/good_job/issues/462)
|
|
36
|
+
|
|
37
|
+
**Merged pull requests:**
|
|
38
|
+
|
|
39
|
+
- Update appraisal for Rails 7.0.0.rc1 [\#466](https://github.com/bensheldon/good_job/pull/466) ([bensheldon](https://github.com/bensheldon))
|
|
40
|
+
|
|
41
|
+
## [v2.7.3](https://github.com/bensheldon/good_job/tree/v2.7.3) (2021-11-30)
|
|
42
|
+
|
|
43
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v2.7.2...v2.7.3)
|
|
44
|
+
|
|
45
|
+
**Fixed bugs:**
|
|
46
|
+
|
|
47
|
+
- Logger error on 2.7.2 [\#463](https://github.com/bensheldon/good_job/issues/463)
|
|
48
|
+
- Fix Railtie configuration assignment when Rails configuration is a Hash, not an OrderedOptions [\#464](https://github.com/bensheldon/good_job/pull/464) ([bensheldon](https://github.com/bensheldon))
|
|
49
|
+
|
|
50
|
+
## [v2.7.2](https://github.com/bensheldon/good_job/tree/v2.7.2) (2021-11-29)
|
|
51
|
+
|
|
52
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v2.7.1...v2.7.2)
|
|
53
|
+
|
|
54
|
+
**Implemented enhancements:**
|
|
55
|
+
|
|
56
|
+
- Allow GoodJob global configuration accessors to also be set via Rails config hash [\#460](https://github.com/bensheldon/good_job/pull/460) ([bensheldon](https://github.com/bensheldon))
|
|
57
|
+
|
|
58
|
+
**Merged pull requests:**
|
|
59
|
+
|
|
60
|
+
- Use `ActiveRecord::Relation::QueryAttribute` when setting up bindings for `exec_query` [\#461](https://github.com/bensheldon/good_job/pull/461) ([bensheldon](https://github.com/bensheldon))
|
|
61
|
+
- Configure RSpec `config.example_status_persistence_file_path` [\#459](https://github.com/bensheldon/good_job/pull/459) ([bensheldon](https://github.com/bensheldon))
|
|
62
|
+
- Defer async initialization until Rails fully initialized [\#454](https://github.com/bensheldon/good_job/pull/454) ([bensheldon](https://github.com/bensheldon))
|
|
63
|
+
|
|
3
64
|
## [v2.7.1](https://github.com/bensheldon/good_job/tree/v2.7.1) (2021-11-26)
|
|
4
65
|
|
|
5
66
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v2.7.0...v2.7.1)
|
data/README.md
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://rubygems.org/gems/good_job)
|
|
4
4
|
[](https://github.com/bensheldon/good_job/actions)
|
|
5
|
+
[](https://www.ruby-toolbox.com/projects/good_job)
|
|
5
6
|
|
|
6
7
|
GoodJob is a multithreaded, Postgres-based, ActiveJob backend for Ruby on Rails.
|
|
7
8
|
|
|
@@ -97,7 +98,7 @@ For more of the story of GoodJob, read the [introductory blog post](https://isla
|
|
|
97
98
|
1. Configure the ActiveJob adapter:
|
|
98
99
|
|
|
99
100
|
```ruby
|
|
100
|
-
# config/application.rb
|
|
101
|
+
# config/application.rb or config/environments/{RAILS_ENV}.rb
|
|
101
102
|
config.active_job.queue_adapter = :good_job
|
|
102
103
|
```
|
|
103
104
|
|
|
@@ -212,43 +213,48 @@ to delete old records and preserve space in your database.
|
|
|
212
213
|
|
|
213
214
|
### Configuration options
|
|
214
215
|
|
|
215
|
-
|
|
216
|
+
ActiveJob configuration depends on where the code is placed:
|
|
216
217
|
|
|
217
|
-
|
|
218
|
+
- `config.active_job.queue_adapter = :good_job` within `config/application.rb` or `config/environments/*.rb`.
|
|
219
|
+
- `ActiveJob::Base.queue_adapter = :good_job` within an initializer (e.g. `config/initializers/active_job.rb`).
|
|
218
220
|
|
|
219
|
-
|
|
221
|
+
GoodJob configuration can be placed within Rails `config` directory for all environments (`config/application.rb`), within a particular environment (e.g. `config/environments/development.rb`), or within an initializer (e.g. `config/initializers/good_job.rb`).
|
|
220
222
|
|
|
221
223
|
Configuration examples:
|
|
222
224
|
|
|
223
225
|
```ruby
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
config.
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
config.good_job.execution_mode = :async
|
|
230
|
-
config.good_job.max_threads = 5
|
|
231
|
-
config.good_job.poll_interval = 30 # seconds
|
|
232
|
-
config.good_job.shutdown_timeout = 25 # seconds
|
|
233
|
-
config.good_job.enable_cron = true
|
|
234
|
-
config.good_job.cron = { example: { cron: '0 * * * *', class: 'ExampleJob' } }
|
|
235
|
-
config.good_job.queues = '*'
|
|
236
|
-
|
|
237
|
-
# ...or all at once.
|
|
238
|
-
config.good_job = {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
226
|
+
Rails.application.configure do
|
|
227
|
+
# Configure options individually...
|
|
228
|
+
config.good_job.preserve_job_records = true
|
|
229
|
+
config.good_job.retry_on_unhandled_error = false
|
|
230
|
+
config.good_job.on_thread_error = -> (exception) { Raven.capture_exception(exception) }
|
|
231
|
+
config.good_job.execution_mode = :async
|
|
232
|
+
config.good_job.max_threads = 5
|
|
233
|
+
config.good_job.poll_interval = 30 # seconds
|
|
234
|
+
config.good_job.shutdown_timeout = 25 # seconds
|
|
235
|
+
config.good_job.enable_cron = true
|
|
236
|
+
config.good_job.cron = { example: { cron: '0 * * * *', class: 'ExampleJob' } }
|
|
237
|
+
config.good_job.queues = '*'
|
|
238
|
+
|
|
239
|
+
# ...or all at once.
|
|
240
|
+
config.good_job = {
|
|
241
|
+
preserve_job_records: true,
|
|
242
|
+
retry_on_unhandled_error: false,
|
|
243
|
+
on_thread_error: -> (exception) { Raven.capture_exception(exception) },
|
|
244
|
+
execution_mode: :async,
|
|
245
|
+
max_threads: 5,
|
|
246
|
+
poll_interval: 30,
|
|
247
|
+
shutdown_timeout: 25,
|
|
248
|
+
enable_cron: true,
|
|
249
|
+
cron: {
|
|
250
|
+
example: {
|
|
251
|
+
cron: '0 * * * *',
|
|
252
|
+
class: 'ExampleJob'
|
|
253
|
+
},
|
|
248
254
|
},
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
255
|
+
queues: '*',
|
|
256
|
+
}
|
|
257
|
+
end
|
|
252
258
|
```
|
|
253
259
|
|
|
254
260
|
Available configuration options are:
|
|
@@ -265,6 +271,17 @@ Available configuration options are:
|
|
|
265
271
|
- `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`.
|
|
266
272
|
- `enable_cron` (boolean) whether to run cron process. Defaults to `false`. You can also set this with the environment variable `GOOD_JOB_ENABLE_CRON`.
|
|
267
273
|
- `cron` (hash) cron configuration. Defaults to `{}`. You can also set this as a JSON string with the environment variable `GOOD_JOB_CRON`
|
|
274
|
+
- `cleanup_preserved_jobs_before_seconds_ago` (integer) number of seconds to preserve jobs when using the `$ good_job cleanup_preserved_jobs` CLI command or calling `GoodJob.cleanup_preserved_jobs`. Defaults to `86400` (1 day). Can also be set with the environment variable `GOOD_JOB_CLEANUP_PRESERVED_JOBS_BEFORE_SECONDS_AGO`. _This configuration is only used when {GoodJob.preserve_job_records} is `true`._
|
|
275
|
+
- `cleanup_interval_jobs` (integer) Number of jobs a Scheduler will execute before cleaning up preserved jobs. Defaults to `nil`. Can also be set with the environment variable `GOOD_JOB_CLEANUP_INTERVAL_JOBS`.
|
|
276
|
+
- `cleanup_interval_seconds` (integer) Number of seconds a Scheduler will wait before cleaning up preserved jobs. Defaults to `nil`. Can also be set with the environment variable `GOOD_JOB_CLEANUP_INTERVAL_SECONDS`.
|
|
277
|
+
- `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`).
|
|
278
|
+
- `preserve_job_records` (boolean) keeps job records in your database even after jobs are completed. (Default: `false`)
|
|
279
|
+
- `retry_on_unhandled_error` (boolean) causes jobs to be re-queued and retried if they raise an instance of `StandardError`. Instances of `Exception`, like SIGINT, will *always* be retried, regardless of this attribute’s value. (Default: `true`)
|
|
280
|
+
- `on_thread_error` (proc, lambda, or callable) will be called when an Exception. It can be useful for logging errors to bug tracking services, like Sentry or Airbrake. Example:
|
|
281
|
+
|
|
282
|
+
```ruby
|
|
283
|
+
config.good_job.on_thread_error = -> (exception) { Raven.capture_exception(exception) }
|
|
284
|
+
```
|
|
268
285
|
|
|
269
286
|
By default, GoodJob configures the following execution modes per environment:
|
|
270
287
|
|
|
@@ -285,7 +302,7 @@ config.good_job.execution_mode = :external
|
|
|
285
302
|
|
|
286
303
|
### Global options
|
|
287
304
|
|
|
288
|
-
Good Job’s general behavior can also be configured via
|
|
305
|
+
Good Job’s general behavior can also be configured via attributes directly on the `GoodJob` module:
|
|
289
306
|
|
|
290
307
|
- **`GoodJob.active_record_parent_class`** (string) The ActiveRecord parent class inherited by GoodJob's ActiveRecord model `GoodJob::Job` (defaults to `"ActiveRecord::Base"`). Configure this when using [multiple databases with ActiveRecord](https://guides.rubyonrails.org/active_record_multiple_databases.html) or when other custom configuration is necessary for the ActiveRecord model to connect to the Postgres database. _The value must be a String to avoid premature initialization of ActiveRecord._
|
|
291
308
|
- **`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`.
|
|
@@ -30,7 +30,10 @@ module GoodJob
|
|
|
30
30
|
ORDER BY timestamp ASC
|
|
31
31
|
SQL
|
|
32
32
|
|
|
33
|
-
binds = [
|
|
33
|
+
binds = [
|
|
34
|
+
ActiveRecord::Relation::QueryAttribute.new('start_time', start_time, ActiveRecord::Type::DateTime.new),
|
|
35
|
+
ActiveRecord::Relation::QueryAttribute.new('end_time', end_time, ActiveRecord::Type::DateTime.new),
|
|
36
|
+
]
|
|
34
37
|
executions_data = GoodJob::Execution.connection.exec_query(GoodJob::Execution.pg_or_jdbc_query(count_query), "GoodJob Dashboard Chart", binds)
|
|
35
38
|
|
|
36
39
|
queue_names = executions_data.reject { |d| d['count'].nil? }.map { |d| d['queue_name'] || BaseFilter::EMPTY }.uniq
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<% if !GoodJob::Process.migrated? %>
|
|
2
|
+
<div class="card my-3">
|
|
3
|
+
<div class="card-body">
|
|
4
|
+
<p class="card-text">
|
|
5
|
+
<em>Feature unavailable because of pending database migration.</em>
|
|
6
|
+
</p>
|
|
7
|
+
</div>
|
|
8
|
+
</div>
|
|
9
|
+
<% elsif @processes.present? %>
|
|
10
|
+
<div class="card my-3">
|
|
11
|
+
<div class="table-responsive">
|
|
12
|
+
<table class="table card-table table-bordered table-hover table-sm mb-0">
|
|
13
|
+
<thead>
|
|
14
|
+
<tr>
|
|
15
|
+
<th>Process UUID</th>
|
|
16
|
+
<th>Created At</th></th>
|
|
17
|
+
<th>State</th>
|
|
18
|
+
</tr>
|
|
19
|
+
</thead>
|
|
20
|
+
<tbody>
|
|
21
|
+
<% @processes.each do |process| %>
|
|
22
|
+
<tr class="<%= dom_class(process) %>" id="<%= dom_id(process) %>">
|
|
23
|
+
<td><%= process.id %></td>
|
|
24
|
+
<td><%= relative_time(process.created_at) %></td>
|
|
25
|
+
<td><%= tag.pre JSON.pretty_generate(process.state) %></td>
|
|
26
|
+
</tr>
|
|
27
|
+
<% end %>
|
|
28
|
+
</tbody>
|
|
29
|
+
</table>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
<% else %>
|
|
33
|
+
<div class="card my-3">
|
|
34
|
+
<div class="card-body">
|
|
35
|
+
<p class="card-text">
|
|
36
|
+
<em>No GoodJob processes found.</em>
|
|
37
|
+
</p>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
<% end %>
|
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
<%= stylesheet_link_tag bootstrap_path(format: :css, v: GoodJob::VERSION) %>
|
|
9
9
|
<%= stylesheet_link_tag style_path(format: :css, v: GoodJob::VERSION) %>
|
|
10
10
|
|
|
11
|
-
<%= javascript_include_tag bootstrap_path(format: :js, v: GoodJob::VERSION) %>
|
|
12
|
-
<%= javascript_include_tag chartjs_path(format: :js, v: GoodJob::VERSION) %>
|
|
13
|
-
<%= javascript_include_tag scripts_path(format: :js, v: GoodJob::VERSION) %>
|
|
11
|
+
<%= javascript_include_tag bootstrap_path(format: :js, v: GoodJob::VERSION), nonce: true %>
|
|
12
|
+
<%= javascript_include_tag chartjs_path(format: :js, v: GoodJob::VERSION), nonce: true %>
|
|
13
|
+
<%= javascript_include_tag scripts_path(format: :js, v: GoodJob::VERSION), nonce: true %>
|
|
14
14
|
|
|
15
|
-
<%= javascript_include_tag rails_ujs_path(format: :js, v: GoodJob::VERSION) %>
|
|
15
|
+
<%= javascript_include_tag rails_ujs_path(format: :js, v: GoodJob::VERSION), nonce: true %>
|
|
16
16
|
</head>
|
|
17
17
|
<body>
|
|
18
18
|
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
|
@@ -33,23 +33,14 @@
|
|
|
33
33
|
<li class="nav-item">
|
|
34
34
|
<%= link_to "Cron Schedules", cron_entries_path, class: ["nav-link", ("active" if current_page?(cron_entries_path))] %>
|
|
35
35
|
</li>
|
|
36
|
+
<li class="nav-item">
|
|
37
|
+
<%= link_to "Processes", processes_path, class: ["nav-link", ("active" if current_page?(processes_path))] %>
|
|
38
|
+
</li>
|
|
36
39
|
<li class="nav-item">
|
|
37
40
|
<div class="nav-link">
|
|
38
41
|
<span class="badge bg-secondary">More views coming soon</span>
|
|
39
42
|
</div>
|
|
40
43
|
</li>
|
|
41
|
-
|
|
42
|
-
<!-- Coming Soon
|
|
43
|
-
<li class="nav-item">
|
|
44
|
-
<%= link_to "Upcoming Jobs", 'todo', class: ["nav-link", ("active" if current_page?('todo'))] %>
|
|
45
|
-
</li>
|
|
46
|
-
<li class="nav-item">
|
|
47
|
-
<%= link_to "Finished Jobs", 'todo', class: ["nav-link", ("active" if current_page?('todo'))] %>
|
|
48
|
-
</li>
|
|
49
|
-
<li class="nav-item">
|
|
50
|
-
<%= link_to "Errored Jobs", 'todo', class: ["nav-link", ("active" if current_page?('todo'))] %>
|
|
51
|
-
</li>
|
|
52
|
-
-->
|
|
53
44
|
</ul>
|
|
54
45
|
<div class="text-muted" title="Now is <%= Time.current %>">Times are displayed in <%= Time.current.zone %> timezone</div>
|
|
55
46
|
</div>
|
data/engine/config/routes.rb
CHANGED
|
@@ -2,11 +2,7 @@
|
|
|
2
2
|
GoodJob::Engine.routes.draw do
|
|
3
3
|
root to: 'executions#index'
|
|
4
4
|
|
|
5
|
-
resources :
|
|
6
|
-
member do
|
|
7
|
-
post :enqueue
|
|
8
|
-
end
|
|
9
|
-
end
|
|
5
|
+
resources :executions, only: %i[destroy]
|
|
10
6
|
|
|
11
7
|
resources :jobs, only: %i[index show] do
|
|
12
8
|
member do
|
|
@@ -15,7 +11,14 @@ GoodJob::Engine.routes.draw do
|
|
|
15
11
|
put :retry
|
|
16
12
|
end
|
|
17
13
|
end
|
|
18
|
-
|
|
14
|
+
|
|
15
|
+
resources :cron_entries, only: %i[index show] do
|
|
16
|
+
member do
|
|
17
|
+
post :enqueue
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
resources :processes, only: %i[index]
|
|
19
22
|
|
|
20
23
|
scope controller: :assets do
|
|
21
24
|
constraints(format: :css) do
|
|
@@ -21,6 +21,11 @@ class CreateGoodJobs < ActiveRecord::Migration<%= migration_version %>
|
|
|
21
21
|
t.timestamp :cron_at
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
+
create_table :good_job_processes, id: :uuid do |t|
|
|
25
|
+
t.timestamps
|
|
26
|
+
t.jsonb :state
|
|
27
|
+
end
|
|
28
|
+
|
|
24
29
|
add_index :good_jobs, :scheduled_at, where: "(finished_at IS NULL)", name: "index_good_jobs_on_scheduled_at"
|
|
25
30
|
add_index :good_jobs, [:queue_name, :scheduled_at], where: "(finished_at IS NULL)", name: :index_good_jobs_on_queue_name_and_scheduled_at
|
|
26
31
|
add_index :good_jobs, [:active_job_id, :created_at], name: :index_good_jobs_on_active_job_id_and_created_at
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
class CreateGoodJobProcesses < ActiveRecord::Migration<%= migration_version %>
|
|
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.table_exists?(:good_job_processes)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
create_table :good_job_processes, id: :uuid do |t|
|
|
13
|
+
t.timestamps
|
|
14
|
+
t.jsonb :state
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -4,10 +4,7 @@ module GoodJob
|
|
|
4
4
|
# There is not a table in the database whose discrete rows represents "Jobs".
|
|
5
5
|
# The +good_jobs+ table is a table of individual {GoodJob::Execution}s that share the same +active_job_id+.
|
|
6
6
|
# A single row from the +good_jobs+ table of executions is fetched to represent an ActiveJobJob
|
|
7
|
-
|
|
8
|
-
# @!parse
|
|
9
|
-
# class ActiveJob < ActiveRecord::Base; end
|
|
10
|
-
class ActiveJobJob < Object.const_get(GoodJob.active_record_parent_class)
|
|
7
|
+
class ActiveJobJob < BaseRecord
|
|
11
8
|
include Filterable
|
|
12
9
|
include Lockable
|
|
13
10
|
|
data/lib/good_job/adapter.rb
CHANGED
|
@@ -4,6 +4,12 @@ module GoodJob
|
|
|
4
4
|
# ActiveJob Adapter.
|
|
5
5
|
#
|
|
6
6
|
class Adapter
|
|
7
|
+
# @!attribute [r] instances
|
|
8
|
+
# @!scope class
|
|
9
|
+
# List of all instantiated Adapters in the current process.
|
|
10
|
+
# @return [Array<GoodJob::Adapter>, nil]
|
|
11
|
+
cattr_reader :instances, default: [], instance_reader: false
|
|
12
|
+
|
|
7
13
|
# @param execution_mode [Symbol, nil] specifies how and where jobs should be executed. You can also set this with the environment variable +GOOD_JOB_EXECUTION_MODE+.
|
|
8
14
|
#
|
|
9
15
|
# - +:inline+ executes jobs immediately in whatever process queued them (usually the web server process). This should only be used in test and development environments.
|
|
@@ -20,7 +26,8 @@ module GoodJob
|
|
|
20
26
|
# @param max_threads [Integer, nil] sets the number of threads per scheduler to use when +execution_mode+ is set to +:async+. The +queues+ parameter can specify a number of threads for each group of queues which will override this value. You can also set this with the environment variable +GOOD_JOB_MAX_THREADS+. Defaults to +5+.
|
|
21
27
|
# @param queues [String, nil] determines which queues to execute jobs from when +execution_mode+ is set to +:async+. See {file:README.md#optimize-queues-threads-and-processes} for more details on the format of this string. You can also set this with the environment variable +GOOD_JOB_QUEUES+. Defaults to +"*"+.
|
|
22
28
|
# @param poll_interval [Integer, nil] sets the number of seconds between polls for jobs when +execution_mode+ is set to +:async+. You can also set this with the environment variable +GOOD_JOB_POLL_INTERVAL+. Defaults to +1+.
|
|
23
|
-
|
|
29
|
+
# @param start_async_on_initialize [Boolean] whether to start the async scheduler when the adapter is initialized.
|
|
30
|
+
def initialize(execution_mode: nil, queues: nil, max_threads: nil, poll_interval: nil, start_async_on_initialize: Rails.application.initialized?)
|
|
24
31
|
@configuration = GoodJob::Configuration.new(
|
|
25
32
|
{
|
|
26
33
|
execution_mode: execution_mode,
|
|
@@ -30,16 +37,9 @@ module GoodJob
|
|
|
30
37
|
}
|
|
31
38
|
)
|
|
32
39
|
@configuration.validate!
|
|
40
|
+
self.class.instances << self
|
|
33
41
|
|
|
34
|
-
if
|
|
35
|
-
@notifier = GoodJob::Notifier.new
|
|
36
|
-
@poller = GoodJob::Poller.new(poll_interval: @configuration.poll_interval)
|
|
37
|
-
@scheduler = GoodJob::Scheduler.from_configuration(@configuration, warm_cache_on_initialize: Rails.application.initialized?)
|
|
38
|
-
@notifier.recipients << [@scheduler, :create_thread]
|
|
39
|
-
@poller.recipients << [@scheduler, :create_thread]
|
|
40
|
-
|
|
41
|
-
@cron_manager = GoodJob::CronManager.new(@configuration.cron_entries, start_on_initialize: Rails.application.initialized?) if @configuration.enable_cron?
|
|
42
|
-
end
|
|
42
|
+
start_async if start_async_on_initialize
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
# Enqueues the ActiveJob job to be performed.
|
|
@@ -74,7 +74,7 @@ module GoodJob
|
|
|
74
74
|
job_state = { queue_name: execution.queue_name }
|
|
75
75
|
job_state[:scheduled_at] = execution.scheduled_at if execution.scheduled_at
|
|
76
76
|
|
|
77
|
-
executed_locally = execute_async? && @scheduler
|
|
77
|
+
executed_locally = execute_async? && @scheduler&.create_thread(job_state)
|
|
78
78
|
Notifier.notify(job_state) unless executed_locally
|
|
79
79
|
end
|
|
80
80
|
|
|
@@ -97,6 +97,7 @@ module GoodJob
|
|
|
97
97
|
|
|
98
98
|
executables = [@notifier, @poller, @scheduler].compact
|
|
99
99
|
GoodJob._shutdown_all(executables, timeout: timeout)
|
|
100
|
+
@_async_started = false
|
|
100
101
|
end
|
|
101
102
|
|
|
102
103
|
# Whether in +:async+ execution mode.
|
|
@@ -119,6 +120,28 @@ module GoodJob
|
|
|
119
120
|
@configuration.execution_mode == :inline
|
|
120
121
|
end
|
|
121
122
|
|
|
123
|
+
# Start async executors
|
|
124
|
+
# @return [void]
|
|
125
|
+
def start_async
|
|
126
|
+
return unless execute_async?
|
|
127
|
+
|
|
128
|
+
@notifier = GoodJob::Notifier.new
|
|
129
|
+
@poller = GoodJob::Poller.new(poll_interval: @configuration.poll_interval)
|
|
130
|
+
@scheduler = GoodJob::Scheduler.from_configuration(@configuration, warm_cache_on_initialize: true)
|
|
131
|
+
@notifier.recipients << [@scheduler, :create_thread]
|
|
132
|
+
@poller.recipients << [@scheduler, :create_thread]
|
|
133
|
+
|
|
134
|
+
@cron_manager = GoodJob::CronManager.new(@configuration.cron_entries, start_on_initialize: true) if @configuration.enable_cron?
|
|
135
|
+
|
|
136
|
+
@_async_started = true
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Whether the async executors are running
|
|
140
|
+
# @return [Boolean]
|
|
141
|
+
def async_started?
|
|
142
|
+
@_async_started
|
|
143
|
+
end
|
|
144
|
+
|
|
122
145
|
private
|
|
123
146
|
|
|
124
147
|
# Whether running in a web server process.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module GoodJob # :nodoc:
|
|
3
|
+
# Extends an ActiveRecord odel to override the connection and use
|
|
4
|
+
# an explicit connection that has been removed from the pool.
|
|
5
|
+
module AssignableConnection
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
included do
|
|
9
|
+
thread_cattr_accessor :_connection
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class_methods do
|
|
13
|
+
# Assigns a connection to the model.
|
|
14
|
+
# @param conn [ActiveRecord::ConnectionAdapters::AbstractAdapter]
|
|
15
|
+
# @return [void]
|
|
16
|
+
def connection=(conn)
|
|
17
|
+
self._connection = conn
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Overrides the existing connection method to use the assigned connection
|
|
21
|
+
# @return [ActiveRecord::ConnectionAdapters::AbstractAdapter]
|
|
22
|
+
def connection
|
|
23
|
+
_connection || super
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Block interface to assign the connection, yield, then unassign the connection.
|
|
27
|
+
# @param conn [ActiveRecord::ConnectionAdapters::AbstractAdapter]
|
|
28
|
+
# @return [void]
|
|
29
|
+
def with_connection(conn)
|
|
30
|
+
original_conn = _connection
|
|
31
|
+
self.connection = conn
|
|
32
|
+
yield
|
|
33
|
+
ensure
|
|
34
|
+
self._connection = original_conn
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module GoodJob
|
|
3
|
+
# Base ActiveRecord class that all GoodJob models inherit from.
|
|
4
|
+
# Parent class can be configured with +GoodJob.active_record_parent_class+.
|
|
5
|
+
# @!parse
|
|
6
|
+
# class BaseRecord < ActiveRecord::Base; end
|
|
7
|
+
class BaseRecord < Object.const_get(GoodJob.active_record_parent_class)
|
|
8
|
+
self.abstract_class = true
|
|
9
|
+
|
|
10
|
+
def self.migration_pending_warning!
|
|
11
|
+
ActiveSupport::Deprecation.warn(<<~DEPRECATION)
|
|
12
|
+
GoodJob has pending database migrations. To create the migration files, run:
|
|
13
|
+
rails generate good_job:update
|
|
14
|
+
To apply the migration files, run:
|
|
15
|
+
rails db:migrate
|
|
16
|
+
DEPRECATION
|
|
17
|
+
nil
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module GoodJob # :nodoc:
|
|
3
|
+
# Tracks thresholds for cleaning up old jobs.
|
|
4
|
+
class CleanupTracker
|
|
5
|
+
attr_accessor :cleanup_interval_seconds,
|
|
6
|
+
:cleanup_interval_jobs,
|
|
7
|
+
:job_count,
|
|
8
|
+
:last_at
|
|
9
|
+
|
|
10
|
+
def initialize(cleanup_interval_seconds: nil, cleanup_interval_jobs: nil)
|
|
11
|
+
self.cleanup_interval_seconds = cleanup_interval_seconds
|
|
12
|
+
self.cleanup_interval_jobs = cleanup_interval_jobs
|
|
13
|
+
|
|
14
|
+
reset
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Increments job count.
|
|
18
|
+
# @return [void]
|
|
19
|
+
def increment
|
|
20
|
+
self.job_count += 1
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Whether a cleanup should be run.
|
|
24
|
+
# @return [Boolean]
|
|
25
|
+
def cleanup?
|
|
26
|
+
(cleanup_interval_jobs && job_count > cleanup_interval_jobs) ||
|
|
27
|
+
(cleanup_interval_seconds && last_at < Time.current - cleanup_interval_seconds) ||
|
|
28
|
+
false
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Resets the counters.
|
|
32
|
+
# @return [void]
|
|
33
|
+
def reset
|
|
34
|
+
self.job_count = 0
|
|
35
|
+
self.last_at = Time.current
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
data/lib/good_job/cli.rb
CHANGED
|
@@ -96,6 +96,7 @@ module GoodJob
|
|
|
96
96
|
poller.recipients << [scheduler, :create_thread]
|
|
97
97
|
|
|
98
98
|
cron_manager = GoodJob::CronManager.new(configuration.cron_entries, start_on_initialize: true) if configuration.enable_cron?
|
|
99
|
+
|
|
99
100
|
if configuration.probe_port
|
|
100
101
|
probe_server = GoodJob::ProbeServer.new(port: configuration.probe_port)
|
|
101
102
|
probe_server.start
|