good_job 3.24.0 → 3.25.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 +28 -0
- data/README.md +38 -1
- data/app/helpers/good_job/application_helper.rb +5 -36
- data/app/helpers/good_job/icons_helper.rb +41 -0
- data/app/models/good_job/execution.rb +45 -34
- data/app/models/good_job/execution_result.rb +8 -4
- data/app/models/good_job/job.rb +9 -1
- data/app/models/good_job/process.rb +1 -1
- data/app/views/good_job/_custom_execution_details.html.erb +11 -0
- data/app/views/good_job/_custom_job_details.html.erb +10 -0
- data/app/views/good_job/batches/_jobs.erb +4 -4
- data/app/views/good_job/jobs/_executions.erb +1 -0
- data/app/views/good_job/jobs/_table.erb +5 -5
- data/app/views/good_job/jobs/show.html.erb +2 -0
- data/app/views/good_job/shared/_alert.erb +2 -2
- data/app/views/good_job/shared/_navbar.erb +1 -1
- data/app/views/good_job/shared/icons/_check.html.erb +1 -1
- data/app/views/good_job/shared/icons/_exclamation.html.erb +1 -1
- data/lib/good_job/adapter.rb +35 -11
- data/lib/good_job/configuration.rb +10 -1
- data/lib/good_job/current_thread.rb +7 -0
- data/lib/good_job/daemon.rb +9 -0
- data/lib/good_job/notifier/process_heartbeat.rb +3 -3
- data/lib/good_job/{assignable_connection.rb → overridable_connection.rb} +7 -14
- data/lib/good_job/version.rb +1 -1
- data/lib/good_job.rb +1 -1
- metadata +6 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 47f288c089d005a2b97e5fe6d7bd8fb7094d48fb52565bacf0493b5e547f6516
|
|
4
|
+
data.tar.gz: cf8cd69a7a1f64fee5172b7fd3b184d774eed0cd48d97ba0d01971af0d721646
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3c74189b7315da1fcb5747b285cb4d7837550dcf1456899f2d6f023065a893bd99c93a7e292415b6796a64232335a732666a7783fc04f2319968fff4bfaf7dcc
|
|
7
|
+
data.tar.gz: 67fd833bfa126355ef0853263c7e72b5bfa5d013e1061bc128480408a52b49eddf9724686e3dc47293f8713696074f8029452c035159aee50940f0b320ff36ab
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [v3.25.0](https://github.com/bensheldon/good_job/tree/v3.25.0) (2024-02-22)
|
|
4
|
+
|
|
5
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v3.24.0...v3.25.0)
|
|
6
|
+
|
|
7
|
+
**Implemented enhancements:**
|
|
8
|
+
|
|
9
|
+
- Allow disabling of Dashboard Live Polling configuration [\#1235](https://github.com/bensheldon/good_job/pull/1235) ([erick-tmr](https://github.com/erick-tmr))
|
|
10
|
+
- Add customizable extension partials to good\_job/jobs\#show view [\#1200](https://github.com/bensheldon/good_job/pull/1200) ([grncdr](https://github.com/grncdr))
|
|
11
|
+
|
|
12
|
+
**Fixed bugs:**
|
|
13
|
+
|
|
14
|
+
- Fix default engine cron value [\#1258](https://github.com/bensheldon/good_job/pull/1258) ([hss-mateus](https://github.com/hss-mateus))
|
|
15
|
+
- Print an error when daemon pidfile dir doesn't exist [\#1252](https://github.com/bensheldon/good_job/pull/1252) ([thepry](https://github.com/thepry))
|
|
16
|
+
|
|
17
|
+
**Closed issues:**
|
|
18
|
+
|
|
19
|
+
- Production deployment question [\#1257](https://github.com/bensheldon/good_job/issues/1257)
|
|
20
|
+
- Daemon and App not connecting to secondary database [\#1254](https://github.com/bensheldon/good_job/issues/1254)
|
|
21
|
+
- Logging with logger.warn in classes is suppressed by good job? \(semantic\_logger\) [\#1250](https://github.com/bensheldon/good_job/issues/1250)
|
|
22
|
+
|
|
23
|
+
**Merged pull requests:**
|
|
24
|
+
|
|
25
|
+
- Fix Active Record connection changes on Rails head [\#1259](https://github.com/bensheldon/good_job/pull/1259) ([bensheldon](https://github.com/bensheldon))
|
|
26
|
+
- \[Docs\] Bulk.enqueue takes an array of jobs [\#1256](https://github.com/bensheldon/good_job/pull/1256) ([jpcamara](https://github.com/jpcamara))
|
|
27
|
+
- Clean up icon helpers for less noisy view rendering [\#1248](https://github.com/bensheldon/good_job/pull/1248) ([bensheldon](https://github.com/bensheldon))
|
|
28
|
+
- Use dotenv-rails instead of dotenv [\#1247](https://github.com/bensheldon/good_job/pull/1247) ([bensheldon](https://github.com/bensheldon))
|
|
29
|
+
- Perform inline retries iteratively instead of recursively [\#1246](https://github.com/bensheldon/good_job/pull/1246) ([bensheldon](https://github.com/bensheldon))
|
|
30
|
+
|
|
3
31
|
## [v3.24.0](https://github.com/bensheldon/good_job/tree/v3.24.0) (2024-02-12)
|
|
4
32
|
|
|
5
33
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v3.23.0...v3.24.0)
|
data/README.md
CHANGED
|
@@ -41,6 +41,7 @@ For more of the story of GoodJob, read the [introductory blog post](https://isla
|
|
|
41
41
|
- [Dashboard](#dashboard)
|
|
42
42
|
- [API-only Rails applications](#api-only-rails-applications)
|
|
43
43
|
- [Live polling](#live-polling)
|
|
44
|
+
- [Extending dashboard views](#extending-dashboard-views)
|
|
44
45
|
- [Job priority](#job-priority)
|
|
45
46
|
- [Concurrency controls](#concurrency-controls)
|
|
46
47
|
- [How concurrency controls work](#how-concurrency-controls-work)
|
|
@@ -451,6 +452,42 @@ end
|
|
|
451
452
|
|
|
452
453
|
The Dashboard can be set to automatically refresh by checking "Live Poll" in the Dashboard header, or by setting `?poll=10` with the interval in seconds (default 30 seconds).
|
|
453
454
|
|
|
455
|
+
#### Extending dashboard views
|
|
456
|
+
|
|
457
|
+
GoodJob exposes some views that are intended to be overriden by placing views in your application:
|
|
458
|
+
|
|
459
|
+
- [`app/views/good_job/jobs/_custom_job_details.html.erb`](app/views/good_job/_custom_job_details.html.erb): content added to this partial will be displayed above the argument list on the good_job/jobs#show page.
|
|
460
|
+
- [`app/views/good_job/jobs/_custom_execution_details.html.erb`](app/views/good_job/_custom_execution_details.html.erb): content added to this partial will be displayed above each execution on the good_job/jobs#show page.
|
|
461
|
+
|
|
462
|
+
**Warning:** these partials expose classes (such as `GoodJob::Job`) that are considered internal implementation details of GoodJob. You should always test your custom partials after upgrading GoodJob.
|
|
463
|
+
|
|
464
|
+
For example, if your app deals with widgets and you want to show a link to the widget a job acted on, you can add the following to `app/views/good_job/_custom_job_details.html.erb`:
|
|
465
|
+
|
|
466
|
+
```erb
|
|
467
|
+
<%# file: app/views/good_job/_custom_job_details.html.erb %>
|
|
468
|
+
<% arguments = job.active_job.arguments rescue [] %>
|
|
469
|
+
<% widgets = arguments.select { |arg| arg.is_a?(Widget) } %>
|
|
470
|
+
<% if widgets.any? %>
|
|
471
|
+
<div class="my-4">
|
|
472
|
+
<h5>Widgets</h5>
|
|
473
|
+
<ul>
|
|
474
|
+
<% widgets.each do |widget| %>
|
|
475
|
+
<li><%= link_to widget.name, main_app.widget_url(widget) %></li>
|
|
476
|
+
<% end %>
|
|
477
|
+
</ul>
|
|
478
|
+
</div>
|
|
479
|
+
<% end %>
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
As a second example, you may wish to show a link to a log aggregator next to each job execution. You can do this by adding the following to `app/views/good_job/_custom_execution_details.html.erb`:
|
|
483
|
+
|
|
484
|
+
```erb
|
|
485
|
+
<%# file: app/views/good_job/_custom_execution_details.html.erb %>
|
|
486
|
+
<div class="py-3">
|
|
487
|
+
<%= link_to "Logs", main_app.logs_url(filter: { job_id: job.id }, start_time: execution.performed_at, end_time: execution.finished_at + 1.minute) %>
|
|
488
|
+
</div>
|
|
489
|
+
```
|
|
490
|
+
|
|
454
491
|
### Job priority
|
|
455
492
|
|
|
456
493
|
Higher priority numbers run first in all versions of GoodJob v3.x and below. GoodJob v4.x will change job `priority` to give smaller numbers higher priority (default: `0`), in accordance with Active Job's definition of priority (see #524). To opt-in to this behavior now, set `config.good_job.smaller_number_is_higher_priority = true` in your GoodJob initializer or `application.rb`.
|
|
@@ -609,7 +646,7 @@ end
|
|
|
609
646
|
active_jobs.all?(&:provider_job_id)
|
|
610
647
|
|
|
611
648
|
# Bulk enqueue Active Job instances directly without using `.perform_later`:
|
|
612
|
-
GoodJob::Bulk.enqueue(MyJob.new, AnotherJob.new)
|
|
649
|
+
GoodJob::Bulk.enqueue([MyJob.new, AnotherJob.new])
|
|
613
650
|
```
|
|
614
651
|
|
|
615
652
|
### Batches
|
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
module GoodJob
|
|
4
4
|
module ApplicationHelper
|
|
5
|
+
# Explicit helper inclusion because ApplicationController inherits from the host app.
|
|
6
|
+
#
|
|
7
|
+
# We can't rely on +config.action_controller.include_all_helpers = true+ in the host app.
|
|
8
|
+
include IconsHelper
|
|
9
|
+
|
|
5
10
|
def format_duration(sec)
|
|
6
11
|
return unless sec
|
|
7
12
|
|
|
@@ -24,42 +29,6 @@ module GoodJob
|
|
|
24
29
|
tag.time(text, datetime: timestamp, title: timestamp)
|
|
25
30
|
end
|
|
26
31
|
|
|
27
|
-
STATUS_ICONS = {
|
|
28
|
-
discarded: "exclamation",
|
|
29
|
-
succeeded: "check",
|
|
30
|
-
queued: "dash_circle",
|
|
31
|
-
retried: "arrow_clockwise",
|
|
32
|
-
running: "play",
|
|
33
|
-
scheduled: "clock",
|
|
34
|
-
}.freeze
|
|
35
|
-
|
|
36
|
-
STATUS_COLOR = {
|
|
37
|
-
discarded: "danger",
|
|
38
|
-
succeeded: "success",
|
|
39
|
-
queued: "secondary",
|
|
40
|
-
retried: "warning",
|
|
41
|
-
running: "primary",
|
|
42
|
-
scheduled: "secondary",
|
|
43
|
-
}.freeze
|
|
44
|
-
|
|
45
|
-
def status_badge(status)
|
|
46
|
-
content_tag :span, status_icon(status, class: "text-white") + t(status, scope: 'good_job.status', count: 1),
|
|
47
|
-
class: "badge rounded-pill bg-#{STATUS_COLOR.fetch(status)} d-inline-flex gap-2 ps-1 pe-3 align-items-center"
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def status_icon(status, **options)
|
|
51
|
-
options[:class] ||= "text-#{STATUS_COLOR.fetch(status)}"
|
|
52
|
-
icon = render_icon STATUS_ICONS.fetch(status)
|
|
53
|
-
content_tag :span, icon, **options
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def render_icon(name, **options)
|
|
57
|
-
# workaround to render svg icons without all of the log messages
|
|
58
|
-
partial = lookup_context.find_template("good_job/shared/icons/#{name}", [], true)
|
|
59
|
-
options[:class] = Array(options[:class]).join(" ")
|
|
60
|
-
partial.render(self, { class: options[:class] })
|
|
61
|
-
end
|
|
62
|
-
|
|
63
32
|
def translate_hash(key, **options)
|
|
64
33
|
translation_exists?(key, **options) ? translate(key, **options) : {}
|
|
65
34
|
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GoodJob
|
|
4
|
+
module IconsHelper
|
|
5
|
+
STATUS_ICONS = {
|
|
6
|
+
discarded: "exclamation",
|
|
7
|
+
succeeded: "check",
|
|
8
|
+
queued: "dash_circle",
|
|
9
|
+
retried: "arrow_clockwise",
|
|
10
|
+
running: "play",
|
|
11
|
+
scheduled: "clock",
|
|
12
|
+
}.freeze
|
|
13
|
+
|
|
14
|
+
STATUS_COLOR = {
|
|
15
|
+
discarded: "danger",
|
|
16
|
+
succeeded: "success",
|
|
17
|
+
queued: "secondary",
|
|
18
|
+
retried: "warning",
|
|
19
|
+
running: "primary",
|
|
20
|
+
scheduled: "secondary",
|
|
21
|
+
}.freeze
|
|
22
|
+
|
|
23
|
+
def status_badge(status)
|
|
24
|
+
content_tag :span, status_icon(status, class: "text-white") + t(status, scope: 'good_job.status', count: 1),
|
|
25
|
+
class: "badge rounded-pill bg-#{STATUS_COLOR.fetch(status)} d-inline-flex gap-2 ps-1 pe-3 align-items-center"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def status_icon(status, **options)
|
|
29
|
+
options[:class] ||= "text-#{STATUS_COLOR.fetch(status)}"
|
|
30
|
+
icon = render_icon STATUS_ICONS.fetch(status)
|
|
31
|
+
content_tag :span, icon, **options
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def render_icon(name, **options)
|
|
35
|
+
# workaround to render svg icons without all of the log messages
|
|
36
|
+
partial = lookup_context.find_template("good_job/shared/icons/#{name}", [], true)
|
|
37
|
+
options[:class] = Array(options[:class]).join(" ")
|
|
38
|
+
partial.render(self, { class: options[:class] })
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -343,8 +343,10 @@ module GoodJob
|
|
|
343
343
|
execution.save!
|
|
344
344
|
|
|
345
345
|
if retried
|
|
346
|
-
CurrentThread.execution_retried =
|
|
346
|
+
CurrentThread.execution_retried = execution
|
|
347
347
|
CurrentThread.execution.retried_good_job_id = execution.id unless current_execution.discrete?
|
|
348
|
+
else
|
|
349
|
+
CurrentThread.execution_retried = nil
|
|
348
350
|
end
|
|
349
351
|
|
|
350
352
|
active_job.provider_job_id = execution.id
|
|
@@ -367,22 +369,24 @@ module GoodJob
|
|
|
367
369
|
run_callbacks(:perform) do
|
|
368
370
|
raise PreviouslyPerformedError, 'Cannot perform a job that has already been performed' if finished_at
|
|
369
371
|
|
|
372
|
+
job_performed_at = Time.current
|
|
370
373
|
discrete_execution = nil
|
|
371
374
|
result = GoodJob::CurrentThread.within do |current_thread|
|
|
372
375
|
current_thread.reset
|
|
373
376
|
current_thread.execution = self
|
|
374
377
|
|
|
375
|
-
|
|
376
|
-
|
|
378
|
+
existing_performed_at = performed_at
|
|
379
|
+
if existing_performed_at
|
|
380
|
+
current_thread.execution_interrupted = existing_performed_at
|
|
377
381
|
|
|
378
382
|
if discrete?
|
|
379
|
-
interrupt_error_string = self.class.format_error(GoodJob::InterruptError.new("Interrupted after starting perform at '#{
|
|
383
|
+
interrupt_error_string = self.class.format_error(GoodJob::InterruptError.new("Interrupted after starting perform at '#{existing_performed_at}'"))
|
|
380
384
|
self.error = interrupt_error_string
|
|
381
385
|
self.error_event = ERROR_EVENT_INTERRUPTED if self.class.error_event_migrated?
|
|
382
386
|
|
|
383
387
|
discrete_execution_attrs = {
|
|
384
388
|
error: interrupt_error_string,
|
|
385
|
-
finished_at:
|
|
389
|
+
finished_at: job_performed_at,
|
|
386
390
|
}
|
|
387
391
|
discrete_execution_attrs[:error_event] = GoodJob::ErrorEvents::ERROR_EVENT_ENUMS[GoodJob::ErrorEvents::ERROR_EVENT_INTERRUPTED] if self.class.error_event_migrated?
|
|
388
392
|
discrete_executions.where(finished_at: nil).where.not(performed_at: nil).update_all(discrete_execution_attrs) # rubocop:disable Rails/SkipsModelValidations
|
|
@@ -391,18 +395,17 @@ module GoodJob
|
|
|
391
395
|
|
|
392
396
|
if discrete?
|
|
393
397
|
transaction do
|
|
394
|
-
now = Time.current
|
|
395
398
|
discrete_execution = discrete_executions.create!(
|
|
396
399
|
job_class: job_class,
|
|
397
400
|
queue_name: queue_name,
|
|
398
401
|
serialized_params: serialized_params,
|
|
399
402
|
scheduled_at: (scheduled_at || created_at),
|
|
400
|
-
created_at:
|
|
403
|
+
created_at: job_performed_at
|
|
401
404
|
)
|
|
402
|
-
update!(performed_at:
|
|
405
|
+
update!(performed_at: job_performed_at, executions_count: ((executions_count || 0) + 1))
|
|
403
406
|
end
|
|
404
407
|
else
|
|
405
|
-
update!(performed_at:
|
|
408
|
+
update!(performed_at: job_performed_at)
|
|
406
409
|
end
|
|
407
410
|
|
|
408
411
|
ActiveSupport::Notifications.instrument("perform_job.good_job", { execution: self, process_id: current_thread.process_id, thread_name: current_thread.thread_name }) do |instrument_payload|
|
|
@@ -427,7 +430,7 @@ module GoodJob
|
|
|
427
430
|
instrument_payload.merge!(
|
|
428
431
|
value: value,
|
|
429
432
|
handled_error: handled_error,
|
|
430
|
-
retried: current_thread.execution_retried
|
|
433
|
+
retried: current_thread.execution_retried.present?,
|
|
431
434
|
error_event: error_event
|
|
432
435
|
)
|
|
433
436
|
ExecutionResult.new(value: value, handled_error: handled_error, error_event: error_event, retried: current_thread.execution_retried)
|
|
@@ -445,47 +448,49 @@ module GoodJob
|
|
|
445
448
|
end
|
|
446
449
|
end
|
|
447
450
|
|
|
448
|
-
|
|
451
|
+
job_attributes = {}
|
|
449
452
|
|
|
453
|
+
job_error = result.handled_error || result.unhandled_error
|
|
450
454
|
if job_error
|
|
451
455
|
error_string = self.class.format_error(job_error)
|
|
452
|
-
|
|
453
|
-
|
|
456
|
+
|
|
457
|
+
job_attributes[:error] = error_string
|
|
458
|
+
job_attributes[:error_event] = result.error_event if self.class.error_event_migrated?
|
|
454
459
|
if discrete_execution
|
|
455
460
|
discrete_execution.error = error_string
|
|
456
|
-
discrete_execution.error_event = result.error_event
|
|
461
|
+
discrete_execution.error_event = result.error_event
|
|
457
462
|
end
|
|
458
463
|
else
|
|
459
|
-
|
|
460
|
-
|
|
464
|
+
job_attributes[:error] = nil
|
|
465
|
+
job_attributes[:error_event] = nil
|
|
461
466
|
end
|
|
467
|
+
job_attributes.delete(:error_event) unless self.class.error_event_migrated?
|
|
468
|
+
|
|
469
|
+
job_finished_at = Time.current
|
|
470
|
+
job_attributes[:finished_at] = job_finished_at
|
|
471
|
+
discrete_execution.finished_at = job_finished_at if discrete_execution
|
|
462
472
|
|
|
463
|
-
|
|
464
|
-
|
|
473
|
+
retry_unhandled_error = result.unhandled_error && GoodJob.retry_on_unhandled_error
|
|
474
|
+
reenqueued = result.retried? || retried_good_job_id.present? || retry_unhandled_error
|
|
475
|
+
if reenqueued
|
|
465
476
|
if discrete_execution
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
update!(performed_at: nil, finished_at: nil, retried_good_job_id: nil)
|
|
469
|
-
end
|
|
477
|
+
job_attributes[:performed_at] = nil
|
|
478
|
+
job_attributes[:finished_at] = nil
|
|
470
479
|
else
|
|
471
|
-
|
|
480
|
+
job_attributes[:retried_good_job_id] = retried_good_job_id
|
|
481
|
+
job_attributes[:finished_at] = nil if retry_unhandled_error
|
|
472
482
|
end
|
|
473
|
-
|
|
474
|
-
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
preserve_unhandled = (result.unhandled_error && (GoodJob.retry_on_unhandled_error || GoodJob.preserve_job_records == :on_unhandled_error))
|
|
486
|
+
if GoodJob.preserve_job_records == true || reenqueued || preserve_unhandled || cron_key.present?
|
|
475
487
|
if discrete_execution
|
|
476
|
-
if reenqueued
|
|
477
|
-
self.performed_at = nil
|
|
478
|
-
else
|
|
479
|
-
self.finished_at = now
|
|
480
|
-
end
|
|
481
|
-
discrete_execution.finished_at = now
|
|
482
488
|
transaction do
|
|
483
489
|
discrete_execution.save!
|
|
484
|
-
|
|
490
|
+
update!(job_attributes)
|
|
485
491
|
end
|
|
486
492
|
else
|
|
487
|
-
|
|
488
|
-
save!
|
|
493
|
+
update!(job_attributes)
|
|
489
494
|
end
|
|
490
495
|
else
|
|
491
496
|
destroy_job
|
|
@@ -556,6 +561,12 @@ module GoodJob
|
|
|
556
561
|
@_destroy_job = false
|
|
557
562
|
end
|
|
558
563
|
|
|
564
|
+
def job_state
|
|
565
|
+
state = { queue_name: queue_name }
|
|
566
|
+
state[:scheduled_at] = scheduled_at if scheduled_at
|
|
567
|
+
state
|
|
568
|
+
end
|
|
569
|
+
|
|
559
570
|
private
|
|
560
571
|
|
|
561
572
|
def reset_batch_values(&block)
|
|
@@ -13,9 +13,8 @@ module GoodJob
|
|
|
13
13
|
attr_reader :error_event
|
|
14
14
|
# @return [Boolean, nil]
|
|
15
15
|
attr_reader :unexecutable
|
|
16
|
-
# @return [
|
|
16
|
+
# @return [GoodJob::Execution, nil]
|
|
17
17
|
attr_reader :retried
|
|
18
|
-
alias retried? retried
|
|
19
18
|
|
|
20
19
|
# @param value [Object, nil]
|
|
21
20
|
# @param handled_error [Exception, nil]
|
|
@@ -23,7 +22,7 @@ module GoodJob
|
|
|
23
22
|
# @param error_event [String, nil]
|
|
24
23
|
# @param unexecutable [Boolean, nil]
|
|
25
24
|
# @param retried [Boolean, nil]
|
|
26
|
-
def initialize(value:, handled_error: nil, unhandled_error: nil, error_event: nil, unexecutable: nil, retried:
|
|
25
|
+
def initialize(value:, handled_error: nil, unhandled_error: nil, error_event: nil, unexecutable: nil, retried: nil)
|
|
27
26
|
@value = value
|
|
28
27
|
@handled_error = handled_error
|
|
29
28
|
@unhandled_error = unhandled_error
|
|
@@ -34,7 +33,12 @@ module GoodJob
|
|
|
34
33
|
|
|
35
34
|
# @return [Boolean]
|
|
36
35
|
def succeeded?
|
|
37
|
-
!(handled_error || unhandled_error || unexecutable || retried)
|
|
36
|
+
!(handled_error || unhandled_error || unexecutable || retried?)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @return [Boolean]
|
|
40
|
+
def retried?
|
|
41
|
+
retried.present?
|
|
38
42
|
end
|
|
39
43
|
end
|
|
40
44
|
end
|
data/app/models/good_job/job.rb
CHANGED
|
@@ -195,12 +195,20 @@ module GoodJob
|
|
|
195
195
|
# Do not update `exception_executions` because that comes from rescue_from's arguments
|
|
196
196
|
active_job.executions = (active_job.executions || 0) + 1
|
|
197
197
|
|
|
198
|
+
begin
|
|
199
|
+
error_class, error_message = execution.error.split(GoodJob::Execution::ERROR_MESSAGE_SEPARATOR).map(&:strip)
|
|
200
|
+
error = error_class.constantize.new(error_message)
|
|
201
|
+
rescue StandardError
|
|
202
|
+
error = StandardError.new(execution.error)
|
|
203
|
+
end
|
|
204
|
+
|
|
198
205
|
new_active_job = nil
|
|
199
206
|
GoodJob::CurrentThread.within do |current_thread|
|
|
200
207
|
current_thread.execution = execution
|
|
208
|
+
current_thread.retry_now = true
|
|
201
209
|
|
|
202
210
|
execution.class.transaction(joinable: false, requires_new: true) do
|
|
203
|
-
new_active_job = active_job.retry_job(wait: 0, error:
|
|
211
|
+
new_active_job = active_job.retry_job(wait: 0, error: error)
|
|
204
212
|
execution.error_event = ERROR_EVENT_RETRIED if execution.error && execution.class.error_event_migrated?
|
|
205
213
|
execution.save!
|
|
206
214
|
end
|
|
@@ -6,7 +6,7 @@ module GoodJob # :nodoc:
|
|
|
6
6
|
# ActiveRecord model that represents an GoodJob process (either async or CLI).
|
|
7
7
|
class Process < BaseRecord
|
|
8
8
|
include AdvisoryLockable
|
|
9
|
-
include
|
|
9
|
+
include OverridableConnection
|
|
10
10
|
|
|
11
11
|
# Interval until the process record being updated
|
|
12
12
|
STALE_INTERVAL = 30.seconds
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<%#
|
|
2
|
+
Content added to this partial will be displayed above the collapsible JSON representation of each execution on the jobs#show view.
|
|
3
|
+
|
|
4
|
+
You can make use of the following variables:
|
|
5
|
+
|
|
6
|
+
- `job`: The `GoodJob::Job` instance.
|
|
7
|
+
- `execution`: The `GoodJob::DiscreteExecution` instance.
|
|
8
|
+
- `main_app`: Use this to access helpers (e.g. route helpers) from your application.
|
|
9
|
+
|
|
10
|
+
Note: the `GoodJob::Job` and `GoodJob::Execution` classes are considered an internal implementation detail of GoodJob and may change at any time. Please test your view extensions when upgrading.
|
|
11
|
+
%>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<%#
|
|
2
|
+
Content added to this partial will be displayed above the argument list on the good_job/jobs#show page.
|
|
3
|
+
|
|
4
|
+
You can make use of the following variables:
|
|
5
|
+
|
|
6
|
+
- `job`: The `GoodJob::Job` instance. Calling `job.active_job` will deserialize an instance of your ActiveJob class.
|
|
7
|
+
- `main_app`: Use this to access helpers (e.g. route helpers) from your application.
|
|
8
|
+
|
|
9
|
+
Note: the `GoodJob::Job` class is considered an internal implementation detail and may change at any time. Please test your view extensions when upgrading.
|
|
10
|
+
%>
|
|
@@ -52,27 +52,27 @@
|
|
|
52
52
|
|
|
53
53
|
<div class="dropdown float-end">
|
|
54
54
|
<button class="d-flex align-items-center btn btn-sm" type="button" id="<%= dom_id(job, :actions) %>" data-bs-toggle="dropdown" aria-expanded="false">
|
|
55
|
-
<%=
|
|
55
|
+
<%= render_icon :dots %>
|
|
56
56
|
<span class="visually-hidden"><%=t ".actions.title" %></span>
|
|
57
57
|
</button>
|
|
58
58
|
<ul class="dropdown-menu shadow" aria-labelledby="<%= dom_id(job, :actions) %>">
|
|
59
59
|
<li>
|
|
60
60
|
<% job_reschedulable = job.status.in? [:scheduled, :retried, :queued] %>
|
|
61
61
|
<%= link_to reschedule_job_path(job.id), method: :put, class: "dropdown-item #{'disabled' unless job_reschedulable}", title: t(".actions.reschedule"), data: { confirm: t(".actions.confirm_reschedule"), disable: true } do %>
|
|
62
|
-
<%=
|
|
62
|
+
<%= render_icon "skip_forward" %>
|
|
63
63
|
<%=t "good_job.actions.reschedule" %>
|
|
64
64
|
<% end %>
|
|
65
65
|
</li>
|
|
66
66
|
<li>
|
|
67
67
|
<% job_discardable = job.status.in? [:scheduled, :retried, :queued] %>
|
|
68
68
|
<%= link_to discard_job_path(job.id), method: :put, class: "dropdown-item #{'disabled' unless job_discardable}", title: t(".actions.discard"), data: { confirm: t(".actions.confirm_discard"), disable: true } do %>
|
|
69
|
-
<%=
|
|
69
|
+
<%= render_icon "stop" %>
|
|
70
70
|
<%=t "good_job.actions.discard" %>
|
|
71
71
|
<% end %>
|
|
72
72
|
</li>
|
|
73
73
|
<li>
|
|
74
74
|
<%= link_to retry_job_path(job.id), method: :put, class: "dropdown-item #{'disabled' unless job.status == :discarded}", title: t(".actions.retry"), data: { confirm: t(".actions.confirm_retry"), disable: true } do %>
|
|
75
|
-
<%=
|
|
75
|
+
<%= render_icon "arrow_clockwise" %>
|
|
76
76
|
<%=t "good_job.actions.retry" %>
|
|
77
77
|
<% end %>
|
|
78
78
|
</li>
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
</div>
|
|
41
41
|
<% end %>
|
|
42
42
|
<% end %>
|
|
43
|
+
<%= render 'good_job/custom_execution_details', execution: execution, job: @job %>
|
|
43
44
|
<%= tag.div id: dom_id(execution, "params"), class: "list-group-item collapse small bg-dark text-light" do %>
|
|
44
45
|
<%= tag.pre JSON.pretty_generate(execution.display_serialized_params) %>
|
|
45
46
|
<% end %>
|
|
@@ -109,34 +109,34 @@
|
|
|
109
109
|
|
|
110
110
|
<div class="dropdown float-end">
|
|
111
111
|
<button class="d-flex align-items-center btn btn-sm" type="button" id="<%= dom_id(job, :actions) %>" data-bs-toggle="dropdown" aria-expanded="false">
|
|
112
|
-
<%=
|
|
112
|
+
<%= render_icon "dots" %>
|
|
113
113
|
<span class="visually-hidden"><%=t ".actions.title" %></span>
|
|
114
114
|
</button>
|
|
115
115
|
<ul class="dropdown-menu shadow" aria-labelledby="<%= dom_id(job, :actions) %>">
|
|
116
116
|
<li>
|
|
117
117
|
<% job_reschedulable = job.status.in? [:scheduled, :retried, :queued] %>
|
|
118
118
|
<%= link_to reschedule_job_path(job.id), method: :put, class: "dropdown-item #{'disabled' unless job_reschedulable}", title: t("good_job.jobs.actions.reschedule"), data: { confirm: t("good_job.jobs.actions.confirm_reschedule"), disable: true } do %>
|
|
119
|
-
<%=
|
|
119
|
+
<%= render_icon "skip_forward" %>
|
|
120
120
|
<%=t "good_job.actions.reschedule" %>
|
|
121
121
|
<% end %>
|
|
122
122
|
</li>
|
|
123
123
|
<li>
|
|
124
124
|
<% job_discardable = job.status.in? [:scheduled, :retried, :queued] %>
|
|
125
125
|
<%= link_to discard_job_path(job.id), method: :put, class: "dropdown-item #{'disabled' unless job_discardable}", title: t("good_job.jobs.actions.discard"), data: { confirm: t("good_job.jobs.actions.confirm_discard"), disable: true } do %>
|
|
126
|
-
<%=
|
|
126
|
+
<%= render_icon "stop" %>
|
|
127
127
|
<%=t "good_job.actions.discard" %>
|
|
128
128
|
<% end %>
|
|
129
129
|
</li>
|
|
130
130
|
<li>
|
|
131
131
|
<% job_force_discardable = job.status.in? [:running] %>
|
|
132
132
|
<%= link_to force_discard_job_path(job.id), method: :put, class: "dropdown-item #{'disabled' unless job_force_discardable}", title: t("good_job.jobs.actions.force_discard"), data: { confirm: t("good_job.jobs.actions.confirm_force_discard"), disable: true } do %>
|
|
133
|
-
<%=
|
|
133
|
+
<%= render_icon "eject" %>
|
|
134
134
|
<%=t "good_job.actions.force_discard" %>
|
|
135
135
|
<% end %>
|
|
136
136
|
</li>
|
|
137
137
|
<li>
|
|
138
138
|
<%= link_to retry_job_path(job.id), method: :put, class: "dropdown-item #{'disabled' unless job.status == :discarded}", title: t("good_job.jobs.actions.retry"), data: { confirm: t("good_job.jobs.actions.confirm_retry"), disable: true } do %>
|
|
139
|
-
<%=
|
|
139
|
+
<%= render_icon "arrow_clockwise" %>
|
|
140
140
|
<%=t "good_job.actions.retry" %>
|
|
141
141
|
<% end %>
|
|
142
142
|
</li>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<% if notice %>
|
|
3
3
|
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
|
|
4
4
|
<div class="toast-body d-flex align-items-center gap-2">
|
|
5
|
-
<%=
|
|
5
|
+
<%= render_icon "check", class: "flex-shrink-0 text-success" %>
|
|
6
6
|
<div class="flex-fill"><%= notice %></div>
|
|
7
7
|
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
|
|
8
8
|
</div>
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<% if alert %>
|
|
12
12
|
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
|
|
13
13
|
<div class="toast-body d-flex align-items-center gap-2">
|
|
14
|
-
<%=
|
|
14
|
+
<%= render_icon "exclamation", class: "flex-shrink-0 text-danger" %>
|
|
15
15
|
<div class="flex-fill"><%= alert %></div>
|
|
16
16
|
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
|
|
17
17
|
</div>
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
|
|
61
61
|
<li class="nav-item d-flex flex-column justify-content-center">
|
|
62
62
|
<div class="form-check form-switch m-0">
|
|
63
|
-
<%= check_box_tag "live_poll", params.fetch("poll", 30), params[:poll].present
|
|
63
|
+
<%= check_box_tag "live_poll", params.fetch("poll", 30), (GoodJob.configuration.dashboard_live_poll_enabled && params[:poll].present?), role: "switch", class: "form-check-input", disabled: !GoodJob.configuration.dashboard_live_poll_enabled %>
|
|
64
64
|
<label class="form-check-label navbar-text p-0" for="live_poll">
|
|
65
65
|
<%= t(".live_poll") %>
|
|
66
66
|
</label>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!-- https://icons.getbootstrap.com/icons/check-circle/ -->
|
|
2
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-check-circle" viewBox="0 0 16 16">
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-check-circle <%= local_assigns[:class] %>" viewBox="0 0 16 16">
|
|
3
3
|
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z" />
|
|
4
4
|
<path d="M10.97 4.97a.235.235 0 0 0-.02.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-1.071-1.05z" />
|
|
5
5
|
</svg>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!-- https://icons.getbootstrap.com/icons/exclamation-circle/ -->
|
|
2
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-exclamation-circle" viewBox="0 0 16 16">
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-exclamation-circle <%= local_assigns[:class] %>" viewBox="0 0 16 16">
|
|
3
3
|
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z" />
|
|
4
4
|
<path d="M7.002 11a1 1 0 1 1 2 0 1 1 0 0 1-2 0zM7.1 4.995a.905.905 0 1 1 1.8 0l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 4.995z" />
|
|
5
5
|
</svg>
|
data/lib/good_job/adapter.rb
CHANGED
|
@@ -96,6 +96,13 @@ module GoodJob
|
|
|
96
96
|
begin
|
|
97
97
|
inline_execution = inline_executions.shift
|
|
98
98
|
inline_result = inline_execution.perform
|
|
99
|
+
|
|
100
|
+
retried_execution = inline_result.retried
|
|
101
|
+
while retried_execution && retried_execution.scheduled_at <= Time.current
|
|
102
|
+
inline_execution = retried_execution
|
|
103
|
+
inline_result = inline_execution.perform
|
|
104
|
+
retried_execution = inline_result.retried
|
|
105
|
+
end
|
|
99
106
|
ensure
|
|
100
107
|
inline_execution.advisory_unlock
|
|
101
108
|
inline_execution.run_callbacks(:perform_unlocked)
|
|
@@ -141,26 +148,43 @@ module GoodJob
|
|
|
141
148
|
|
|
142
149
|
Rails.application.executor.wrap do
|
|
143
150
|
will_execute_inline = execute_inline? && (scheduled_at.nil? || scheduled_at <= Time.current)
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
+
will_retry_inline = will_execute_inline && CurrentThread.execution&.active_job_id == active_job.job_id && !CurrentThread.retry_now
|
|
152
|
+
|
|
153
|
+
if will_retry_inline
|
|
154
|
+
execution = GoodJob::Execution.enqueue(
|
|
155
|
+
active_job,
|
|
156
|
+
scheduled_at: scheduled_at
|
|
157
|
+
)
|
|
158
|
+
elsif will_execute_inline
|
|
159
|
+
execution = GoodJob::Execution.enqueue(
|
|
160
|
+
active_job,
|
|
161
|
+
scheduled_at: scheduled_at,
|
|
162
|
+
create_with_advisory_lock: true
|
|
163
|
+
)
|
|
151
164
|
begin
|
|
152
165
|
result = execution.perform
|
|
166
|
+
|
|
167
|
+
retried_execution = result.retried
|
|
168
|
+
while retried_execution && (retried_execution.scheduled_at.nil? || retried_execution.scheduled_at <= Time.current)
|
|
169
|
+
execution = retried_execution
|
|
170
|
+
result = execution.perform
|
|
171
|
+
retried_execution = result.retried
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
Notifier.notify(retried_execution.job_state) if retried_execution&.scheduled_at && retried_execution.scheduled_at > Time.current && send_notify?(active_job)
|
|
153
175
|
ensure
|
|
154
176
|
execution.advisory_unlock
|
|
155
177
|
execution.run_callbacks(:perform_unlocked)
|
|
156
178
|
end
|
|
157
179
|
raise result.unhandled_error if result.unhandled_error
|
|
158
180
|
else
|
|
159
|
-
|
|
160
|
-
|
|
181
|
+
execution = GoodJob::Execution.enqueue(
|
|
182
|
+
active_job,
|
|
183
|
+
scheduled_at: scheduled_at
|
|
184
|
+
)
|
|
161
185
|
|
|
162
|
-
executed_locally = execute_async? && @capsule&.create_thread(job_state)
|
|
163
|
-
Notifier.notify(job_state) if !executed_locally && send_notify?(active_job)
|
|
186
|
+
executed_locally = execute_async? && @capsule&.create_thread(execution.job_state)
|
|
187
|
+
Notifier.notify(execution.job_state) if !executed_locally && send_notify?(active_job)
|
|
164
188
|
end
|
|
165
189
|
|
|
166
190
|
execution
|
|
@@ -31,6 +31,8 @@ module GoodJob
|
|
|
31
31
|
DEFAULT_ENABLE_LISTEN_NOTIFY = true
|
|
32
32
|
# Default Dashboard I18n locale
|
|
33
33
|
DEFAULT_DASHBOARD_DEFAULT_LOCALE = :en
|
|
34
|
+
# Default Dashboard Live Poll button enabled
|
|
35
|
+
DEFAULT_DASHBOARD_LIVE_POLL_ENABLED = true
|
|
34
36
|
|
|
35
37
|
def self.validate_execution_mode(execution_mode)
|
|
36
38
|
raise ArgumentError, "GoodJob execution mode must be one of #{EXECUTION_MODES.join(', ')}. It was '#{execution_mode}' which is not valid." unless execution_mode.in?(EXECUTION_MODES)
|
|
@@ -204,9 +206,10 @@ module GoodJob
|
|
|
204
206
|
|
|
205
207
|
def cron
|
|
206
208
|
env_cron = JSON.parse(ENV.fetch('GOOD_JOB_CRON'), symbolize_names: true) if ENV['GOOD_JOB_CRON'].present?
|
|
209
|
+
rails_config_cron = rails_config[:cron].presence
|
|
207
210
|
|
|
208
211
|
options[:cron] ||
|
|
209
|
-
|
|
212
|
+
rails_config_cron ||
|
|
210
213
|
env_cron ||
|
|
211
214
|
{}
|
|
212
215
|
end
|
|
@@ -380,6 +383,12 @@ module GoodJob
|
|
|
380
383
|
rails_config[:dashboard_default_locale] || DEFAULT_DASHBOARD_DEFAULT_LOCALE
|
|
381
384
|
end
|
|
382
385
|
|
|
386
|
+
def dashboard_live_poll_enabled
|
|
387
|
+
return rails_config[:dashboard_live_poll_enabled] unless rails_config[:dashboard_live_poll_enabled].nil?
|
|
388
|
+
|
|
389
|
+
DEFAULT_DASHBOARD_LIVE_POLL_ENABLED
|
|
390
|
+
end
|
|
391
|
+
|
|
383
392
|
# Whether running in a web server process.
|
|
384
393
|
# @return [Boolean, nil]
|
|
385
394
|
def in_webserver?
|
|
@@ -16,6 +16,7 @@ module GoodJob
|
|
|
16
16
|
execution
|
|
17
17
|
execution_interrupted
|
|
18
18
|
execution_retried
|
|
19
|
+
retry_now
|
|
19
20
|
].freeze
|
|
20
21
|
|
|
21
22
|
# @!attribute [rw] cron_at
|
|
@@ -66,6 +67,12 @@ module GoodJob
|
|
|
66
67
|
# @return [Boolean, nil]
|
|
67
68
|
thread_mattr_accessor :execution_retried
|
|
68
69
|
|
|
70
|
+
# @!attribute [rw] retry_now
|
|
71
|
+
# @!scope class
|
|
72
|
+
# Execution Retried
|
|
73
|
+
# @return [Boolean, nil]
|
|
74
|
+
thread_mattr_accessor :retry_now
|
|
75
|
+
|
|
69
76
|
# Resets attributes
|
|
70
77
|
# @param [Hash] values to assign
|
|
71
78
|
# @return [void]
|
data/lib/good_job/daemon.rb
CHANGED
|
@@ -17,6 +17,7 @@ module GoodJob
|
|
|
17
17
|
# Daemonizes the current process and writes out a pidfile.
|
|
18
18
|
# @return [void]
|
|
19
19
|
def daemonize
|
|
20
|
+
check_pid_dir
|
|
20
21
|
check_pid
|
|
21
22
|
::Process.daemon
|
|
22
23
|
write_pid
|
|
@@ -38,6 +39,14 @@ module GoodJob
|
|
|
38
39
|
File.delete(pidfile) if File.exist?(pidfile) # rubocop:disable Lint/NonAtomicFileOperation
|
|
39
40
|
end
|
|
40
41
|
|
|
42
|
+
# @return [void]
|
|
43
|
+
def check_pid_dir
|
|
44
|
+
dirname = File.dirname(pidfile)
|
|
45
|
+
return if Dir.exist?(dirname)
|
|
46
|
+
|
|
47
|
+
abort "Pidfile directory \"#{dirname}\" doesn't exist. Aborting..."
|
|
48
|
+
end
|
|
49
|
+
|
|
41
50
|
# @return [void]
|
|
42
51
|
def check_pid
|
|
43
52
|
case pid_status(pidfile)
|
|
@@ -14,7 +14,7 @@ module GoodJob # :nodoc:
|
|
|
14
14
|
|
|
15
15
|
# Registers the current process.
|
|
16
16
|
def register_process
|
|
17
|
-
GoodJob::Process.
|
|
17
|
+
GoodJob::Process.override_connection(connection) do
|
|
18
18
|
GoodJob::Process.cleanup
|
|
19
19
|
@process = GoodJob::Process.register
|
|
20
20
|
end
|
|
@@ -22,7 +22,7 @@ module GoodJob # :nodoc:
|
|
|
22
22
|
|
|
23
23
|
def refresh_process
|
|
24
24
|
Rails.application.executor.wrap do
|
|
25
|
-
GoodJob::Process.
|
|
25
|
+
GoodJob::Process.override_connection(connection) do
|
|
26
26
|
GoodJob::Process.with_logger_silenced do
|
|
27
27
|
@process&.refresh_if_stale(cleanup: true)
|
|
28
28
|
end
|
|
@@ -32,7 +32,7 @@ module GoodJob # :nodoc:
|
|
|
32
32
|
|
|
33
33
|
# Deregisters the current process.
|
|
34
34
|
def deregister_process
|
|
35
|
-
GoodJob::Process.
|
|
35
|
+
GoodJob::Process.override_connection(connection) do
|
|
36
36
|
@process&.deregister
|
|
37
37
|
end
|
|
38
38
|
end
|
|
@@ -3,36 +3,29 @@
|
|
|
3
3
|
module GoodJob # :nodoc:
|
|
4
4
|
# Extends an ActiveRecord odel to override the connection and use
|
|
5
5
|
# an explicit connection that has been removed from the pool.
|
|
6
|
-
module
|
|
6
|
+
module OverridableConnection
|
|
7
7
|
extend ActiveSupport::Concern
|
|
8
8
|
|
|
9
9
|
included do
|
|
10
|
-
thread_cattr_accessor :
|
|
10
|
+
thread_cattr_accessor :_overridden_connection
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
class_methods do
|
|
14
|
-
# Assigns a connection to the model.
|
|
15
|
-
# @param conn [ActiveRecord::ConnectionAdapters::AbstractAdapter]
|
|
16
|
-
# @return [void]
|
|
17
|
-
def connection=(conn)
|
|
18
|
-
self._connection = conn
|
|
19
|
-
end
|
|
20
|
-
|
|
21
14
|
# Overrides the existing connection method to use the assigned connection
|
|
22
15
|
# @return [ActiveRecord::ConnectionAdapters::AbstractAdapter]
|
|
23
16
|
def connection
|
|
24
|
-
|
|
17
|
+
_overridden_connection || super
|
|
25
18
|
end
|
|
26
19
|
|
|
27
20
|
# Block interface to assign the connection, yield, then unassign the connection.
|
|
28
21
|
# @param conn [ActiveRecord::ConnectionAdapters::AbstractAdapter]
|
|
29
22
|
# @return [void]
|
|
30
|
-
def
|
|
31
|
-
original_conn =
|
|
32
|
-
self.
|
|
23
|
+
def override_connection(conn)
|
|
24
|
+
original_conn = _overridden_connection
|
|
25
|
+
self._overridden_connection = conn
|
|
33
26
|
yield
|
|
34
27
|
ensure
|
|
35
|
-
self.
|
|
28
|
+
self._overridden_connection = original_conn
|
|
36
29
|
end
|
|
37
30
|
end
|
|
38
31
|
end
|
data/lib/good_job/version.rb
CHANGED
data/lib/good_job.rb
CHANGED
|
@@ -15,7 +15,7 @@ require "good_job/active_job_extensions/interrupt_errors"
|
|
|
15
15
|
require "good_job/active_job_extensions/labels"
|
|
16
16
|
require "good_job/active_job_extensions/notify_options"
|
|
17
17
|
|
|
18
|
-
require "good_job/
|
|
18
|
+
require "good_job/overridable_connection"
|
|
19
19
|
require "good_job/bulk"
|
|
20
20
|
require "good_job/callable"
|
|
21
21
|
require "good_job/capsule"
|
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: 3.
|
|
4
|
+
version: 3.25.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: 2024-02-
|
|
11
|
+
date: 2024-02-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activejob
|
|
@@ -288,6 +288,7 @@ files:
|
|
|
288
288
|
- app/frontend/good_job/vendor/rails_ujs.js
|
|
289
289
|
- app/frontend/good_job/vendor/stimulus.js
|
|
290
290
|
- app/helpers/good_job/application_helper.rb
|
|
291
|
+
- app/helpers/good_job/icons_helper.rb
|
|
291
292
|
- app/models/concerns/good_job/advisory_lockable.rb
|
|
292
293
|
- app/models/concerns/good_job/error_events.rb
|
|
293
294
|
- app/models/concerns/good_job/filterable.rb
|
|
@@ -305,6 +306,8 @@ files:
|
|
|
305
306
|
- app/models/good_job/job.rb
|
|
306
307
|
- app/models/good_job/process.rb
|
|
307
308
|
- app/models/good_job/setting.rb
|
|
309
|
+
- app/views/good_job/_custom_execution_details.html.erb
|
|
310
|
+
- app/views/good_job/_custom_job_details.html.erb
|
|
308
311
|
- app/views/good_job/batches/_jobs.erb
|
|
309
312
|
- app/views/good_job/batches/_table.erb
|
|
310
313
|
- app/views/good_job/batches/index.html.erb
|
|
@@ -375,7 +378,6 @@ files:
|
|
|
375
378
|
- lib/good_job/active_job_extensions/labels.rb
|
|
376
379
|
- lib/good_job/active_job_extensions/notify_options.rb
|
|
377
380
|
- lib/good_job/adapter.rb
|
|
378
|
-
- lib/good_job/assignable_connection.rb
|
|
379
381
|
- lib/good_job/bulk.rb
|
|
380
382
|
- lib/good_job/callable.rb
|
|
381
383
|
- lib/good_job/capsule.rb
|
|
@@ -394,6 +396,7 @@ files:
|
|
|
394
396
|
- lib/good_job/multi_scheduler.rb
|
|
395
397
|
- lib/good_job/notifier.rb
|
|
396
398
|
- lib/good_job/notifier/process_heartbeat.rb
|
|
399
|
+
- lib/good_job/overridable_connection.rb
|
|
397
400
|
- lib/good_job/poller.rb
|
|
398
401
|
- lib/good_job/probe_server.rb
|
|
399
402
|
- lib/good_job/probe_server/healthcheck_middleware.rb
|