good_job 3.23.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 +50 -0
- data/README.md +55 -5
- 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/batch_record.rb +1 -1
- 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/config/locales/pt-BR.yml +243 -0
- data/lib/good_job/active_job_extensions/concurrency.rb +8 -0
- 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 +8 -4
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,55 @@
|
|
|
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
|
+
|
|
31
|
+
## [v3.24.0](https://github.com/bensheldon/good_job/tree/v3.24.0) (2024-02-12)
|
|
32
|
+
|
|
33
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v3.23.0...v3.24.0)
|
|
34
|
+
|
|
35
|
+
**Fixed bugs:**
|
|
36
|
+
|
|
37
|
+
- Fix batches so that retried-and-successful jobs leave the batch succeeded [\#1243](https://github.com/bensheldon/good_job/pull/1243) ([bensheldon](https://github.com/bensheldon))
|
|
38
|
+
- Use the job class as the default concurrency key if none is provided [\#1145](https://github.com/bensheldon/good_job/pull/1145) ([Earlopain](https://github.com/Earlopain))
|
|
39
|
+
|
|
40
|
+
**Closed issues:**
|
|
41
|
+
|
|
42
|
+
- Batch callbacks not run when job fails, then succeeds [\#1239](https://github.com/bensheldon/good_job/issues/1239)
|
|
43
|
+
- Broken pipe @ io\_writev - \<STDERR\> \(Errno::EPIPE\) [\#1233](https://github.com/bensheldon/good_job/issues/1233)
|
|
44
|
+
- PG::UniqueViolation unique constraint "index\_good\_jobs\_on\_cron\_key\_and\_cron\_at\_cond" [\#1230](https://github.com/bensheldon/good_job/issues/1230)
|
|
45
|
+
- Default concurrency key [\#1110](https://github.com/bensheldon/good_job/issues/1110)
|
|
46
|
+
|
|
47
|
+
**Merged pull requests:**
|
|
48
|
+
|
|
49
|
+
- Use Ruby 3.3 for development; add Bootsnap; update to Rails 7.1.3 [\#1240](https://github.com/bensheldon/good_job/pull/1240) ([bensheldon](https://github.com/bensheldon))
|
|
50
|
+
- Tweak docs for new concurrency default [\#1229](https://github.com/bensheldon/good_job/pull/1229) ([Earlopain](https://github.com/Earlopain))
|
|
51
|
+
- Brazilian Portuguese locale [\#1226](https://github.com/bensheldon/good_job/pull/1226) ([hss-mateus](https://github.com/hss-mateus))
|
|
52
|
+
|
|
3
53
|
## [v3.23.0](https://github.com/bensheldon/good_job/tree/v3.23.0) (2024-01-23)
|
|
4
54
|
|
|
5
55
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v3.22.0...v3.23.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`.
|
|
@@ -511,12 +548,25 @@ class MyJob < ApplicationJob
|
|
|
511
548
|
|
|
512
549
|
# A unique key to be globally locked against.
|
|
513
550
|
# Can be String or Lambda/Proc that is invoked in the context of the job.
|
|
551
|
+
#
|
|
552
|
+
# If a key is not provided GoodJob will use the job class name.
|
|
553
|
+
#
|
|
554
|
+
# To disable concurrency control, for example in a subclass, set the
|
|
555
|
+
# key explicitly to nil (e.g. `key: nil` or `key: -> { nil }`)
|
|
556
|
+
#
|
|
557
|
+
# If you provide a custom concurrency key (for example, if concurrency is supposed
|
|
558
|
+
# to be controlled by the first job argument) make sure that it is sufficiently unique across
|
|
559
|
+
# jobs and queues by adding the job class or queue to the key yourself, if needed.
|
|
560
|
+
#
|
|
561
|
+
# Note: When using a model instance as part of your custom concurrency key, make sure
|
|
562
|
+
# to explicitly use its `id` or `to_global_id` because otherwise it will not stringify as expected.
|
|
563
|
+
#
|
|
514
564
|
# Note: Arguments passed to #perform_later can be accessed through Active Job's `arguments` method
|
|
515
565
|
# which is an array containing positional arguments and, optionally, a kwarg hash.
|
|
516
|
-
key: -> { "
|
|
566
|
+
key: -> { "#{self.class.name}-#{queue_name}-#{arguments.first}-#{arguments.last[:version]}" } # MyJob.perform_later("Alice", version: 'v2') => "MyJob-default-Alice-v2"
|
|
517
567
|
)
|
|
518
568
|
|
|
519
|
-
def perform(first_name)
|
|
569
|
+
def perform(first_name, version:)
|
|
520
570
|
# do work
|
|
521
571
|
end
|
|
522
572
|
end
|
|
@@ -525,8 +575,8 @@ end
|
|
|
525
575
|
When testing, the resulting concurrency key value can be inspected:
|
|
526
576
|
|
|
527
577
|
```ruby
|
|
528
|
-
job = MyJob.perform_later("Alice")
|
|
529
|
-
job.good_job_concurrency_key #=> "MyJob-Alice"
|
|
578
|
+
job = MyJob.perform_later("Alice", version: 'v1')
|
|
579
|
+
job.good_job_concurrency_key #=> "MyJob-default-Alice-v1"
|
|
530
580
|
```
|
|
531
581
|
|
|
532
582
|
#### How concurrency controls work
|
|
@@ -596,7 +646,7 @@ end
|
|
|
596
646
|
active_jobs.all?(&:provider_job_id)
|
|
597
647
|
|
|
598
648
|
# Bulk enqueue Active Job instances directly without using `.perform_later`:
|
|
599
|
-
GoodJob::Bulk.enqueue(MyJob.new, AnotherJob.new)
|
|
649
|
+
GoodJob::Bulk.enqueue([MyJob.new, AnotherJob.new])
|
|
600
650
|
```
|
|
601
651
|
|
|
602
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
|
|
@@ -46,7 +46,7 @@ module GoodJob
|
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
def _continue_discard_or_finish(execution = nil, lock: true)
|
|
49
|
-
execution_discarded = execution && execution.error.present? && execution.retried_good_job_id.nil?
|
|
49
|
+
execution_discarded = execution && execution.error.present? && execution.finished_at && execution.retried_good_job_id.nil?
|
|
50
50
|
take_advisory_lock(lock) do
|
|
51
51
|
Batch.within_thread(batch_id: nil, batch_callback_id: id) do
|
|
52
52
|
reload
|
|
@@ -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>
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
---
|
|
2
|
+
pt-BR:
|
|
3
|
+
good_job:
|
|
4
|
+
actions:
|
|
5
|
+
destroy: Excluir
|
|
6
|
+
discard: Descartar
|
|
7
|
+
force_discard: Forçar descarte
|
|
8
|
+
inspect: Inspecionar
|
|
9
|
+
reschedule: Reagendar
|
|
10
|
+
retry: Tentar novamente
|
|
11
|
+
batches:
|
|
12
|
+
index:
|
|
13
|
+
older_batches: Lotes antigos
|
|
14
|
+
pending_migrations: O GoodJob tem migrações pendentes no banco de dados.
|
|
15
|
+
title: Lotes
|
|
16
|
+
jobs:
|
|
17
|
+
actions:
|
|
18
|
+
confirm_destroy: Tem certeza de que deseja excluir esta tarefa?
|
|
19
|
+
confirm_discard: Tem certeza de que deseja descartar esta tarefa?
|
|
20
|
+
confirm_reschedule: Tem certeza de que deseja reagendar esta tarefa?
|
|
21
|
+
confirm_retry: Tem certeza de que deseja tentar novamente esta tarefa?
|
|
22
|
+
destroy: Excluir Tarefa
|
|
23
|
+
discard: Descartar Tarefa
|
|
24
|
+
reschedule: Reagendar Tarefa
|
|
25
|
+
retry: Tentar Tarefa Novamente
|
|
26
|
+
title: Ações
|
|
27
|
+
no_jobs_found: Nenhuma tarefa encontrada.
|
|
28
|
+
show:
|
|
29
|
+
attributes: Atributos
|
|
30
|
+
batched_jobs: Tarefas em Lote
|
|
31
|
+
callback_jobs: Tarefas de Callback
|
|
32
|
+
table:
|
|
33
|
+
no_batches_found: Nenhum lote encontrado.
|
|
34
|
+
cron_entries:
|
|
35
|
+
actions:
|
|
36
|
+
confirm_disable: Tem certeza de que deseja desativar esta tarefa programada?
|
|
37
|
+
confirm_enable: Tem certeza de que deseja ativar esta tarefa programada?
|
|
38
|
+
confirm_enqueue: Tem certeza de que deseja enfileirar esta tarefa programada agora?
|
|
39
|
+
disable: Desativar tarefa programada
|
|
40
|
+
enable: Ativar tarefa programada
|
|
41
|
+
enqueue: Enfileirar tarefa programada agora
|
|
42
|
+
disable:
|
|
43
|
+
notice: A tarefa programada foi desativada.
|
|
44
|
+
enable:
|
|
45
|
+
notice: A tarefa programada foi ativada.
|
|
46
|
+
enqueue:
|
|
47
|
+
notice: A tarefa programada foi enfileirada.
|
|
48
|
+
index:
|
|
49
|
+
no_cron_schedules_found: Nenhuma tarefa programada encontrada.
|
|
50
|
+
title: Tarefas Programadas
|
|
51
|
+
pending_migrations: Requer migração pendente do GoodJob no banco de dados.
|
|
52
|
+
show:
|
|
53
|
+
cron_entry_key: Chave da Tarefa Programada
|
|
54
|
+
datetime:
|
|
55
|
+
distance_in_words:
|
|
56
|
+
about_x_hours:
|
|
57
|
+
one: cerca de 1 hora
|
|
58
|
+
other: cerca de %{count} horas
|
|
59
|
+
about_x_months:
|
|
60
|
+
one: cerca de 1 mês
|
|
61
|
+
other: cerca de %{count} meses
|
|
62
|
+
about_x_years:
|
|
63
|
+
one: cerca de 1 ano
|
|
64
|
+
other: cerca de %{count} anos
|
|
65
|
+
almost_x_years:
|
|
66
|
+
one: quase 1 ano
|
|
67
|
+
other: quase %{count} anos
|
|
68
|
+
half_a_minute: meio minuto
|
|
69
|
+
less_than_x_minutes:
|
|
70
|
+
one: menos de um minuto
|
|
71
|
+
other: menos de %{count} minutos
|
|
72
|
+
less_than_x_seconds:
|
|
73
|
+
one: menos de 1 segundo
|
|
74
|
+
other: menos de %{count} segundos
|
|
75
|
+
over_x_years:
|
|
76
|
+
one: mais de 1 ano
|
|
77
|
+
other: mais de %{count} anos
|
|
78
|
+
x_days:
|
|
79
|
+
one: 1 dia
|
|
80
|
+
other: "%{count} dias"
|
|
81
|
+
x_minutes:
|
|
82
|
+
one: 1 minuto
|
|
83
|
+
other: "%{count} minutos"
|
|
84
|
+
x_months:
|
|
85
|
+
one: 1 mês
|
|
86
|
+
other: "%{count} meses"
|
|
87
|
+
x_seconds:
|
|
88
|
+
one: 1 segundo
|
|
89
|
+
other: "%{count} segundos"
|
|
90
|
+
x_years:
|
|
91
|
+
one: 1 ano
|
|
92
|
+
other: "%{count} anos"
|
|
93
|
+
duration:
|
|
94
|
+
hours: "%{hour}h %{min}m"
|
|
95
|
+
less_than_10_seconds: "%{sec}s"
|
|
96
|
+
milliseconds: "%{ms}ms"
|
|
97
|
+
minutes: "%{min}m %{sec}s"
|
|
98
|
+
seconds: "%{sec}s"
|
|
99
|
+
error_event:
|
|
100
|
+
discarded: Descartado
|
|
101
|
+
handled: Tratado
|
|
102
|
+
interrupted: Interrompido
|
|
103
|
+
retried: Tentado novamente
|
|
104
|
+
retry_stopped: Tentativa de reexecução interrompida
|
|
105
|
+
unhandled: Não tratado
|
|
106
|
+
helpers:
|
|
107
|
+
relative_time:
|
|
108
|
+
future: em %{time}
|
|
109
|
+
past: "%{time} atrás"
|
|
110
|
+
jobs:
|
|
111
|
+
actions:
|
|
112
|
+
confirm_destroy: Tem certeza de que deseja excluir a tarefa?
|
|
113
|
+
confirm_discard: Tem certeza de que deseja descartar a tarefa?
|
|
114
|
+
confirm_force_discard: 'Tem certeza de que deseja forçar o descarte desta tarefa? A tarefa será marcada como descartada, mas a tarefa em execução não será interrompida - no entanto, ela não será tentada novamente em falhas.
|
|
115
|
+
|
|
116
|
+
'
|
|
117
|
+
confirm_reschedule: Tem certeza de que deseja reagendar a tarefa?
|
|
118
|
+
confirm_retry: Tem certeza de que deseja tentar novamente a tarefa?
|
|
119
|
+
destroy: Excluir tarefa
|
|
120
|
+
discard: Descartar tarefa
|
|
121
|
+
force_discard: Forçar descarte de tarefa
|
|
122
|
+
reschedule: Reagendar tarefa
|
|
123
|
+
retry: Tentar tarefa novamente
|
|
124
|
+
destroy:
|
|
125
|
+
notice: A tarefa foi excluida.
|
|
126
|
+
discard:
|
|
127
|
+
notice: A tarefa foi descartada.
|
|
128
|
+
executions:
|
|
129
|
+
in_queue: na fila
|
|
130
|
+
runtime: tempo de execução
|
|
131
|
+
title: Execuções
|
|
132
|
+
force_discard:
|
|
133
|
+
notice: A tarefa foi forçadamente descartada. Ela continuará a ser executada, mas não será tentada novamente em caso de falhas.
|
|
134
|
+
index:
|
|
135
|
+
job_pagination: Paginação de Tarefa
|
|
136
|
+
older_jobs: Tarefas antigas
|
|
137
|
+
reschedule:
|
|
138
|
+
notice: A tarefa foi reagendada.
|
|
139
|
+
retry:
|
|
140
|
+
notice: A tarefa foi tentada novamente.
|
|
141
|
+
show:
|
|
142
|
+
jobs: Tarefas
|
|
143
|
+
table:
|
|
144
|
+
actions:
|
|
145
|
+
apply_to_all:
|
|
146
|
+
one: Aplicar a todas 1 tarefa.
|
|
147
|
+
other: Aplicar a todas %{count} tarefas.
|
|
148
|
+
confirm_destroy_all: Tem certeza de que deseja excluir as tarefas selecionadas?
|
|
149
|
+
confirm_discard_all: Tem certeza de que deseja descartar as tarefas selecionadas?
|
|
150
|
+
confirm_reschedule_all: Tem certeza de que deseja reagendar as tarefas selecionadas?
|
|
151
|
+
confirm_retry_all: Tem certeza de que deseja tentar novamente as tarefas selecionadas?
|
|
152
|
+
destroy_all: Excluir tudo
|
|
153
|
+
discard_all: Descartar tudo
|
|
154
|
+
reschedule_all: Reagendar tudo
|
|
155
|
+
retry_all: Tentar novamente tudo
|
|
156
|
+
title: Ações
|
|
157
|
+
no_jobs_found: Nenhuma tarefa encontrada.
|
|
158
|
+
toggle_actions: Alternar Ações
|
|
159
|
+
toggle_all_jobs: Alternar todas as tarefas
|
|
160
|
+
models:
|
|
161
|
+
batch:
|
|
162
|
+
created: Criado
|
|
163
|
+
created_at: Criado em
|
|
164
|
+
discarded: Descartado
|
|
165
|
+
discarded_at: Descartado em
|
|
166
|
+
enqueued: Enfileirado
|
|
167
|
+
enqueued_at: Enfileirado em
|
|
168
|
+
finished: Concluído
|
|
169
|
+
finished_at: Concluído em
|
|
170
|
+
jobs: Tarefas
|
|
171
|
+
name: Nome
|
|
172
|
+
cron:
|
|
173
|
+
class: Classe
|
|
174
|
+
last_run: Última execução
|
|
175
|
+
next_scheduled: Próximo agendamento
|
|
176
|
+
schedule: Agendamento
|
|
177
|
+
job:
|
|
178
|
+
arguments: Argumentos
|
|
179
|
+
attempts: Tentativas
|
|
180
|
+
priority: Prioridade
|
|
181
|
+
queue: Fila
|
|
182
|
+
number:
|
|
183
|
+
format:
|
|
184
|
+
delimiter: "."
|
|
185
|
+
separator: ","
|
|
186
|
+
human:
|
|
187
|
+
decimal_units:
|
|
188
|
+
delimiter: "."
|
|
189
|
+
format: "%n%u"
|
|
190
|
+
precision: 3
|
|
191
|
+
separator: ","
|
|
192
|
+
units:
|
|
193
|
+
billion: B
|
|
194
|
+
million: M
|
|
195
|
+
quadrillion: Q
|
|
196
|
+
thousand: K
|
|
197
|
+
trillion: T
|
|
198
|
+
unit: ''
|
|
199
|
+
processes:
|
|
200
|
+
index:
|
|
201
|
+
cron_enabled: Agendamento ativado
|
|
202
|
+
no_good_job_processes_found: Nenhum processo do GoodJob encontrado.
|
|
203
|
+
process: Processo
|
|
204
|
+
schedulers: Agendadores
|
|
205
|
+
started: Iniciado
|
|
206
|
+
title: Processos
|
|
207
|
+
updated: Atualizado
|
|
208
|
+
shared:
|
|
209
|
+
boolean:
|
|
210
|
+
'false': Não
|
|
211
|
+
'true': Sim
|
|
212
|
+
error: Erro
|
|
213
|
+
filter:
|
|
214
|
+
all: Tudo
|
|
215
|
+
all_jobs: Todas as tarefas
|
|
216
|
+
all_queues: Todas as filas
|
|
217
|
+
clear: Limpar
|
|
218
|
+
job_name: Nome da Tarefa
|
|
219
|
+
placeholder: Pesquisar por classe, ID da tarefa, parâmetros da tarefa e texto de erro.
|
|
220
|
+
queue_name: Nome da Fila
|
|
221
|
+
search: Pesquisar
|
|
222
|
+
navbar:
|
|
223
|
+
batches: Lotes
|
|
224
|
+
cron_schedules: Agendamentos
|
|
225
|
+
jobs: Tarefas
|
|
226
|
+
live_poll: Acompanhamento ao Vivo
|
|
227
|
+
name: "GoodJob 👍"
|
|
228
|
+
processes: Processos
|
|
229
|
+
theme:
|
|
230
|
+
auto: Automático
|
|
231
|
+
dark: Escuro
|
|
232
|
+
light: Claro
|
|
233
|
+
theme: Tema
|
|
234
|
+
secondary_navbar:
|
|
235
|
+
inspiration: Lembre-se, você também está fazendo uma Boa Tarefa!
|
|
236
|
+
last_updated: Última atualização
|
|
237
|
+
status:
|
|
238
|
+
discarded: Descartado
|
|
239
|
+
queued: Enfileirado
|
|
240
|
+
retried: Tentado novamente
|
|
241
|
+
running: Em execução
|
|
242
|
+
scheduled: Agendado
|
|
243
|
+
succeeded: Concluído com sucesso
|
|
@@ -96,6 +96,8 @@ module GoodJob
|
|
|
96
96
|
# Generates the concurrency key from the configuration
|
|
97
97
|
# @return [Object] concurrency key
|
|
98
98
|
def _good_job_concurrency_key
|
|
99
|
+
return _good_job_default_concurrency_key unless self.class.good_job_concurrency_config.key?(:key)
|
|
100
|
+
|
|
99
101
|
key = self.class.good_job_concurrency_config[:key]
|
|
100
102
|
return if key.blank?
|
|
101
103
|
|
|
@@ -105,6 +107,12 @@ module GoodJob
|
|
|
105
107
|
key
|
|
106
108
|
end
|
|
107
109
|
|
|
110
|
+
# Generates the default concurrency key when the configuration doesn't provide one
|
|
111
|
+
# @return [String] concurrency key
|
|
112
|
+
def _good_job_default_concurrency_key
|
|
113
|
+
self.class.name.to_s
|
|
114
|
+
end
|
|
115
|
+
|
|
108
116
|
private
|
|
109
117
|
|
|
110
118
|
def good_job_enqueue_concurrency_check(job, on_abort:, on_enqueue:)
|
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-
|
|
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
|
|
@@ -347,6 +350,7 @@ files:
|
|
|
347
350
|
- config/locales/ja.yml
|
|
348
351
|
- config/locales/ko.yml
|
|
349
352
|
- config/locales/nl.yml
|
|
353
|
+
- config/locales/pt-BR.yml
|
|
350
354
|
- config/locales/ru.yml
|
|
351
355
|
- config/locales/tr.yml
|
|
352
356
|
- config/locales/uk.yml
|
|
@@ -374,7 +378,6 @@ files:
|
|
|
374
378
|
- lib/good_job/active_job_extensions/labels.rb
|
|
375
379
|
- lib/good_job/active_job_extensions/notify_options.rb
|
|
376
380
|
- lib/good_job/adapter.rb
|
|
377
|
-
- lib/good_job/assignable_connection.rb
|
|
378
381
|
- lib/good_job/bulk.rb
|
|
379
382
|
- lib/good_job/callable.rb
|
|
380
383
|
- lib/good_job/capsule.rb
|
|
@@ -393,6 +396,7 @@ files:
|
|
|
393
396
|
- lib/good_job/multi_scheduler.rb
|
|
394
397
|
- lib/good_job/notifier.rb
|
|
395
398
|
- lib/good_job/notifier/process_heartbeat.rb
|
|
399
|
+
- lib/good_job/overridable_connection.rb
|
|
396
400
|
- lib/good_job/poller.rb
|
|
397
401
|
- lib/good_job/probe_server.rb
|
|
398
402
|
- lib/good_job/probe_server/healthcheck_middleware.rb
|
|
@@ -436,7 +440,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
436
440
|
- !ruby/object:Gem::Version
|
|
437
441
|
version: '0'
|
|
438
442
|
requirements: []
|
|
439
|
-
rubygems_version: 3.
|
|
443
|
+
rubygems_version: 3.5.3
|
|
440
444
|
signing_key:
|
|
441
445
|
specification_version: 4
|
|
442
446
|
summary: A multithreaded, Postgres-based ActiveJob backend for Ruby on Rails
|