good_job 3.15.14 → 3.16.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 +34 -2
- data/README.md +20 -3
- data/app/controllers/good_job/application_controller.rb +16 -9
- data/app/controllers/good_job/batches_controller.rb +1 -0
- data/app/controllers/good_job/cron_entries_controller.rb +2 -1
- data/app/controllers/good_job/frontends_controller.rb +1 -0
- data/app/controllers/good_job/jobs_controller.rb +1 -0
- data/app/controllers/good_job/processes_controller.rb +1 -0
- data/app/filters/good_job/base_filter.rb +1 -0
- data/app/filters/good_job/batches_filter.rb +1 -0
- data/app/filters/good_job/jobs_filter.rb +1 -0
- data/app/helpers/good_job/application_helper.rb +1 -1
- data/app/models/concerns/good_job/error_events.rb +28 -0
- data/app/models/concerns/good_job/filterable.rb +1 -0
- data/app/models/{good_job → concerns/good_job}/lockable.rb +1 -0
- data/app/models/concerns/good_job/reportable.rb +1 -0
- data/app/models/good_job/active_record_parent_class.rb +9 -0
- data/app/models/good_job/base_execution.rb +9 -0
- data/app/models/good_job/base_record.rb +4 -1
- data/app/models/good_job/cron_entry.rb +1 -0
- data/app/models/good_job/discrete_execution.rb +9 -0
- data/app/models/good_job/execution.rb +41 -8
- data/app/models/good_job/execution_result.rb +17 -2
- data/app/models/good_job/i18n_config.rb +25 -0
- data/app/models/good_job/job.rb +4 -1
- data/app/models/good_job/process.rb +13 -2
- data/app/views/good_job/jobs/_executions.erb +1 -1
- data/app/views/good_job/jobs/_table.erb +17 -4
- data/app/views/good_job/processes/index.html.erb +2 -2
- data/app/views/good_job/shared/_navbar.erb +1 -1
- data/config/locales/de.yml +8 -1
- data/config/locales/en.yml +8 -1
- data/config/locales/es.yml +8 -1
- data/config/locales/fr.yml +8 -1
- data/config/locales/ja.yml +8 -1
- data/config/locales/nl.yml +8 -1
- data/config/locales/ru.yml +8 -1
- data/config/locales/tr.yml +8 -1
- data/config/locales/{ua.yml → uk.yml} +33 -2
- data/config/routes.rb +1 -0
- data/exe/good_job +1 -0
- data/lib/active_job/queue_adapters/good_job_adapter.rb +1 -0
- data/lib/generators/good_job/install_generator.rb +1 -0
- data/lib/generators/good_job/templates/install/migrations/create_good_jobs.rb.erb +5 -1
- data/lib/generators/good_job/templates/update/migrations/01_create_good_jobs.rb.erb +3 -1
- data/lib/generators/good_job/templates/update/migrations/02_create_good_job_settings.rb.erb +1 -0
- data/lib/generators/good_job/templates/update/migrations/03_create_index_good_jobs_jobs_on_priority_created_at_when_unfinished.rb.erb +1 -0
- data/lib/generators/good_job/templates/update/migrations/04_create_good_job_batches.rb.erb +1 -0
- data/lib/generators/good_job/templates/update/migrations/05_create_good_job_executions.rb.erb +1 -0
- data/lib/generators/good_job/templates/update/migrations/06_create_good_jobs_error_event.rb.erb +16 -0
- data/lib/generators/good_job/update_generator.rb +1 -0
- data/lib/good_job/active_job_extensions/batches.rb +1 -0
- data/lib/good_job/active_job_extensions/concurrency.rb +1 -0
- data/lib/good_job/active_job_extensions/interrupt_errors.rb +1 -0
- data/lib/good_job/active_job_extensions/notify_options.rb +1 -0
- data/lib/good_job/adapter.rb +3 -2
- data/lib/good_job/assignable_connection.rb +1 -0
- data/lib/good_job/bulk.rb +1 -0
- data/lib/good_job/capsule.rb +4 -3
- data/lib/good_job/cleanup_tracker.rb +2 -1
- data/lib/good_job/cli.rb +1 -0
- data/lib/good_job/configuration.rb +7 -0
- data/lib/good_job/cron_manager.rb +3 -3
- data/lib/good_job/current_thread.rb +8 -0
- data/lib/good_job/daemon.rb +1 -0
- data/lib/good_job/engine.rb +13 -0
- data/lib/good_job/interrupt_error.rb +1 -0
- data/lib/good_job/job_performer.rb +1 -0
- data/lib/good_job/log_subscriber.rb +1 -0
- data/lib/good_job/metrics.rb +57 -0
- data/lib/good_job/multi_scheduler.rb +1 -0
- data/lib/good_job/notifier.rb +3 -3
- data/lib/good_job/poller.rb +3 -3
- data/lib/good_job/scheduler.rb +30 -11
- data/lib/good_job/version.rb +2 -1
- data/lib/good_job.rb +15 -1
- metadata +9 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 20bb64485159969378a5abf5d960de13a3f27df63846f85cda6e4ef410c9af23
|
4
|
+
data.tar.gz: de0d39b68577c973ca4a4c9d9bccd8bda29bad7e6ca5a936112db7c8df6b038b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9fa9474d0ee2ff9e8e47aa5e3cc31d6cd1924c7d00276a9da2bc4611b970ea80d68a0bc79c9da7f4266c85e0bedfc491860dabff4d4e1088d485a11a21a895f5
|
7
|
+
data.tar.gz: eb6a2ea5cdacd25fd930aab7eba6b498ffb5998fa82c78e552f7acaebf59f6f6cd950fb6488e4c317b461ff8931df5250820cdeb46d40a4047a1e8c24495756b
|
data/CHANGELOG.md
CHANGED
@@ -1,12 +1,46 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [v3.16.0](https://github.com/bensheldon/good_job/tree/v3.16.0) (2023-07-10)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v3.15.14...v3.16.0)
|
6
|
+
|
7
|
+
**Implemented enhancements:**
|
8
|
+
|
9
|
+
- Add `GoodJob.configure_active_record` as alternative to `GoodJob.active_record_parent_class` [\#1004](https://github.com/bensheldon/good_job/pull/1004) ([bensheldon](https://github.com/bensheldon))
|
10
|
+
- Configure `dashboard_default_locale` using custom subclass of `I18n::Config` to isolate I18n configuration from parent application [\#1001](https://github.com/bensheldon/good_job/pull/1001) ([bensheldon](https://github.com/bensheldon))
|
11
|
+
- Create `error_event` column to track the context of an error \(discarded, retried, retry\_stopped, etc\) [\#995](https://github.com/bensheldon/good_job/pull/995) ([bensheldon](https://github.com/bensheldon))
|
12
|
+
- Added metrics to Scheduler and track in Process state [\#984](https://github.com/bensheldon/good_job/pull/984) ([AndersGM](https://github.com/AndersGM))
|
13
|
+
|
14
|
+
**Fixed bugs:**
|
15
|
+
|
16
|
+
- Use Concurrent::Array for class `instances` to avoid JRuby synchronization errors [\#1002](https://github.com/bensheldon/good_job/pull/1002) ([bensheldon](https://github.com/bensheldon))
|
17
|
+
- Add test to assert enqueuing behavior within transactions [\#998](https://github.com/bensheldon/good_job/pull/998) ([bensheldon](https://github.com/bensheldon))
|
18
|
+
- Fix Ukrainian language code [\#996](https://github.com/bensheldon/good_job/pull/996) ([bensheldon](https://github.com/bensheldon))
|
19
|
+
|
20
|
+
**Closed issues:**
|
21
|
+
|
22
|
+
- Is `pgcrypto` necessary? [\#805](https://github.com/bensheldon/good_job/issues/805)
|
23
|
+
- Integrate Sorbet type checking [\#404](https://github.com/bensheldon/good_job/issues/404)
|
24
|
+
|
25
|
+
**Merged pull requests:**
|
26
|
+
|
27
|
+
- Unify `frozen_string_literal` comment style [\#1003](https://github.com/bensheldon/good_job/pull/1003) ([dixpac](https://github.com/dixpac))
|
28
|
+
- Add more execution mode details and caveats to Readme Set Up section [\#997](https://github.com/bensheldon/good_job/pull/997) ([bensheldon](https://github.com/bensheldon))
|
29
|
+
- Add note in migrations that `pgcrypto` extension isn't necessary in PG 13+ [\#837](https://github.com/bensheldon/good_job/pull/837) ([bensheldon](https://github.com/bensheldon))
|
30
|
+
- Add Sorbet to linter [\#760](https://github.com/bensheldon/good_job/pull/760) ([sam1el](https://github.com/sam1el))
|
31
|
+
|
3
32
|
## [v3.15.14](https://github.com/bensheldon/good_job/tree/v3.15.14) (2023-07-03)
|
4
33
|
|
5
34
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v3.15.13...v3.15.14)
|
6
35
|
|
36
|
+
**Implemented enhancements:**
|
37
|
+
|
38
|
+
- Add Process heartbeat that is updated inside of Notifier [\#977](https://github.com/bensheldon/good_job/pull/977) ([bensheldon](https://github.com/bensheldon))
|
39
|
+
|
7
40
|
**Fixed bugs:**
|
8
41
|
|
9
42
|
- Dashboard error when trying to display 0 running/queued/retried jobs due to missing 'zero' translation [\#990](https://github.com/bensheldon/good_job/issues/990)
|
43
|
+
- Add explicit namespace back to `GoodJob::DiscreteExecution` [\#983](https://github.com/bensheldon/good_job/pull/983) ([bensheldon](https://github.com/bensheldon))
|
10
44
|
|
11
45
|
**Closed issues:**
|
12
46
|
|
@@ -20,8 +54,6 @@
|
|
20
54
|
- Bump rails from 7.0.5 to 7.0.6 [\#993](https://github.com/bensheldon/good_job/pull/993) ([dependabot[bot]](https://github.com/apps/dependabot))
|
21
55
|
- Fix CI: Lock traces version for Ruby 2.6 compatible version [\#987](https://github.com/bensheldon/good_job/pull/987) ([bensheldon](https://github.com/bensheldon))
|
22
56
|
- Turkish Language support [\#986](https://github.com/bensheldon/good_job/pull/986) ([SemihCag](https://github.com/SemihCag))
|
23
|
-
- Add explicit namespace back to `GoodJob::DiscreteExecution` [\#983](https://github.com/bensheldon/good_job/pull/983) ([bensheldon](https://github.com/bensheldon))
|
24
|
-
- Add Process heartbeat that is updated inside of Notifier [\#977](https://github.com/bensheldon/good_job/pull/977) ([bensheldon](https://github.com/bensheldon))
|
25
57
|
- Use generic error reporter in Readme examples [\#964](https://github.com/bensheldon/good_job/pull/964) ([shouichi](https://github.com/shouichi))
|
26
58
|
|
27
59
|
## [v3.15.13](https://github.com/bensheldon/good_job/tree/v3.15.13) (2023-06-14)
|
data/README.md
CHANGED
@@ -121,8 +121,13 @@ For more of the story of GoodJob, read the [introductory blog post](https://isla
|
|
121
121
|
YourJob.set(queue: :some_queue, wait: 5.minutes, priority: 10).perform_later
|
122
122
|
```
|
123
123
|
|
124
|
-
1. In development, GoodJob executes jobs
|
125
|
-
|
124
|
+
1. **In Rails' development environment**, by default, GoodJob's Adapter executes jobs `async` in a background thread pool in `rails server`.
|
125
|
+
- Because of Rails deferred autoloading, jobs enqueued via the `rails console` may not begin executing on a separate server process until the Rails application is fully initialized by loading a web page once.
|
126
|
+
- Remember, only Active Job's `perform_later` sends jobs to the queue adapter; Active Job's `perform_now` executes the job immediately and does not invoke the queue adapter. GoodJob is not involved in `perform_now` jobs.
|
127
|
+
1. **In Rails' test environment**, by default, GoodJob's Adapter executes jobs `inline` immediately in the current thread.
|
128
|
+
- Future-scheduled jobs can be executed with `GoodJob.perform_inline` using using a tool like Timecop or `ActiveSupport::Testing::TimeHelpers`.
|
129
|
+
- Note that Active Job's TestAdapter, which powers test helpers (e.g. `assert_enqueued_with`), may override GoodJob's Adapter in [some configurations](https://github.com/rails/rails/issues/37270).
|
130
|
+
1. **In Rails' production environment**, by default, GoodJob's Adapter enqueues jobs in `external` mode to be executed by a separate execution process:
|
126
131
|
- By default, GoodJob separates job enqueuing from job execution so that jobs can be scaled independently of the web server. Use the GoodJob command-line tool to execute jobs:
|
127
132
|
|
128
133
|
```bash
|
@@ -239,6 +244,7 @@ Rails.application.configure do
|
|
239
244
|
config.good_job.shutdown_timeout = 25 # seconds
|
240
245
|
config.good_job.enable_cron = true
|
241
246
|
config.good_job.cron = { example: { cron: '0 * * * *', class: 'ExampleJob' } }
|
247
|
+
config.good_job.dashboard_default_locale = :en
|
242
248
|
|
243
249
|
# ...or all at once.
|
244
250
|
config.good_job = {
|
@@ -257,6 +263,7 @@ Rails.application.configure do
|
|
257
263
|
class: 'ExampleJob'
|
258
264
|
},
|
259
265
|
},
|
266
|
+
dashboard_default_locale: :en,
|
260
267
|
}
|
261
268
|
end
|
262
269
|
```
|
@@ -311,7 +318,17 @@ config.good_job.execution_mode = :external
|
|
311
318
|
|
312
319
|
Good Job’s general behavior can also be configured via attributes directly on the `GoodJob` module:
|
313
320
|
|
314
|
-
- **`GoodJob.
|
321
|
+
- **`GoodJob.configure_active_record { ... }`** Inject Active Record configuration into GoodJob's base model, for example, 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. Example:
|
322
|
+
|
323
|
+
```ruby
|
324
|
+
# config/initializers/good_job.rb
|
325
|
+
GoodJob.configure_active_record do
|
326
|
+
connects_to database: :special_database
|
327
|
+
self.table_name_prefix = "special_application_"
|
328
|
+
end
|
329
|
+
```
|
330
|
+
|
331
|
+
- **`GoodJob.active_record_parent_class`** (string) Alternatively, modify the ActiveRecord parent class inherited by GoodJob's Active Record model `GoodJob::Job` (defaults to `"ActiveRecord::Base"`). Configure this _The value must be a String to avoid premature initialization of ActiveRecord._
|
315
332
|
|
316
333
|
You’ll generally want to configure these in `config/initializers/good_job.rb`, like so:
|
317
334
|
|
@@ -1,9 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module GoodJob
|
3
4
|
class ApplicationController < ActionController::Base
|
4
5
|
protect_from_forgery with: :exception
|
5
6
|
|
6
|
-
around_action :
|
7
|
+
around_action :use_good_job_locale
|
7
8
|
|
8
9
|
content_security_policy do |policy|
|
9
10
|
policy.default_src(:none) if policy.default_src(*policy.default_src).blank?
|
@@ -30,8 +31,21 @@ module GoodJob
|
|
30
31
|
{ locale: I18n.locale }.merge(options)
|
31
32
|
end
|
32
33
|
|
33
|
-
def
|
34
|
+
def use_good_job_locale(&action)
|
35
|
+
@original_i18n_config = I18n.config
|
36
|
+
I18n.config = ::GoodJob::I18nConfig.new
|
34
37
|
I18n.with_locale(current_locale, &action)
|
38
|
+
ensure
|
39
|
+
I18n.config = @original_i18n_config
|
40
|
+
@original_i18n_config = nil
|
41
|
+
end
|
42
|
+
|
43
|
+
def use_original_locale
|
44
|
+
prev_config = I18n.config
|
45
|
+
I18n.config = @original_i18n_config if @original_i18n_config
|
46
|
+
yield
|
47
|
+
ensure
|
48
|
+
I18n.config = prev_config
|
35
49
|
end
|
36
50
|
|
37
51
|
def current_locale
|
@@ -39,16 +53,9 @@ module GoodJob
|
|
39
53
|
request.GET['locale']
|
40
54
|
elsif params[:locale]
|
41
55
|
params[:locale]
|
42
|
-
elsif good_job_available_locales.exclude?(I18n.default_locale) && I18n.available_locales.include?(:en)
|
43
|
-
:en
|
44
56
|
else
|
45
57
|
I18n.default_locale
|
46
58
|
end
|
47
59
|
end
|
48
|
-
|
49
|
-
def good_job_available_locales
|
50
|
-
@_good_job_available_locales ||= GoodJob::Engine.root.join("config/locales").glob("*.yml").map { |path| File.basename(path, ".yml").to_sym }.uniq
|
51
|
-
end
|
52
|
-
helper_method :good_job_available_locales
|
53
60
|
end
|
54
61
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module GoodJob
|
3
4
|
class CronEntriesController < GoodJob::ApplicationController
|
4
5
|
before_action :check_settings_migration!, only: [:enable, :disable]
|
@@ -14,7 +15,7 @@ module GoodJob
|
|
14
15
|
|
15
16
|
def enqueue
|
16
17
|
@cron_entry = CronEntry.find(params[:cron_key])
|
17
|
-
@cron_entry.enqueue(Time.current)
|
18
|
+
use_original_locale { @cron_entry.enqueue(Time.current) }
|
18
19
|
redirect_back(fallback_location: cron_entries_path, notice: t(".notice"))
|
19
20
|
end
|
20
21
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module GoodJob
|
3
4
|
module ApplicationHelper
|
4
5
|
def format_duration(sec)
|
@@ -63,7 +64,6 @@ module GoodJob
|
|
63
64
|
end
|
64
65
|
|
65
66
|
def translation_exists?(key, **options)
|
66
|
-
true if good_job_available_locales.include?(I18n.locale)
|
67
67
|
I18n.exists?(scope_key_by_partial(key), **options)
|
68
68
|
end
|
69
69
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GoodJob
|
4
|
+
# Shared methods for filtering Execution/Job records from the +good_jobs+ table.
|
5
|
+
module ErrorEvents
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
ERROR_EVENTS = [
|
9
|
+
ERROR_EVENT_INTERRUPTED = 'interrupted',
|
10
|
+
ERROR_EVENT_UNHANDLED = 'unhandled',
|
11
|
+
ERROR_EVENT_HANDLED = 'handled',
|
12
|
+
ERROR_EVENT_RETRIED = 'retried',
|
13
|
+
ERROR_EVENT_RETRY_STOPPED = 'retry_stopped',
|
14
|
+
ERROR_EVENT_DISCARDED = 'discarded',
|
15
|
+
].freeze
|
16
|
+
|
17
|
+
included do
|
18
|
+
enum error_event: {
|
19
|
+
ERROR_EVENT_INTERRUPTED => 0,
|
20
|
+
ERROR_EVENT_UNHANDLED => 1,
|
21
|
+
ERROR_EVENT_HANDLED => 2,
|
22
|
+
ERROR_EVENT_RETRIED => 3,
|
23
|
+
ERROR_EVENT_RETRY_STOPPED => 4,
|
24
|
+
ERROR_EVENT_DISCARDED => 5,
|
25
|
+
}.freeze, _prefix: :error_event
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -4,6 +4,8 @@ module GoodJob
|
|
4
4
|
# ActiveRecord model to share behavior between {Job} and {Execution} models
|
5
5
|
# which both read out of the same table.
|
6
6
|
class BaseExecution < BaseRecord
|
7
|
+
include ErrorEvents
|
8
|
+
|
7
9
|
self.table_name = 'good_jobs'
|
8
10
|
|
9
11
|
# With a given class name
|
@@ -37,6 +39,13 @@ module GoodJob
|
|
37
39
|
def discrete_support?
|
38
40
|
GoodJob::DiscreteExecution.migrated?
|
39
41
|
end
|
42
|
+
|
43
|
+
def error_event_migrated?
|
44
|
+
return true if columns_hash["error_event"].present?
|
45
|
+
|
46
|
+
migration_pending_warning!
|
47
|
+
false
|
48
|
+
end
|
40
49
|
end
|
41
50
|
|
42
51
|
# The ActiveJob job class, as a string
|
@@ -1,10 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module GoodJob
|
3
4
|
# Base ActiveRecord class that all GoodJob models inherit from.
|
4
5
|
# Parent class can be configured with +GoodJob.active_record_parent_class+.
|
5
6
|
# @!parse
|
6
7
|
# class BaseRecord < ActiveRecord::Base; end
|
7
|
-
class BaseRecord <
|
8
|
+
class BaseRecord < ActiveRecordParentClass
|
8
9
|
self.abstract_class = true
|
9
10
|
|
10
11
|
def self.migration_pending_warning!
|
@@ -26,5 +27,7 @@ module GoodJob
|
|
26
27
|
migration_pending_warning!
|
27
28
|
false
|
28
29
|
end
|
30
|
+
|
31
|
+
ActiveSupport.run_load_hooks(:good_job_base_record, self)
|
29
32
|
end
|
30
33
|
end
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module GoodJob # :nodoc:
|
4
4
|
class DiscreteExecution < BaseRecord
|
5
|
+
include ErrorEvents
|
6
|
+
|
5
7
|
self.table_name = 'good_job_executions'
|
6
8
|
|
7
9
|
belongs_to :execution, class_name: 'GoodJob::Execution', foreign_key: 'active_job_id', primary_key: 'active_job_id', inverse_of: :discrete_executions, optional: true
|
@@ -11,6 +13,13 @@ module GoodJob # :nodoc:
|
|
11
13
|
|
12
14
|
alias_attribute :performed_at, :created_at
|
13
15
|
|
16
|
+
def self.error_event_migrated?
|
17
|
+
return true if columns_hash["error_event"].present?
|
18
|
+
|
19
|
+
migration_pending_warning!
|
20
|
+
false
|
21
|
+
end
|
22
|
+
|
14
23
|
def number
|
15
24
|
serialized_params.fetch('executions', 0) + 1
|
16
25
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module GoodJob
|
3
4
|
# ActiveRecord model that represents an +ActiveJob+ job.
|
4
5
|
class Execution < BaseExecution
|
@@ -262,7 +263,11 @@ module GoodJob
|
|
262
263
|
unfinished.dequeueing_ordered(parsed_queues).only_scheduled.limit(1).with_advisory_lock(unlock_session: true, select_limit: queue_select_limit) do |executions|
|
263
264
|
execution = executions.first
|
264
265
|
break if execution.blank?
|
265
|
-
|
266
|
+
|
267
|
+
unless execution.executable?
|
268
|
+
result = ExecutionResult.new(value: nil, unexecutable: true)
|
269
|
+
break
|
270
|
+
end
|
266
271
|
|
267
272
|
yield(execution) if block_given?
|
268
273
|
result = execution.perform
|
@@ -373,10 +378,14 @@ module GoodJob
|
|
373
378
|
if discrete?
|
374
379
|
interrupt_error_string = self.class.format_error(GoodJob::InterruptError.new("Interrupted after starting perform at '#{performed_at}'"))
|
375
380
|
self.error = interrupt_error_string
|
376
|
-
|
381
|
+
self.error_event = ERROR_EVENT_INTERRUPTED if self.class.error_event_migrated?
|
382
|
+
|
383
|
+
discrete_execution_attrs = {
|
377
384
|
error: interrupt_error_string,
|
378
|
-
finished_at: Time.current
|
379
|
-
|
385
|
+
finished_at: Time.current,
|
386
|
+
}
|
387
|
+
discrete_execution_attrs[:error_event] = GoodJob::DiscreteExecution.error_events[GoodJob::DiscreteExecution::ERROR_EVENT_INTERRUPTED] if self.class.error_event_migrated?
|
388
|
+
discrete_executions.where(finished_at: nil).where.not(performed_at: nil).update_all(discrete_execution_attrs) # rubocop:disable Rails/SkipsModelValidations
|
380
389
|
end
|
381
390
|
end
|
382
391
|
|
@@ -405,15 +414,34 @@ module GoodJob
|
|
405
414
|
end
|
406
415
|
handled_error ||= current_thread.error_on_retry || current_thread.error_on_discard
|
407
416
|
|
417
|
+
error_event = if handled_error == current_thread.error_on_discard
|
418
|
+
ERROR_EVENT_DISCARDED
|
419
|
+
elsif handled_error == current_thread.error_on_retry
|
420
|
+
ERROR_EVENT_RETRIED
|
421
|
+
elsif handled_error == current_thread.error_on_retry_stopped
|
422
|
+
ERROR_EVENT_RETRY_STOPPED
|
423
|
+
elsif handled_error
|
424
|
+
ERROR_EVENT_HANDLED
|
425
|
+
end
|
426
|
+
|
408
427
|
instrument_payload.merge!(
|
409
428
|
value: value,
|
410
429
|
handled_error: handled_error,
|
411
|
-
retried: current_thread.execution_retried
|
430
|
+
retried: current_thread.execution_retried,
|
431
|
+
error_event: error_event
|
412
432
|
)
|
413
|
-
ExecutionResult.new(value: value, handled_error: handled_error, retried: current_thread.execution_retried)
|
433
|
+
ExecutionResult.new(value: value, handled_error: handled_error, error_event: error_event, retried: current_thread.execution_retried)
|
414
434
|
rescue StandardError => e
|
435
|
+
error_event = if e.is_a?(GoodJob::InterruptError)
|
436
|
+
ERROR_EVENT_INTERRUPTED
|
437
|
+
elsif e == current_thread.error_on_retry_stopped
|
438
|
+
ERROR_EVENT_RETRY_STOPPED
|
439
|
+
else
|
440
|
+
ERROR_EVENT_UNHANDLED
|
441
|
+
end
|
442
|
+
|
415
443
|
instrument_payload[:unhandled_error] = e
|
416
|
-
ExecutionResult.new(value: nil, unhandled_error: e)
|
444
|
+
ExecutionResult.new(value: nil, unhandled_error: e, error_event: error_event)
|
417
445
|
end
|
418
446
|
end
|
419
447
|
|
@@ -422,9 +450,14 @@ module GoodJob
|
|
422
450
|
if job_error
|
423
451
|
error_string = self.class.format_error(job_error)
|
424
452
|
self.error = error_string
|
425
|
-
|
453
|
+
self.error_event = result.error_event if self.class.error_event_migrated?
|
454
|
+
if discrete_execution
|
455
|
+
discrete_execution.error = error_string
|
456
|
+
discrete_execution.error_event = result.error_event if discrete_execution.class.error_event_migrated?
|
457
|
+
end
|
426
458
|
else
|
427
459
|
self.error = nil
|
460
|
+
self.error_event = nil if self.class.error_event_migrated?
|
428
461
|
end
|
429
462
|
|
430
463
|
reenqueued = result.retried? || retried_good_job_id.present?
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module GoodJob
|
3
4
|
# Stores the results of job execution
|
4
5
|
class ExecutionResult
|
@@ -8,18 +9,32 @@ module GoodJob
|
|
8
9
|
attr_reader :handled_error
|
9
10
|
# @return [Exception, nil]
|
10
11
|
attr_reader :unhandled_error
|
11
|
-
# @return [
|
12
|
+
# @return [String, nil]
|
13
|
+
attr_reader :error_event
|
14
|
+
# @return [Boolean, nil]
|
15
|
+
attr_reader :unexecutable
|
16
|
+
# @return [Boolean, nil]
|
12
17
|
attr_reader :retried
|
13
18
|
alias retried? retried
|
14
19
|
|
15
20
|
# @param value [Object, nil]
|
16
21
|
# @param handled_error [Exception, nil]
|
17
22
|
# @param unhandled_error [Exception, nil]
|
18
|
-
|
23
|
+
# @param error_event [String, nil]
|
24
|
+
# @param unexecutable [Boolean, nil]
|
25
|
+
# @param retried [Boolean, nil]
|
26
|
+
def initialize(value:, handled_error: nil, unhandled_error: nil, error_event: nil, unexecutable: nil, retried: false)
|
19
27
|
@value = value
|
20
28
|
@handled_error = handled_error
|
21
29
|
@unhandled_error = unhandled_error
|
30
|
+
@error_event = error_event
|
31
|
+
@unexecutable = unexecutable
|
22
32
|
@retried = retried
|
23
33
|
end
|
34
|
+
|
35
|
+
# @return [Boolean]
|
36
|
+
def succeeded?
|
37
|
+
!(handled_error || unhandled_error || unexecutable || retried)
|
38
|
+
end
|
24
39
|
end
|
25
40
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GoodJob
|
4
|
+
class I18nConfig < ::I18n::Config
|
5
|
+
BACKEND = I18n::Backend::Simple.new
|
6
|
+
AVAILABLE_LOCALES = GoodJob::Engine.root.join("config/locales").glob("*.yml").map { |path| File.basename(path, ".yml").to_sym }.uniq
|
7
|
+
AVAILABLE_LOCALES_SET = AVAILABLE_LOCALES.inject(Set.new) { |set, locale| set << locale.to_s << locale.to_sym }
|
8
|
+
|
9
|
+
def backend
|
10
|
+
BACKEND
|
11
|
+
end
|
12
|
+
|
13
|
+
def available_locales
|
14
|
+
AVAILABLE_LOCALES
|
15
|
+
end
|
16
|
+
|
17
|
+
def available_locales_set
|
18
|
+
AVAILABLE_LOCALES_SET
|
19
|
+
end
|
20
|
+
|
21
|
+
def default_locale
|
22
|
+
GoodJob.configuration.dashboard_default_locale
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/app/models/good_job/job.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module GoodJob
|
3
4
|
# ActiveRecord model that represents an +ActiveJob+ job.
|
4
5
|
# There is not a table in the database whose discrete rows represents "Jobs".
|
@@ -198,6 +199,7 @@ module GoodJob
|
|
198
199
|
|
199
200
|
execution.class.transaction(joinable: false, requires_new: true) do
|
200
201
|
new_active_job = active_job.retry_job(wait: 0, error: execution.error)
|
202
|
+
execution.error_event = ERROR_EVENT_RETRIED if execution.error && execution.class.error_event_migrated?
|
201
203
|
execution.save!
|
202
204
|
end
|
203
205
|
end
|
@@ -221,7 +223,8 @@ module GoodJob
|
|
221
223
|
update_execution = proc do
|
222
224
|
execution.update(
|
223
225
|
finished_at: Time.current,
|
224
|
-
error: GoodJob::Execution.format_error(job_error)
|
226
|
+
error: GoodJob::Execution.format_error(job_error),
|
227
|
+
error_event: :discarded
|
225
228
|
)
|
226
229
|
end
|
227
230
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'socket'
|
3
4
|
|
4
5
|
module GoodJob # :nodoc:
|
@@ -58,8 +59,10 @@ module GoodJob # :nodoc:
|
|
58
59
|
proctitle: $PROGRAM_NAME,
|
59
60
|
preserve_job_records: GoodJob.preserve_job_records,
|
60
61
|
retry_on_unhandled_error: GoodJob.retry_on_unhandled_error,
|
61
|
-
schedulers: GoodJob::Scheduler.instances.map(&:
|
62
|
+
schedulers: GoodJob::Scheduler.instances.map(&:stats),
|
62
63
|
cron_enabled: GoodJob.configuration.enable_cron?,
|
64
|
+
total_succeeded_executions_count: GoodJob::Scheduler.instances.sum { |scheduler| scheduler.stats.fetch(:succeeded_executions_count) },
|
65
|
+
total_errored_executions_count: GoodJob::Scheduler.instances.sum { |scheduler| scheduler.stats.fetch(:errored_executions_count) },
|
63
66
|
}
|
64
67
|
end
|
65
68
|
|
@@ -98,8 +101,16 @@ module GoodJob # :nodoc:
|
|
98
101
|
end
|
99
102
|
end
|
100
103
|
|
104
|
+
def state
|
105
|
+
super || {}
|
106
|
+
end
|
107
|
+
|
101
108
|
def basename
|
102
|
-
File.basename(state
|
109
|
+
File.basename(state.fetch("proctitle", ""))
|
110
|
+
end
|
111
|
+
|
112
|
+
def schedulers
|
113
|
+
state.fetch("schedulers", [])
|
103
114
|
end
|
104
115
|
|
105
116
|
def refresh_if_stale(cleanup: false)
|
@@ -35,7 +35,7 @@
|
|
35
35
|
</div>
|
36
36
|
<% if execution.error %>
|
37
37
|
<div class="mt-3 small">
|
38
|
-
<strong class="small"><%=t ".error" %>:</strong>
|
38
|
+
<strong class="small"><%=t "good_job.shared.error" %>:</strong>
|
39
39
|
<code class="text-wrap text-break m-0 text-black"><%= execution.error %></code>
|
40
40
|
</div>
|
41
41
|
<% end %>
|