good_job 2.7.1 → 2.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Gem Version](https://badge.fury.io/rb/good_job.svg)](https://rubygems.org/gems/good_job)
|
4
4
|
[![Test Status](https://github.com/bensheldon/good_job/workflows/Test/badge.svg)](https://github.com/bensheldon/good_job/actions)
|
5
|
+
[![Ruby Toolbox](https://img.shields.io/badge/dynamic/json?color=blue&label=Ruby%20Toolbox&query=%24.projects%5B0%5D.score&url=https%3A%2F%2Fwww.ruby-toolbox.com%2Fapi%2Fprojects%2Fcompare%2Fgood_job&logo=data:image/svg+xml;base64,PHN2ZyBhcmlhLWhpZGRlbj0idHJ1ZSIgZm9jdXNhYmxlPSJmYWxzZSIgZGF0YS1wcmVmaXg9ImZhcyIgZGF0YS1pY29uPSJmbGFzayIgY2xhc3M9InN2Zy1pbmxpbmUtLWZhIGZhLWZsYXNrIGZhLXctMTQiIHJvbGU9ImltZyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgNDQ4IDUxMiI+PHBhdGggZmlsbD0id2hpdGUiIGQ9Ik00MzcuMiA0MDMuNUwzMjAgMjE1VjY0aDhjMTMuMyAwIDI0LTEwLjcgMjQtMjRWMjRjMC0xMy4zLTEwLjctMjQtMjQtMjRIMTIwYy0xMy4zIDAtMjQgMTAuNy0yNCAyNHYxNmMwIDEzLjMgMTAuNyAyNCAyNCAyNGg4djE1MUwxMC44IDQwMy41Qy0xOC41IDQ1MC42IDE1LjMgNTEyIDcwLjkgNTEyaDMwNi4yYzU1LjcgMCA4OS40LTYxLjUgNjAuMS0xMDguNXpNMTM3LjkgMzIwbDQ4LjItNzcuNmMzLjctNS4yIDUuOC0xMS42IDUuOC0xOC40VjY0aDY0djE2MGMwIDYuOSAyLjIgMTMuMiA1LjggMTguNGw0OC4yIDc3LjZoLTE3MnoiPjwvcGF0aD48L3N2Zz4=)](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
|