good_job 3.21.5 → 3.22.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fa13b9d970c63760bc8264906c0aeccf1f71f0b6e678338cca21778956fdba0a
4
- data.tar.gz: cd4d0e5be595b266396a2ffa0d9da0955940f1afe7639a2456b9a8f67e9d205b
3
+ metadata.gz: 699ef4a7d62e1eb7c96644525c9e2239913fb5d23ceafacb13be172e96720532
4
+ data.tar.gz: dcfe3389202698fa321d4c10349758766db20811feafb10024373d54c94dcd2e
5
5
  SHA512:
6
- metadata.gz: '0871dcec35bfe62dbc59f07e81aa048cd04300316e859080a7f61f1666e61a1641a52ce76e6d827d2bf6e7524dd3893cacda66d94fb7ec994d88ae2e7b616ead'
7
- data.tar.gz: 7171bf22d85558b2f48bbecc80fbe5a57cea331be996b6324e266bc1e8a1da9a3a13f9ab191d5dac167797be5e2b64c0a7bb9ebb05e4937b2946de9bafc17193
6
+ metadata.gz: 77215885e8d798ba791814bb64f07f2c70d54fa0dd3bb9bbc476dc0464d4c150089bf466e4ed16b648425f78eb9b7e86f0b1259f19e166633f60619347ea3281
7
+ data.tar.gz: 8f1b9a3e16080e331581efbe48bea5060666c4eda460737e1a34508ee6d278abc1fea91e68d486759f39a082b1969c87f84e22a620d7598bc4f9b93d69fce7d8
data/CHANGELOG.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # Changelog
2
2
 
3
+ ## [v3.22.0](https://github.com/bensheldon/good_job/tree/v3.22.0) (2024-01-03)
4
+
5
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.21.5...v3.22.0)
6
+
7
+ **Implemented enhancements:**
8
+
9
+ - Add "updated at" info in secondary top navbar [\#1204](https://github.com/bensheldon/good_job/pull/1204) ([sparshalc](https://github.com/sparshalc))
10
+ - Re-add footer with GoodJob version number [\#1201](https://github.com/bensheldon/good_job/pull/1201) ([Pauloparakleto](https://github.com/Pauloparakleto))
11
+ - Add Active Job extension for Labels [\#1188](https://github.com/bensheldon/good_job/pull/1188) ([bensheldon](https://github.com/bensheldon))
12
+
13
+ **Closed issues:**
14
+
15
+ - Limiting by executed jobs count by period [\#1198](https://github.com/bensheldon/good_job/issues/1198)
16
+ - Cron jobs processing in async mode [\#1196](https://github.com/bensheldon/good_job/issues/1196)
17
+ - Dashboard Missing Translation? [\#1192](https://github.com/bensheldon/good_job/issues/1192)
18
+ - Show last update on top nav bar [\#1183](https://github.com/bensheldon/good_job/issues/1183)
19
+ - Wrong quoting of the query to spot new jobs? [\#1179](https://github.com/bensheldon/good_job/issues/1179)
20
+ - \[Possible bug\] good\_job does not honour the wait parameter on retry\_on [\#1174](https://github.com/bensheldon/good_job/issues/1174)
21
+ - Running GoodJob in production with systemd throws an error due to a wrong communication with WatchDog [\#1172](https://github.com/bensheldon/good_job/issues/1172)
22
+
23
+ **Merged pull requests:**
24
+
25
+ - Bump the bundler-dependencies group with 3 updates [\#1202](https://github.com/bensheldon/good_job/pull/1202) ([dependabot[bot]](https://github.com/apps/dependabot))
26
+ - Remove duplicated intro sentence in README [\#1195](https://github.com/bensheldon/good_job/pull/1195) ([benoittgt](https://github.com/benoittgt))
27
+ - Fix namespace for `InterruptError` in README [\#1193](https://github.com/bensheldon/good_job/pull/1193) ([padde](https://github.com/padde))
28
+ - Bump github/codeql-action from 2 to 3 [\#1191](https://github.com/bensheldon/good_job/pull/1191) ([dependabot[bot]](https://github.com/apps/dependabot))
29
+ - Bump actions/upload-artifact from 3 to 4 [\#1190](https://github.com/bensheldon/good_job/pull/1190) ([dependabot[bot]](https://github.com/apps/dependabot))
30
+ - Lock RubyGems version for Ruby \< 3.0 in CI [\#1189](https://github.com/bensheldon/good_job/pull/1189) ([bensheldon](https://github.com/bensheldon))
31
+ - Active Record and Active Job name formatting [\#1182](https://github.com/bensheldon/good_job/pull/1182) ([andyatkinson](https://github.com/andyatkinson))
32
+ - Remove redundant `good_jobs.active_job_id` index [\#1181](https://github.com/bensheldon/good_job/pull/1181) ([andyatkinson](https://github.com/andyatkinson))
33
+ - Add missing word in readme [\#1177](https://github.com/bensheldon/good_job/pull/1177) ([Earlopain](https://github.com/Earlopain))
34
+
3
35
  ## [v3.21.5](https://github.com/bensheldon/good_job/tree/v3.21.5) (2023-12-12)
4
36
 
5
37
  [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.21.4...v3.21.5)
data/README.md CHANGED
@@ -4,11 +4,11 @@
4
4
  [![Test Status](https://github.com/bensheldon/good_job/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/bensheldon/good_job/actions/workflows/test.yml?query=branch%3Amain)
5
5
  [![Ruby Toolbox](https://img.shields.io/badge/dynamic/json?color=blue&label=Ruby%20Toolbox&query=%24.projects%5B0%5D.score&url=https%3A%2F%2Fwww.ruby-toolbox.com%2Fapi%2Fprojects%2Fcompare%2Fgood_job&logo=data:image/svg+xml;base64,PHN2ZyBhcmlhLWhpZGRlbj0idHJ1ZSIgZm9jdXNhYmxlPSJmYWxzZSIgZGF0YS1wcmVmaXg9ImZhcyIgZGF0YS1pY29uPSJmbGFzayIgY2xhc3M9InN2Zy1pbmxpbmUtLWZhIGZhLWZsYXNrIGZhLXctMTQiIHJvbGU9ImltZyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgNDQ4IDUxMiI+PHBhdGggZmlsbD0id2hpdGUiIGQ9Ik00MzcuMiA0MDMuNUwzMjAgMjE1VjY0aDhjMTMuMyAwIDI0LTEwLjcgMjQtMjRWMjRjMC0xMy4zLTEwLjctMjQtMjQtMjRIMTIwYy0xMy4zIDAtMjQgMTAuNy0yNCAyNHYxNmMwIDEzLjMgMTAuNyAyNCAyNCAyNGg4djE1MUwxMC44IDQwMy41Qy0xOC41IDQ1MC42IDE1LjMgNTEyIDcwLjkgNTEyaDMwNi4yYzU1LjcgMCA4OS40LTYxLjUgNjAuMS0xMDguNXpNMTM3LjkgMzIwbDQ4LjItNzcuNmMzLjctNS4yIDUuOC0xMS42IDUuOC0xOC40VjY0aDY0djE2MGMwIDYuOSAyLjIgMTMuMiA1LjggMTguNGw0OC4yIDc3LjZoLTE3MnoiPjwvcGF0aD48L3N2Zz4=)](https://www.ruby-toolbox.com/projects/good_job)
6
6
 
7
- GoodJob is a multithreaded, Postgres-based, ActiveJob backend for Ruby on Rails.
7
+ GoodJob is a multithreaded, Postgres-based, Active Job backend for Ruby on Rails.
8
8
 
9
- **Inspired by [Delayed::Job](https://github.com/collectiveidea/delayed_job) and [Que](https://github.com/que-rb/que), GoodJob is designed for maximum compatibility with Ruby on Rails, ActiveJob, and Postgres to be simple and performant for most workloads.**
9
+ **Inspired by [Delayed::Job](https://github.com/collectiveidea/delayed_job) and [Que](https://github.com/que-rb/que), GoodJob is designed for maximum compatibility with Ruby on Rails, Active Job, and Postgres to be simple and performant for most workloads.**
10
10
 
11
- - **Designed for ActiveJob.** Complete support for [async, queues, delays, priorities, timeouts, and retries](https://edgeguides.rubyonrails.org/active_job_basics.html) with near-zero configuration.
11
+ - **Designed for Active Job.** Complete support for [async, queues, delays, priorities, timeouts, and retries](https://edgeguides.rubyonrails.org/active_job_basics.html) with near-zero configuration.
12
12
  - **Built for Rails.** Fully adopts Ruby on Rails [threading and code execution guidelines](https://guides.rubyonrails.org/threading_and_code_execution.html) with [Concurrent::Ruby](https://github.com/ruby-concurrency/concurrent-ruby).
13
13
  - **Backed by Postgres.** Relies upon Postgres integrity, session-level Advisory Locks to provide run-once safety and stay within the limits of `schema.rb`, and LISTEN/NOTIFY to reduce queuing latency.
14
14
  - **For most workloads.** Targets full-stack teams, economy-minded solo developers, and applications that enqueue 1-million jobs/day and more.
@@ -55,7 +55,7 @@ For more of the story of GoodJob, read the [introductory blog post](https://isla
55
55
  - [Exceptions, retries, and reliability](#exceptions-retries-and-reliability)
56
56
  - [Exceptions](#exceptions)
57
57
  - [Retries](#retries)
58
- - [ActionMailer retries](#actionmailer-retries)
58
+ - [Action Mailer retries](#action-mailer-retries)
59
59
  - [Interrupts](#interrupts)
60
60
  - [Timeouts](#timeouts)
61
61
  - [Optimize queues, threads, and processes](#optimize-queues-threads-and-processes)
@@ -63,7 +63,7 @@ For more of the story of GoodJob, read the [introductory blog post](https://isla
63
63
  - [Production setup](#production-setup)
64
64
  - [Queue performance with Queue Select Limit](#queue-performance-with-queue-select-limit)
65
65
  - [Execute jobs async / in-process](#execute-jobs-async--in-process)
66
- - [Migrate to GoodJob from a different ActiveJob backend](#migrate-to-goodjob-from-a-different-activejob-backend)
66
+ - [Migrate to GoodJob from a different Active Job backend](#migrate-to-goodjob-from-a-different-active-job-backend)
67
67
  - [Monitor and preserve worked jobs](#monitor-and-preserve-worked-jobs)
68
68
  - [Write tests](#write-tests)
69
69
  - [PgBouncer compatibility](#pgbouncer-compatibility)
@@ -103,7 +103,7 @@ For more of the story of GoodJob, read the [introductory blog post](https://isla
103
103
  bin/rails db:migrate:animals
104
104
  ```
105
105
 
106
- 1. Configure the ActiveJob adapter:
106
+ 1. Configure the Active Job adapter:
107
107
 
108
108
  ```ruby
109
109
  # config/application.rb or config/environments/{RAILS_ENV}.rb
@@ -116,7 +116,7 @@ For more of the story of GoodJob, read the [introductory blog post](https://isla
116
116
  YourJob.perform_later
117
117
  ```
118
118
 
119
- GoodJob supports all ActiveJob features:
119
+ GoodJob supports all Active Job features:
120
120
 
121
121
  ```ruby
122
122
  YourJob.set(queue: :some_queue, wait: 5.minutes, priority: 10).perform_later
@@ -222,7 +222,7 @@ and this command is not required to be used.
222
222
 
223
223
  ### Configuration options
224
224
 
225
- ActiveJob configuration depends on where the code is placed:
225
+ Active Job configuration depends on where the code is placed:
226
226
 
227
227
  - `config.active_job.queue_adapter = :good_job` within `config/application.rb` or `config/environments/*.rb`.
228
228
  - `ActiveJob::Base.queue_adapter = :good_job` within an initializer (e.g. `config/initializers/active_job.rb`).
@@ -321,7 +321,7 @@ config.good_job.execution_mode = :external
321
321
 
322
322
  Good Job’s general behavior can also be configured via attributes directly on the `GoodJob` module:
323
323
 
324
- - **`GoodJob.configure_active_record { ... }`** Inject Active Record configuration into GoodJob's base model, for example, when using [multiple databases with ActiveRecord](https://guides.rubyonrails.org/active_record_multiple_databases.html) or when other custom configuration is necessary for the ActiveRecord model to connect to the Postgres database. Example:
324
+ - **`GoodJob.configure_active_record { ... }`** Inject Active Record configuration into GoodJob's base model, for example, when using [multiple databases with Active Record](https://guides.rubyonrails.org/active_record_multiple_databases.html) or when other custom configuration is necessary for the Active Record model to connect to the Postgres database. Example:
325
325
 
326
326
  ```ruby
327
327
  # config/initializers/good_job.rb
@@ -331,7 +331,7 @@ Good Job’s general behavior can also be configured via attributes directly on
331
331
  end
332
332
  ```
333
333
 
334
- - **`GoodJob.active_record_parent_class`** (string) Alternatively, modify the ActiveRecord parent class inherited by GoodJob's Active Record model `GoodJob::Job` (defaults to `"ActiveRecord::Base"`). Configure this _The value must be a String to avoid premature initialization of ActiveRecord._
334
+ - **`GoodJob.active_record_parent_class`** (string) Alternatively, modify the Active Record parent class inherited by GoodJob's Active Record model `GoodJob::Job` (defaults to `"ActiveRecord::Base"`). Configure this _The value must be a String to avoid premature initialization of Active Record._
335
335
 
336
336
  You’ll generally want to configure these in `config/initializers/good_job.rb`, like so:
337
337
 
@@ -438,9 +438,34 @@ The Dashboard can be set to automatically refresh by checking "Live Poll" in the
438
438
 
439
439
  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`.
440
440
 
441
+ ### Labelled jobs
442
+
443
+ Labels are the recommended way to add context or metadata to specific jobs. For example, all jobs that have a dependency on an email service could be labeled `email`. Using labels requires adding the Active Job extension `GoodJob::ActiveJobExtensions::Labels` to your job class.
444
+
445
+ ```ruby
446
+ class ApplicationRecord < ActiveJob::Base
447
+ include GoodJob::ActiveJobExtensions::Labels
448
+ end
449
+
450
+ # Add a default label to every job within the class
451
+ class WelcomeJob < ApplicationRecord
452
+ self.good_job_labels = ["email"]
453
+
454
+ def perform
455
+ # Labels can be inspected from within the job
456
+ puts good_job_labels # => ["email"]
457
+ end
458
+ end
459
+
460
+ # Or add to individual jobs when enqueued
461
+ WelcomeJob.set(good_job_labels: ["email"]).perform_later
462
+ ```
463
+
464
+ Labels can be used to search jobs in the Dashboard. For example, to find all jobs labeled `email`, search for `email`.
465
+
441
466
  ### Concurrency controls
442
467
 
443
- GoodJob can extend ActiveJob to provide limits on concurrently running jobs, either at time of _enqueue_ or at _perform_. Limiting concurrency can help prevent duplicate, double or unnecessary jobs from being enqueued, or race conditions when performing, for example when interacting with 3rd-party APIs.
468
+ GoodJob can extend Active Job to provide limits on concurrently running jobs, either at time of _enqueue_ or at _perform_. Limiting concurrency can help prevent duplicate, double or unnecessary jobs from being enqueued, or race conditions when performing, for example when interacting with 3rd-party APIs.
444
469
 
445
470
  ```ruby
446
471
  class MyJob < ApplicationJob
@@ -469,7 +494,7 @@ class MyJob < ApplicationJob
469
494
 
470
495
  # A unique key to be globally locked against.
471
496
  # Can be String or Lambda/Proc that is invoked in the context of the job.
472
- # Note: Arguments passed to #perform_later can be accessed through ActiveJob's `arguments` method
497
+ # Note: Arguments passed to #perform_later can be accessed through Active Job's `arguments` method
473
498
  # which is an array containing positional arguments and, optionally, a kwarg hash.
474
499
  key: -> { "MyJob-#{arguments.first}-#{arguments.last[:version]}" } # MyJob.perform_later("Alice", version: 'v2') => "MyJob-Alice-v2"
475
500
  )
@@ -549,11 +574,11 @@ active_jobs = GoodJob::Bulk.enqueue do
549
574
  # If an exception is raised within this block, no jobs will be inserted.
550
575
  end
551
576
 
552
- # All ActiveJob instances are returned from GoodJob::Bulk.enqueue.
577
+ # All Active Job instances are returned from GoodJob::Bulk.enqueue.
553
578
  # Jobs that have been successfully enqueued have a `provider_job_id` set.
554
579
  active_jobs.all?(&:provider_job_id)
555
580
 
556
- # Bulk enqueue ActiveJob instances directly without using `.perform_later`:
581
+ # Bulk enqueue Active Job instances directly without using `.perform_later`:
557
582
  GoodJob::Bulk.enqueue(MyJob.new, AnotherJob.new)
558
583
  ```
559
584
 
@@ -737,7 +762,7 @@ GoodJob follows semantic versioning, though updates may be encouraged through de
737
762
 
738
763
  Upgrading between minor versions (e.g. v1.4 to v1.5) should not introduce breaking changes, but can introduce new deprecation warnings and database migration warnings.
739
764
 
740
- Database migrations introduced in minor releases are _not required_ to be applied until the next major release. If you would like apply newly introduced migrations immediately, assert `GoodJob.migrated?` in your application's test suite.
765
+ Database migrations introduced in minor releases are _not required_ to be applied until the next major release. If you would like to apply newly introduced migrations immediately, assert `GoodJob.migrated?` in your application's test suite.
741
766
 
742
767
  To perform upgrades to the GoodJob database tables:
743
768
 
@@ -791,7 +816,7 @@ Notable changes:
791
816
  - Renames `:async_server` execution mode to `:async`; renames prior `:async` execution mode to `:async_all`.
792
817
  - Sets default Development environment's execution mode to `:async` with disabled polling.
793
818
  - Excludes performing jobs from `enqueue_limit`'s count in `GoodJob::ActiveJobExtensions::Concurrency`.
794
- - Triggers `GoodJob.on_thread_error` for unhandled ActiveJob exceptions.
819
+ - Triggers `GoodJob.on_thread_error` for unhandled Active Job exceptions.
795
820
  - Renames `GoodJob.reperform_jobs_on_standard_error` accessor to `GoodJob.retry_on_unhandled_error`.
796
821
  - Renames `GoodJob::Adapter.shutdown(wait:)` argument to `GoodJob::Adapter.shutdown(timeout:)`.
797
822
  - Changes Advisory Lock key format from `good_jobs[ROW_ID]` to `good_jobs-[ACTIVE_JOB_ID]`.
@@ -801,11 +826,11 @@ Notable changes:
801
826
 
802
827
  ### Exceptions, retries, and reliability
803
828
 
804
- GoodJob guarantees that a completely-performed job will run once and only once. GoodJob fully supports ActiveJob's built-in functionality for error handling, retries and timeouts.
829
+ GoodJob guarantees that a completely-performed job will run once and only once. GoodJob fully supports Active Job's built-in functionality for error handling, retries and timeouts.
805
830
 
806
831
  #### Exceptions
807
832
 
808
- ActiveJob provides [tools for rescuing and retrying exceptions](https://guides.rubyonrails.org/active_job_basics.html#exceptions), including `retry_on`, `discard_on`, `rescue_from` that will rescue exceptions before they get to GoodJob.
833
+ Active Job provides [tools for rescuing and retrying exceptions](https://guides.rubyonrails.org/active_job_basics.html#exceptions), including `retry_on`, `discard_on`, `rescue_from` that will rescue exceptions before they get to GoodJob.
809
834
 
810
835
  If errors do reach GoodJob, you can assign a callable to `GoodJob.on_thread_error` to be notified. For example, to log errors to an exception monitoring service like Sentry (or Bugsnag, Airbrake, Honeybadger, etc.):
811
836
 
@@ -816,9 +841,9 @@ GoodJob.on_thread_error = -> (exception) { Rails.error.report(exception) }
816
841
 
817
842
  #### Retries
818
843
 
819
- By default, GoodJob relies on ActiveJob's retry functionality.
844
+ By default, GoodJob relies on Active Job's retry functionality.
820
845
 
821
- ActiveJob can be configured to retry an infinite number of times, with a polynomial backoff. Using ActiveJob's `retry_on` prevents exceptions from reaching GoodJob:
846
+ Active Job can be configured to retry an infinite number of times, with a polynomial backoff. Using Active Job's `retry_on` prevents exceptions from reaching GoodJob:
822
847
 
823
848
  ```ruby
824
849
  class ApplicationJob < ActiveJob::Base
@@ -860,7 +885,7 @@ end
860
885
 
861
886
  By default, jobs will not be retried unless `retry_on` is configured. This can be overridden by setting `GoodJob.retry_on_unhandled_error` to `true`; GoodJob will then retry the failing job immediately and infinitely, potentially causing high load.
862
887
 
863
- #### ActionMailer retries
888
+ #### Action Mailer retries
864
889
 
865
890
  Any configuration in `ApplicationJob` will have to be duplicated on `ActionMailer::MailDeliveryJob` because ActionMailer uses that custom class which inherits from `ActiveJob::Base`, rather than your application's `ApplicationJob`.
866
891
 
@@ -886,16 +911,16 @@ might also be configured to use (deprecated now) `ActionMailer::DeliveryJob`.
886
911
 
887
912
  Jobs will be automatically retried if the process is interrupted while performing a job, for example as the result of a `SIGKILL` or power failure.
888
913
 
889
- If you need more control over interrupt-caused retries, include the `GoodJob::ActiveJobExtensions::InterruptErrors` extension in your job class. When an interrupted job is retried, the extension will raise a `GoodJob::InterruptError` exception within the job, which allows you to use ActiveJob's `retry_on` and `discard_on` to control the behavior of the job.
914
+ If you need more control over interrupt-caused retries, include the `GoodJob::ActiveJobExtensions::InterruptErrors` extension in your job class. When an interrupted job is retried, the extension will raise a `GoodJob::InterruptError` exception within the job, which allows you to use Active Job's `retry_on` and `discard_on` to control the behavior of the job.
890
915
 
891
916
  ```ruby
892
917
  class MyJob < ApplicationJob
893
918
  # The extension must be included before other extensions
894
919
  include GoodJob::ActiveJobExtensions::InterruptErrors
895
920
  # Discard the job if it is interrupted
896
- discard_on InterruptError
921
+ discard_on GoodJob::InterruptError
897
922
  # Retry the job if it is interrupted
898
- retry_on InterruptError, wait: 0, attempts: Float::INFINITY
923
+ retry_on GoodJob::InterruptError, wait: 0, attempts: Float::INFINITY
899
924
  end
900
925
  ```
901
926
 
@@ -1176,9 +1201,9 @@ Depending on your application configuration, you may need to take additional ste
1176
1201
 
1177
1202
  If you are using cron-style jobs, you might also want to look at your Passenger configuration, especially at [`passenger_pool_idle_time`](https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_pool_idle_time) and [`passenger_min_instances`](https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_min_instances) to make sure there's always at least once process running that can execute cron-style scheduled jobs. See also [Passenger's optimization guide](https://www.phusionpassenger.com/library/config/nginx/optimization/#minimizing-process-spawning) for more information.
1178
1203
 
1179
- ### Migrate to GoodJob from a different ActiveJob backend
1204
+ ### Migrate to GoodJob from a different Active Job backend
1180
1205
 
1181
- If your application is already using an ActiveJob backend, you will need to install GoodJob to enqueue and perform newly created jobs _and_ finish performing pre-existing jobs on the previous backend.
1206
+ If your application is already using an Active Job backend, you will need to install GoodJob to enqueue and perform newly created jobs _and_ finish performing pre-existing jobs on the previous backend.
1182
1207
 
1183
1208
  1. Enqueue newly created jobs on GoodJob either entirely by setting `ActiveJob::Base.queue_adapter = :good_job` or progressively via individual job classes:
1184
1209
 
@@ -1198,7 +1223,7 @@ If your application is already using an ActiveJob backend, you will need to inst
1198
1223
  worker: bundle exec que ./config/environment.rb & bundle exec good_job & wait -n
1199
1224
  ```
1200
1225
 
1201
- 1. Once you are confident that no unperformed jobs remain in the previous ActiveJob backend, code and configuration for that backend can be completely removed.
1226
+ 1. Once you are confident that no unperformed jobs remain in the previous Active Job backend, code and configuration for that backend can be completely removed.
1202
1227
 
1203
1228
  ### Monitor and preserve worked jobs
1204
1229
 
@@ -1271,7 +1296,7 @@ A workaround to this limitation is to make a direct database connection availabl
1271
1296
  url: postgres://database_host/my_database
1272
1297
  ```
1273
1298
 
1274
- 1. Create a new ActiveRecord base class that uses the direct database connection
1299
+ 1. Create a new Active Record base class that uses the direct database connection
1275
1300
 
1276
1301
  ```ruby
1277
1302
  # app/models/application_direct_record.rb
@@ -1282,7 +1307,7 @@ A workaround to this limitation is to make a direct database connection availabl
1282
1307
  end
1283
1308
  ```
1284
1309
 
1285
- 1. Configure GoodJob to use the newly created ActiveRecord base class:
1310
+ 1. Configure GoodJob to use the newly created Active Record base class:
1286
1311
 
1287
1312
  ```ruby
1288
1313
  # config/initializers/good_job.rb
@@ -96,7 +96,7 @@ module GoodJob
96
96
  def redirect_on_error(exception)
97
97
  alert = case exception
98
98
  when GoodJob::Job::AdapterNotGoodJobError
99
- "ActiveJob Queue Adapter must be GoodJob."
99
+ "Active Job Queue Adapter must be GoodJob."
100
100
  when GoodJob::Job::ActionForStateMismatchError
101
101
  "Job is not in an appropriate state for this action."
102
102
  else
@@ -34,7 +34,7 @@ module GoodJob
34
34
  query = query.to_s.strip
35
35
  next if query.blank?
36
36
 
37
- tsvector = "(to_tsvector('english', serialized_params) || to_tsvector('english', id::text) || to_tsvector('english', COALESCE(error, '')::text))"
37
+ tsvector = "(to_tsvector('english', id::text) || to_tsvector('english', COALESCE(active_job_id::text, '')) || to_tsvector('english', serialized_params) || to_tsvector('english', COALESCE(error, ''))#{" || to_tsvector('english', COALESCE(array_to_string(labels, ' '), ''))" if labels_migrated?})"
38
38
  to_tsquery_function = database_supports_websearch_to_tsquery? ? 'websearch_to_tsquery' : 'plainto_tsquery'
39
39
  where("#{tsvector} @@ #{to_tsquery_function}(?)", query)
40
40
  .order(sanitize_sql_for_order([Arel.sql("ts_rank(#{tsvector}, #{to_tsquery_function}(?))"), query]) => 'DESC')
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GoodJob
4
- # ActiveRecord model to share behavior between {Job} and {Execution} models
4
+ # Active Record model to share behavior between {Job} and {Execution} models
5
5
  # which both read out of the same table.
6
6
  class BaseExecution < BaseRecord
7
7
  include AdvisoryLockable
@@ -56,6 +56,27 @@ module GoodJob
56
56
  migration_pending_warning!
57
57
  false
58
58
  end
59
+
60
+ def labels_migrated?
61
+ return true if columns_hash["labels"].present?
62
+
63
+ migration_pending_warning!
64
+ false
65
+ end
66
+
67
+ def labels_indices_migrated?
68
+ return true if connection.index_name_exists?(:good_jobs, :index_good_jobs_on_labels)
69
+
70
+ migration_pending_warning!
71
+ false
72
+ end
73
+
74
+ def active_job_id_index_removal_migrated?
75
+ return true unless connection.index_name_exists?(:good_jobs, :index_good_jobs_on_active_job_id)
76
+
77
+ migration_pending_warning!
78
+ false
79
+ end
59
80
  end
60
81
 
61
82
  # The ActiveJob job class, as a string
@@ -88,6 +109,7 @@ module GoodJob
88
109
  .tap do |job_data|
89
110
  job_data["provider_job_id"] = id
90
111
  job_data["good_job_concurrency_key"] = concurrency_key if concurrency_key
112
+ job_data["good_job_labels"] = Array(labels) if self.class.labels_migrated? && labels.present?
91
113
  end
92
114
  end
93
115
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GoodJob
4
- # ActiveRecord model that represents an +ActiveJob+ job.
4
+ # Active Record model that represents an +ActiveJob+ job.
5
5
  class Execution < BaseExecution
6
6
  # Raised if something attempts to execute a previously completed Execution again.
7
7
  PreviouslyPerformedError = Class.new(StandardError)
@@ -150,9 +150,9 @@ module GoodJob
150
150
  # @return [ActiveRecord::Relation]
151
151
  scope :schedule_ordered, -> { order(coalesce_scheduled_at_created_at.asc) }
152
152
 
153
- # Get Jobs were completed before the given timestamp. If no timestamp is
154
- # provided, get all jobs that have been completed. By default, GoodJob
155
- # destroys jobs after they are completed and this will find no jobs.
153
+ # Get completed jobs before the given timestamp. If no timestamp is
154
+ # provided, get *all* completed jobs. By default, GoodJob
155
+ # destroys jobs after they're completed, meaning this returns no jobs.
156
156
  # However, if you have changed {GoodJob.preserve_job_records}, this may
157
157
  # find completed Jobs.
158
158
  # @!method finished(timestamp = nil)
@@ -225,6 +225,13 @@ module GoodJob
225
225
  execution_args[:scheduled_at] = Time.zone.at(active_job.scheduled_at) if active_job.scheduled_at
226
226
  execution_args[:concurrency_key] = active_job.good_job_concurrency_key if active_job.respond_to?(:good_job_concurrency_key)
227
227
 
228
+ if active_job.respond_to?(:good_job_labels) && active_job.good_job_labels.any? && labels_migrated?
229
+ labels = active_job.good_job_labels.dup
230
+ labels.map! { |label| label.to_s.strip.presence }
231
+ labels.tap(&:compact!).tap(&:uniq!)
232
+ execution_args[:labels] = labels
233
+ end
234
+
228
235
  reenqueued_current_execution = CurrentThread.active_job_id && CurrentThread.active_job_id == active_job.job_id
229
236
  current_execution = CurrentThread.execution
230
237
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GoodJob
4
- # ActiveRecord model that represents an +ActiveJob+ job.
4
+ # Active Record model that represents an +ActiveJob+ job.
5
5
  # There is not a table in the database whose discrete rows represents "Jobs".
6
6
  # The +good_jobs+ table is a table of individual {GoodJob::Execution}s that share the same +active_job_id+.
7
7
  # A single row from the +good_jobs+ table of executions is fetched to represent a Job.
@@ -9,7 +9,7 @@ module GoodJob
9
9
  class Job < BaseExecution
10
10
  # Raised when an inappropriate action is applied to a Job based on its state.
11
11
  ActionForStateMismatchError = Class.new(StandardError)
12
- # Raised when an action requires GoodJob to be the ActiveJob Queue Adapter but GoodJob is not.
12
+ # Raised when GoodJob is not configured as the Active Job Queue Adapter
13
13
  AdapterNotGoodJobError = Class.new(StandardError)
14
14
  # Attached to a Job's Execution when the Job is discarded.
15
15
  DiscardJobError = Class.new(StandardError)
@@ -60,7 +60,7 @@ module GoodJob
60
60
 
61
61
  scope :unfinished_undiscrete, -> { where(finished_at: nil, retried_good_job_id: nil, is_discrete: [nil, false]) }
62
62
 
63
- # The job's ActiveJob UUID
63
+ # The job's Active Job UUID
64
64
  # @return [String]
65
65
  def id
66
66
  active_job_id
@@ -101,7 +101,7 @@ module GoodJob
101
101
  executions.first
102
102
  end
103
103
 
104
- # The number of times this job has been executed, according to ActiveJob's serialized state.
104
+ # The number of times this job has been executed, according to Active Job's serialized state.
105
105
  # @return [Numeric]
106
106
  def executions_count
107
107
  aj_count = serialized_params.fetch('executions', 0)
@@ -1,13 +1,5 @@
1
1
  <footer class="footer mt-auto py-3 bg-body-tertiary border-top text-muted small" id="footer" data-live-poll-region="footer">
2
2
  <div class="container-fluid">
3
- <div class="row">
4
- <div class="col-6">
5
- <%= t(".last_update_html", time: Time.current.utc.iso8601) %>
6
- </div>
7
-
8
- <div class="col-6 text-end">
9
- <%= t(".wording") %>
10
- </div>
11
- </div>
3
+ GoodJob v<%= GoodJob::VERSION %>
12
4
  </div>
13
5
  </footer>
@@ -1,4 +1,4 @@
1
- <nav class="navbar navbar-expand-lg border-bottom bg-body sticky-top shadow-sm">
1
+ <nav class="navbar navbar-expand-lg border-bottom bg-body sticky-top">
2
2
  <div class="container-fluid">
3
3
  <%= link_to t(".name"), root_path, class: "navbar-brand mb-0 h1" %>
4
4
  <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
@@ -0,0 +1,11 @@
1
+ <nav class="navbar p-1 shadow-sm mt-auto border-bottom text-muted small" id="secondary-navbar" data-live-poll-region="secondary-navbar">
2
+ <div class="container-fluid">
3
+ <div class="flex-fill d-none d-sm-block ">
4
+ <%= t(".inspiration") %>
5
+ </div>
6
+ <div class="flex-fill text-end">
7
+ <% last_updated_at = Time.current.utc.iso8601 %>
8
+ <%= t(".last_updated") %>&nbsp;<time id="page-updated-at" datetime="<%= last_updated_at %>"><%== last_updated_at %></time>
9
+ </div>
10
+ </div>
11
+ </nav>
@@ -1,44 +1,39 @@
1
1
  <!DOCTYPE html>
2
2
  <html lang="<%= I18n.locale %>" data-bs-theme="auto">
3
- <head>
4
- <title>Good Job Dashboard</title>
5
- <meta charset="utf-8">
6
- <meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
7
- <%= csrf_meta_tags %>
8
- <%= csp_meta_tag %>
9
-
10
- <%# Do not use asset tag helpers to avoid paths being overriden by config.asset_host %>
11
- <%= tag.link rel: "stylesheet", href: frontend_static_path(:bootstrap, format: :css, v: GoodJob::VERSION, locale: nil), nonce: content_security_policy_nonce %>
12
- <%= tag.link rel: "stylesheet", href: frontend_static_path(:style, format: :css, v: GoodJob::VERSION, locale: nil), nonce: content_security_policy_nonce %>
13
-
14
- <%= tag.script "", src: frontend_static_path(:bootstrap, format: :js, v: GoodJob::VERSION, locale: nil), nonce: content_security_policy_nonce %>
15
- <%= tag.script "", src: frontend_static_path(:chartjs, format: :js, v: GoodJob::VERSION, locale: nil), nonce: content_security_policy_nonce %>
16
- <%= tag.script "", src: frontend_static_path(:rails_ujs, format: :js, v: GoodJob::VERSION, locale: nil), nonce: content_security_policy_nonce %>
17
-
18
- <%= tag.script "", src: frontend_static_path(:es_module_shims, format: :js, v: GoodJob::VERSION, locale: nil), async: true, nonce: content_security_policy_nonce %>
19
- <% importmaps = GoodJob::FrontendsController.js_modules.keys.index_with { |module_name| frontend_module_path(module_name, format: :js, locale: nil, v: GoodJob::VERSION) } %>
20
- <%= tag.script({ imports: importmaps }.to_json.html_safe, type: "importmap", nonce: content_security_policy_nonce) %>
21
- <%= tag.script "", type: "module", nonce: content_security_policy_nonce do %> import "application"; <% end %>
22
- <%= tag.script "", nonce: content_security_policy_nonce do %>
23
- // Ensure theme is updated before dom loads to avoid flash of style
24
- let theme = localStorage.getItem('good_job-theme');
25
- if (!["light", "dark"].includes(theme)) {
3
+ <head>
4
+ <title>Good Job Dashboard</title>
5
+ <meta charset="utf-8">
6
+ <meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
7
+ <%= csrf_meta_tags %>
8
+ <%= csp_meta_tag %>
9
+ <%# Do not use asset tag helpers to avoid paths being overriden by config.asset_host %>
10
+ <%= tag.link rel: "stylesheet", href: frontend_static_path(:bootstrap, format: :css, v: GoodJob::VERSION, locale: nil), nonce: content_security_policy_nonce %>
11
+ <%= tag.link rel: "stylesheet", href: frontend_static_path(:style, format: :css, v: GoodJob::VERSION, locale: nil), nonce: content_security_policy_nonce %>
12
+ <%= tag.script "", src: frontend_static_path(:bootstrap, format: :js, v: GoodJob::VERSION, locale: nil), nonce: content_security_policy_nonce %>
13
+ <%= tag.script "", src: frontend_static_path(:chartjs, format: :js, v: GoodJob::VERSION, locale: nil), nonce: content_security_policy_nonce %>
14
+ <%= tag.script "", src: frontend_static_path(:rails_ujs, format: :js, v: GoodJob::VERSION, locale: nil), nonce: content_security_policy_nonce %>
15
+ <%= tag.script "", src: frontend_static_path(:es_module_shims, format: :js, v: GoodJob::VERSION, locale: nil), async: true, nonce: content_security_policy_nonce %>
16
+ <% importmaps = GoodJob::FrontendsController.js_modules.keys.index_with { |module_name| frontend_module_path(module_name, format: :js, locale: nil, v: GoodJob::VERSION) } %>
17
+ <%= tag.script({ imports: importmaps }.to_json.html_safe, type: "importmap", nonce: content_security_policy_nonce) %>
18
+ <%= tag.script "", type: "module", nonce: content_security_policy_nonce do %> import "application"; <% end %>
19
+ <%= tag.script "", nonce: content_security_policy_nonce do %>
20
+ // Ensure theme is updated before dom loads to avoid flash of style
21
+ let theme = localStorage.getItem('good_job-theme');
22
+ if (!["light", "dark"].includes(theme)) {
26
23
  theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
27
- }
28
- document.documentElement.setAttribute('data-bs-theme', theme);
29
- <% end %>
30
- </head>
31
- <body>
32
- <div class="d-flex flex-column min-vh-100">
33
- <%= render "good_job/shared/navbar" %>
34
-
35
- <div class="container-fluid flex-grow-1 relative">
36
- <%= render "good_job/shared/alert" %>
37
-
38
- <%= yield %>
24
+ }
25
+ document.documentElement.setAttribute('data-bs-theme', theme);
26
+ <% end %>
27
+ </head>
28
+ <body>
29
+ <div class="d-flex flex-column min-vh-100">
30
+ <%= render "good_job/shared/navbar" %>
31
+ <%= render "good_job/shared/secondary_navbar" %>
32
+ <div class="container-fluid flex-grow-1 relative">
33
+ <%= render "good_job/shared/alert" %>
34
+ <%= yield %>
35
+ </div>
36
+ <%= render "good_job/shared/footer" %>
39
37
  </div>
40
-
41
- <%= render "good_job/shared/footer" %>
42
- </div>
43
- </body>
38
+ </body>
44
39
  </html>
@@ -219,9 +219,6 @@ de:
219
219
  placeholder: Suchen Sie nach Klasse, Job-ID, Jobparametern und Fehlertext.
220
220
  queue_name: Warteschlangenname
221
221
  search: Suchen
222
- footer:
223
- last_update_html: Zuletzt aktualisiert <time id="page-updated-at" datetime="%{time}">%{time}</time>
224
- wording: Denk daran, auch du machst einen guten Job!
225
222
  navbar:
226
223
  batches: Batches
227
224
  cron_schedules: Cron
@@ -234,6 +231,9 @@ de:
234
231
  dark: Dunkel
235
232
  light: Licht
236
233
  theme: Thema
234
+ secondary_navbar:
235
+ inspiration: Denk daran, auch du machst einen guten Job!
236
+ last_updated: Zuletzt aktualisiert
237
237
  status:
238
238
  discarded: Ausrangiert
239
239
  queued: In der Warteschlange
@@ -219,9 +219,6 @@ en:
219
219
  placeholder: Search by class, job id, job params, and error text.
220
220
  queue_name: Queue name
221
221
  search: Search
222
- footer:
223
- last_update_html: Last updated <time id="page-updated-at" datetime="%{time}">%{time}</time>
224
- wording: Remember, you're doing a Good Job too!
225
222
  navbar:
226
223
  batches: Batches
227
224
  cron_schedules: Cron
@@ -234,6 +231,9 @@ en:
234
231
  dark: Dark
235
232
  light: Light
236
233
  theme: Theme
234
+ secondary_navbar:
235
+ inspiration: Remember, you're doing a Good Job too!
236
+ last_updated: Last updated
237
237
  status:
238
238
  discarded: Discarded
239
239
  queued: Queued
@@ -217,9 +217,6 @@ es:
217
217
  placeholder: Buscar por clase, id de tarea, parámetros y mensaje de error.
218
218
  queue_name: Nombre de la cola
219
219
  search: Buscar
220
- footer:
221
- last_update_html: Última actualización <time id="page-updated-at" datetime="%{time}">%{time}</time>
222
- wording: "¡Recuerda, también tú estás haciendo un buen trabajo!"
223
220
  navbar:
224
221
  batches: Batches
225
222
  cron_schedules: Cron
@@ -232,6 +229,9 @@ es:
232
229
  dark: Oscuro
233
230
  light: Luz
234
231
  theme: Tema
232
+ secondary_navbar:
233
+ inspiration: "¡Recuerda, también tú estás haciendo un buen trabajo!"
234
+ last_updated: Última actualización
235
235
  status:
236
236
  discarded:
237
237
  one: Descartada
@@ -219,9 +219,6 @@ fr:
219
219
  placeholder: Recherche par classe, ID de job, paramètres de job et texte d'erreur.
220
220
  queue_name: Nom de la file d'attente
221
221
  search: Recherche
222
- footer:
223
- last_update_html: Dernière mise à jour <time id="page-updated-at" datetime="%{time}">%{time}</time>
224
- wording: N'oublie pas, toi aussi tu fais du bon boulot !
225
222
  navbar:
226
223
  batches: Lots
227
224
  cron_schedules: Cron
@@ -234,6 +231,9 @@ fr:
234
231
  dark: Sombre
235
232
  light: Lumière
236
233
  theme: Thème
234
+ secondary_navbar:
235
+ inspiration: N'oublie pas, toi aussi tu fais du bon boulot !
236
+ last_updated: Dernière mise à jour
237
237
  status:
238
238
  discarded: Mis au rebut
239
239
  queued: À la file
@@ -219,9 +219,6 @@ ja:
219
219
  placeholder: クラス名、ジョブID、ジョブパラメータ、エラーテキストで検索
220
220
  queue_name: キュー名
221
221
  search: 検索
222
- footer:
223
- last_update_html: 最終更新 <time id="page-updated-at" datetime="%{time}">%{time}</time>
224
- wording: Remember, you're doing a Good Job too!
225
222
  navbar:
226
223
  batches: バッチ
227
224
  cron_schedules: Cron
@@ -234,6 +231,9 @@ ja:
234
231
  dark: 暗い
235
232
  light: ライト
236
233
  theme: テーマ
234
+ secondary_navbar:
235
+ inspiration: 覚えておいてください、あなたも良い仕事をしています!
236
+ last_updated: 最終更新
237
237
  status:
238
238
  discarded: 破棄済み
239
239
  queued: 処理待ち
@@ -219,9 +219,6 @@ nl:
219
219
  placeholder: Zoek op klasse, taak-ID, taakparameters en fouttekst.
220
220
  queue_name: Wachtrij naam
221
221
  search: Zoekopdracht
222
- footer:
223
- last_update_html: Laatst bijgewerkt <time id="page-updated-at" datetime="%{time}">%{time}</time>
224
- wording: 'Onthoud: jij levert ook goed werk!'
225
222
  navbar:
226
223
  batches: Batches
227
224
  cron_schedules: Cron
@@ -234,6 +231,9 @@ nl:
234
231
  dark: Donker
235
232
  light: Licht
236
233
  theme: Thema
234
+ secondary_navbar:
235
+ inspiration: 'Onthoud: jij levert ook goed werk!'
236
+ last_updated: Laatst bijgewerkt
237
237
  status:
238
238
  discarded: weggegooid
239
239
  queued: In de wachtrij
@@ -245,9 +245,6 @@ ru:
245
245
  placeholder: Поиск по классу, идентификатору задания, параметрам задания и тексту ошибки.
246
246
  queue_name: Имя очереди
247
247
  search: Поиск
248
- footer:
249
- last_update_html: Последнее обновление <time id="page-updated-at" datetime="%{time}">%{time}</time>
250
- wording: Запомни, ты делаешь Good Job тоже!
251
248
  navbar:
252
249
  batches: Batches
253
250
  cron_schedules: Cron
@@ -260,6 +257,9 @@ ru:
260
257
  dark: Темный
261
258
  light: Свет
262
259
  theme: Тема
260
+ secondary_navbar:
261
+ inspiration: Запомни, ты делаешь Good Job тоже!
262
+ last_updated: Последнее обновление
263
263
  status:
264
264
  discarded: Отброшено
265
265
  queued: В очереди
@@ -219,9 +219,6 @@ tr:
219
219
  placeholder: Sınıfa, iş kimliğine, iş parametrelerine ve hata metnine göre ara.
220
220
  queue_name: Kuyruk adı
221
221
  search: Ara
222
- footer:
223
- last_update_html: Son güncelleme <time id="page-updated-at" datetime="%{time}">%{time}</time>
224
- wording: Unutmayın, siz de iyi bir iş yapıyorsunuz!
225
222
  navbar:
226
223
  batches: Toplu İşler
227
224
  cron_schedules: Cron
@@ -234,6 +231,9 @@ tr:
234
231
  dark: Karanlık
235
232
  light: Işık
236
233
  theme: Tema
234
+ secondary_navbar:
235
+ inspiration: Unutmayın, siz de iyi bir iş yapıyorsunuz!
236
+ last_updated: Son güncelleme
237
237
  status:
238
238
  discarded: İptal Edildi
239
239
  queued: Sırada
@@ -245,9 +245,6 @@ uk:
245
245
  placeholder: Пошук за класом, ідентифікатором роботи, параметрами роботи та текстом помилки.
246
246
  queue_name: Назва черги
247
247
  search: Пошук
248
- footer:
249
- last_update_html: Останнє оновлення <time id="page-updated-at" datetime="%{time}">%{time}</time>
250
- wording: Пам'ятайте, ви теж робите хорошу роботу!
251
248
  navbar:
252
249
  batches: Batches
253
250
  cron_schedules: Cron
@@ -260,6 +257,9 @@ uk:
260
257
  dark: Темний
261
258
  light: світло
262
259
  theme: Тема
260
+ secondary_navbar:
261
+ inspiration: Пам'ятайте, ви теж робите хорошу роботу!
262
+ last_updated: Останнє оновлення
263
263
  status:
264
264
  discarded: Відхилено
265
265
  queued: У черзі
@@ -29,6 +29,7 @@ class CreateGoodJobs < ActiveRecord::Migration<%= migration_version %>
29
29
  t.integer :executions_count
30
30
  t.text :job_class
31
31
  t.integer :error_event, limit: 2
32
+ t.text :labels, array: true
32
33
  end
33
34
 
34
35
  create_table :good_job_batches, id: :uuid do |t|
@@ -76,12 +77,12 @@ class CreateGoodJobs < ActiveRecord::Migration<%= migration_version %>
76
77
  add_index :good_jobs, :concurrency_key, where: "(finished_at IS NULL)", name: :index_good_jobs_on_concurrency_key_when_unfinished
77
78
  add_index :good_jobs, [:cron_key, :created_at], where: "(cron_key IS NOT NULL)", name: :index_good_jobs_on_cron_key_and_created_at_cond
78
79
  add_index :good_jobs, [:cron_key, :cron_at], where: "(cron_key IS NOT NULL)", unique: true, name: :index_good_jobs_on_cron_key_and_cron_at_cond
79
- add_index :good_jobs, [:active_job_id], name: :index_good_jobs_on_active_job_id
80
80
  add_index :good_jobs, [:finished_at], where: "retried_good_job_id IS NULL AND finished_at IS NOT NULL", name: :index_good_jobs_jobs_on_finished_at
81
81
  add_index :good_jobs, [:priority, :created_at], order: { priority: "DESC NULLS LAST", created_at: :asc },
82
82
  where: "finished_at IS NULL", name: :index_good_jobs_jobs_on_priority_created_at_when_unfinished
83
83
  add_index :good_jobs, [:batch_id], where: "batch_id IS NOT NULL"
84
84
  add_index :good_jobs, [:batch_callback_id], where: "batch_callback_id IS NOT NULL"
85
+ add_index :good_jobs, :labels, using: :gin, where: "(labels IS NOT NULL)", name: :index_good_jobs_on_labels
85
86
 
86
87
  add_index :good_job_executions, [:active_job_id, :created_at], name: :index_good_job_executions_on_active_job_id_and_created_at
87
88
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateGoodJobLabels < ActiveRecord::Migration<%= migration_version %>
4
+ def change
5
+ reversible do |dir|
6
+ dir.up do
7
+ # Ensure this incremental update migration is idempotent
8
+ # with monolithic install migration.
9
+ return if connection.column_exists?(:good_jobs, :labels)
10
+ end
11
+ end
12
+
13
+ add_column :good_jobs, :labels, :text, array: true
14
+ end
15
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateGoodJobLabelsIndex < ActiveRecord::Migration<%= migration_version %>
4
+ disable_ddl_transaction!
5
+
6
+ def change
7
+ reversible do |dir|
8
+ dir.up do
9
+ unless connection.index_name_exists?(:good_jobs, :index_good_jobs_on_labels)
10
+ add_index :good_jobs, :labels, using: :gin, where: "(labels IS NOT NULL)",
11
+ name: :index_good_jobs_on_labels, algorithm: :concurrently
12
+ end
13
+ end
14
+
15
+ dir.down do
16
+ if connection.index_name_exists?(:good_jobs, :index_good_jobs_on_labels)
17
+ remove_index :good_jobs, name: :index_good_jobs_on_labels
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ class RemoveGoodJobActiveIdIndex < ActiveRecord::Migration<%= migration_version %>
4
+ disable_ddl_transaction!
5
+
6
+ def change
7
+ reversible do |dir|
8
+ dir.up do
9
+ if connection.index_name_exists?(:good_jobs, :index_good_jobs_on_active_job_id)
10
+ remove_index :good_jobs, name: :index_good_jobs_on_active_job_id
11
+ end
12
+ end
13
+
14
+ dir.down do
15
+ unless connection.index_name_exists?(:good_jobs, :index_good_jobs_on_active_job_id)
16
+ add_index :good_jobs, :active_job_id, name: :index_good_jobs_on_active_job_id
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GoodJob
4
+ module ActiveJobExtensions
5
+ module Labels
6
+ extend ActiveSupport::Concern
7
+
8
+ module Prepends
9
+ def initialize(*arguments)
10
+ super
11
+ self.good_job_labels = Array(self.class.good_job_labels)
12
+ end
13
+
14
+ def enqueue(options = {})
15
+ self.good_job_labels = Array(options[:good_job_labels]) if options.key?(:good_job_labels)
16
+ super
17
+ end
18
+
19
+ def deserialize(job_data)
20
+ super
21
+ self.good_job_labels = job_data.delete("good_job_labels")&.dup || []
22
+ end
23
+ end
24
+
25
+ included do
26
+ prepend Prepends
27
+ class_attribute :good_job_labels, instance_accessor: false, instance_predicate: false, default: []
28
+ attr_accessor :good_job_labels
29
+ end
30
+ end
31
+ end
32
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module GoodJob
4
4
  # GoodJob gem version.
5
- VERSION = '3.21.5'
5
+ VERSION = '3.22.0'
6
6
 
7
7
  # GoodJob version as Gem::Version object
8
8
  GEM_VERSION = Gem::Version.new(VERSION)
data/lib/good_job.rb CHANGED
@@ -12,6 +12,7 @@ require "good_job/active_job_extensions/batches"
12
12
  require "good_job/active_job_extensions/concurrency"
13
13
  require "good_job/interrupt_error"
14
14
  require "good_job/active_job_extensions/interrupt_errors"
15
+ require "good_job/active_job_extensions/labels"
15
16
  require "good_job/active_job_extensions/notify_options"
16
17
 
17
18
  require "good_job/assignable_connection"
@@ -22,7 +23,7 @@ require "good_job/cleanup_tracker"
22
23
  require "good_job/cli"
23
24
  require "good_job/configuration"
24
25
  require "good_job/cron_manager"
25
- require 'good_job/current_thread'
26
+ require "good_job/current_thread"
26
27
  require "good_job/daemon"
27
28
  require "good_job/dependencies"
28
29
  require "good_job/job_performer"
@@ -272,7 +273,7 @@ module GoodJob
272
273
  def self.migrated?
273
274
  # Always update with the most recent migration check
274
275
  GoodJob::Execution.reset_column_information
275
- GoodJob::Execution.cron_indices_migrated?
276
+ GoodJob::Execution.active_job_id_index_removal_migrated?
276
277
  end
277
278
 
278
279
  ActiveSupport.run_load_hooks(:good_job, self)
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.21.5
4
+ version: 3.22.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: 2023-12-12 00:00:00.000000000 Z
11
+ date: 2024-01-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob
@@ -307,6 +307,7 @@ files:
307
307
  - app/views/good_job/shared/_filter.erb
308
308
  - app/views/good_job/shared/_footer.erb
309
309
  - app/views/good_job/shared/_navbar.erb
310
+ - app/views/good_job/shared/_secondary_navbar.erb
310
311
  - app/views/good_job/shared/icons/_arrow_clockwise.html.erb
311
312
  - app/views/good_job/shared/icons/_check.html.erb
312
313
  - app/views/good_job/shared/icons/_circle_half.html.erb
@@ -345,11 +346,15 @@ files:
345
346
  - lib/generators/good_job/templates/update/migrations/05_create_good_job_executions.rb.erb
346
347
  - lib/generators/good_job/templates/update/migrations/06_create_good_jobs_error_event.rb.erb
347
348
  - lib/generators/good_job/templates/update/migrations/07_recreate_good_job_cron_indexes_with_conditional.rb.erb
349
+ - lib/generators/good_job/templates/update/migrations/08_create_good_job_labels.rb.erb
350
+ - lib/generators/good_job/templates/update/migrations/09_create_good_job_labels_index.rb.erb
351
+ - lib/generators/good_job/templates/update/migrations/10_remove_good_job_active_id_index.rb.erb
348
352
  - lib/generators/good_job/update_generator.rb
349
353
  - lib/good_job.rb
350
354
  - lib/good_job/active_job_extensions/batches.rb
351
355
  - lib/good_job/active_job_extensions/concurrency.rb
352
356
  - lib/good_job/active_job_extensions/interrupt_errors.rb
357
+ - lib/good_job/active_job_extensions/labels.rb
353
358
  - lib/good_job/active_job_extensions/notify_options.rb
354
359
  - lib/good_job/adapter.rb
355
360
  - lib/good_job/assignable_connection.rb