good_job 3.4.2 → 3.4.5

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: 6c31e9dc63de3bff82d55ba85e17690ad52dcf2d9b8ebc8bcb0d8dc7eab261b0
4
- data.tar.gz: bab18a8d7b7328f172a6b66adba1767281cba281a8f38e76c8bfd303fd6a25a9
3
+ metadata.gz: 844b8a9661a1a6eefe86a2e58d829405b92bc7b54ad51ba6aeafea22c11ca080
4
+ data.tar.gz: c09eab3a34fe0ba218d79c0576bc1d9fe8cb41182c6d2bbf0af28c9bbbd4ce9a
5
5
  SHA512:
6
- metadata.gz: d93aa0c5246d5e7be37f5887b7b3c8618815b88558360bb351ce9df7529460994506ddbdd84f1c57d4066216ea0fa98afdb937468c60aafd4966a2de88967656
7
- data.tar.gz: b25da80ce2ca30517c4ec89187e062db7c1fd1e0b16ce7adaec33fb7da53ce1d56163a58595b57b4919d9d9cdbdf81ef6cf61664c48d2940c1f06fc70aaa6589
6
+ metadata.gz: 9d492d0763ab41e64f3670bc15176f6eec58d4fea9b4be7aa850cb2eaff4dba56d94ce42c6b0d123046dc2f0f2d183ad69e1fe6792b3e0b7fc077a32fecdff78
7
+ data.tar.gz: bb956e43b86d88a4492d715ddef00710d5667d6aed2a33b9fe9c532cfb1515f74a03e8bc7d7b86b4655296ed5ab22ad8813d9794c281304f1672853364928d1e
data/CHANGELOG.md CHANGED
@@ -1,5 +1,52 @@
1
1
  # Changelog
2
2
 
3
+ ## [v3.4.5](https://github.com/bensheldon/good_job/tree/v3.4.5) (2022-09-12)
4
+
5
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.4.4...v3.4.5)
6
+
7
+ **Fixed bugs:**
8
+
9
+ - Dashboard: Remove translation\_missing red highlighting; remove number\_to\_human.hundreds; add form labels [\#708](https://github.com/bensheldon/good_job/pull/708) ([bensheldon](https://github.com/bensheldon))
10
+
11
+ **Closed issues:**
12
+
13
+ - pg\_xact No Such File error in logs [\#709](https://github.com/bensheldon/good_job/issues/709)
14
+ - Broken upgrade to v3. [\#703](https://github.com/bensheldon/good_job/issues/703)
15
+
16
+ **Merged pull requests:**
17
+
18
+ - Sentry integration Docs [\#711](https://github.com/bensheldon/good_job/pull/711) ([remy727](https://github.com/remy727))
19
+ - Add an `Execution` `after_perform_unlocked` callback [\#706](https://github.com/bensheldon/good_job/pull/706) ([bensheldon](https://github.com/bensheldon))
20
+
21
+ ## [v3.4.4](https://github.com/bensheldon/good_job/tree/v3.4.4) (2022-08-20)
22
+
23
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.4.3...v3.4.4)
24
+
25
+ **Fixed bugs:**
26
+
27
+ - Keep locale param when submitting dashboard filter [\#707](https://github.com/bensheldon/good_job/pull/707) ([aki77](https://github.com/aki77))
28
+
29
+ **Merged pull requests:**
30
+
31
+ - Fix document [\#704](https://github.com/bensheldon/good_job/pull/704) ([aki77](https://github.com/aki77))
32
+ - Describe pessimistic usecases [\#702](https://github.com/bensheldon/good_job/pull/702) ([shouichi](https://github.com/shouichi))
33
+
34
+ ## [v3.4.3](https://github.com/bensheldon/good_job/tree/v3.4.3) (2022-08-15)
35
+
36
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.4.2...v3.4.3)
37
+
38
+ **Closed issues:**
39
+
40
+ - How to run multiple workers? [\#699](https://github.com/bensheldon/good_job/issues/699)
41
+ - Getting Postgres Errors on killing development server after setting up Goodjob [\#692](https://github.com/bensheldon/good_job/issues/692)
42
+
43
+ **Merged pull requests:**
44
+
45
+ - Fix Project v2 GitHub Actions [\#701](https://github.com/bensheldon/good_job/pull/701) ([bensheldon](https://github.com/bensheldon))
46
+ - Remove development dependencies: memory\_profiler, rbtrace, sigdump [\#700](https://github.com/bensheldon/good_job/pull/700) ([bensheldon](https://github.com/bensheldon))
47
+ - Allow concurrency limits to be configured dynamically with lambda/proc [\#696](https://github.com/bensheldon/good_job/pull/696) ([baka-san](https://github.com/baka-san))
48
+ - Add additional details to Concurrency Control explanation [\#695](https://github.com/bensheldon/good_job/pull/695) ([bensheldon](https://github.com/bensheldon))
49
+
3
50
  ## [v3.4.2](https://github.com/bensheldon/good_job/tree/v3.4.2) (2022-08-13)
4
51
 
5
52
  [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.4.1...v3.4.2)
@@ -18,7 +65,7 @@
18
65
 
19
66
  **Merged pull requests:**
20
67
 
21
- - Enqueues jobs with I18n default locale [\#698](https://github.com/bensheldon/good_job/pull/698) ([esasse](https://github.com/esasse))
68
+ - Enqueues Cron jobs with I18n default locale [\#698](https://github.com/bensheldon/good_job/pull/698) ([esasse](https://github.com/esasse))
22
69
 
23
70
  ## [v3.4.1](https://github.com/bensheldon/good_job/tree/v3.4.1) (2022-08-06)
24
71
 
@@ -109,8 +156,6 @@
109
156
 
110
157
  **Closed issues:**
111
158
 
112
- - Calculating database connections [\#669](https://github.com/bensheldon/good_job/issues/669)
113
- - Unable to Replace GoodJob's Logger [\#667](https://github.com/bensheldon/good_job/issues/667)
114
159
  - Readme should consistently encourage usage of `config.good_job....` instead of `GoodJob.` configuration [\#628](https://github.com/bensheldon/good_job/issues/628)
115
160
  - Improve the "Gem development" section of README? [\#551](https://github.com/bensheldon/good_job/issues/551)
116
161
  - Simplify Rails initialization to only be a mountable Engine [\#543](https://github.com/bensheldon/good_job/issues/543)
data/README.md CHANGED
@@ -232,7 +232,7 @@ Rails.application.configure do
232
232
  # Configure options individually...
233
233
  config.good_job.preserve_job_records = true
234
234
  config.good_job.retry_on_unhandled_error = false
235
- config.good_job.on_thread_error = -> (exception) { Raven.capture_exception(exception) }
235
+ config.good_job.on_thread_error = -> (exception) { Sentry.capture_exception(exception) }
236
236
  config.good_job.execution_mode = :async
237
237
  config.good_job.queues = '*'
238
238
  config.good_job.max_threads = 5
@@ -245,7 +245,7 @@ Rails.application.configure do
245
245
  config.good_job = {
246
246
  preserve_job_records: true,
247
247
  retry_on_unhandled_error: false,
248
- on_thread_error: -> (exception) { Raven.capture_exception(exception) },
248
+ on_thread_error: -> (exception) { Sentry.capture_exception(exception) },
249
249
  execution_mode: :async,
250
250
  queues: '*',
251
251
  max_threads: 5,
@@ -283,11 +283,11 @@ Available configuration options are:
283
283
  - `inline_execution_respects_schedule` (boolean) Opt-in to future behavior of inline execution respecting scheduled jobs. Defaults to `false`.
284
284
  - `logger` ([Rails Logger](https://api.rubyonrails.org/classes/ActiveSupport/Logger.html)) lets you set a custom logger for GoodJob. It should be an instance of a Rails `Logger` (Default: `Rails.logger`).
285
285
  - `preserve_job_records` (boolean) keeps job records in your database even after jobs are completed. (Default: `true`)
286
- - `retry_on_unhandled_error` (boolean) causes jobs to be re-queued and retried if they raise an instance of `StandardError`. Be advised this may lead to jobs being repeated infinitely ([see below for more on retries](#retries)). Instances of `Exception`, like SIGINT, will *always* be retried, regardless of this attribute’s value. (Default: `true`)
286
+ - `retry_on_unhandled_error` (boolean) causes jobs to be re-queued and retried if they raise an instance of `StandardError`. Be advised this may lead to jobs being repeated infinitely ([see below for more on retries](#retries)). Instances of `Exception`, like SIGINT, will *always* be retried, regardless of this attribute’s value. (Default: `false`)
287
287
  - `on_thread_error` (proc, lambda, or callable) will be called when an Exception. It can be useful for logging errors to bug tracking services, like Sentry or Airbrake. Example:
288
288
 
289
289
  ```ruby
290
- config.good_job.on_thread_error = -> (exception) { Raven.capture_exception(exception) }
290
+ config.good_job.on_thread_error = -> (exception) { Sentry.capture_exception(exception) }
291
291
  ```
292
292
 
293
293
  By default, GoodJob configures the following execution modes per environment:
@@ -406,15 +406,18 @@ class MyJob < ApplicationJob
406
406
 
407
407
  good_job_control_concurrency_with(
408
408
  # Maximum number of unfinished jobs to allow with the concurrency key
409
+ # Can be an Integer or Lambda/Proc that is invoked in the context of the job
409
410
  total_limit: 1,
410
411
 
411
412
  # Or, if more control is needed:
412
413
  # Maximum number of jobs with the concurrency key to be
413
414
  # concurrently enqueued (excludes performing jobs)
415
+ # Can be an Integer or Lambda/Proc that is invoked in the context of the job
414
416
  enqueue_limit: 2,
415
417
 
416
418
  # Maximum number of jobs with the concurrency key to be
417
419
  # concurrently performed (excludes enqueued jobs)
420
+ # Can be an Integer or Lambda/Proc that is invoked in the context of the job
418
421
  perform_limit: 1,
419
422
 
420
423
  # Note: Under heavy load, the total number of jobs may exceed the
@@ -444,10 +447,12 @@ job.good_job_concurrency_key #=> "Unique-Alice"
444
447
 
445
448
  #### How concurrency controls work
446
449
 
447
- GoodJob's concurrency control strategy for `perform_limit` is "optimistic retry with an incremental backoff". The [code is readable](https://github.com/bensheldon/good_job/blob/main/lib/good_job/active_job_extensions/concurrency.rb).
450
+ GoodJob's concurrency control strategy for `perform_limit` is "optimistic retry with an incremental backoff". The [code is readable](https://github.com/bensheldon/good_job/blob/main/lib/good_job/active_job_extensions/concurrency.rb).
448
451
 
449
- - "Optimistic" meaning that the implementation's performance trade-off assumes that collisions are atypical (e.g. two users enqueue the same job at the same time) rather than regular (e.g. the system enqueues thousands of colliding jobs at the same time).
452
+ - "Optimistic" meaning that the implementation's performance trade-off assumes that collisions are atypical (e.g. two users enqueue the same job at the same time) rather than regular (e.g. the system enqueues thousands of colliding jobs at the same time). Depending on your concurrency requirements, you may also want to manage concurrency through the number of GoodJob threads and processes that are performing a given queue.
450
453
  - "Retry with an incremental backoff" means that when `perform_limit` is exceeded, the job will raise a `GoodJob::ActiveJobExtensions::Concurrency::ConcurrencyExceededError` which is caught by a `retry_on` handler which re-schedules the job to execute in the near future with an incremental backoff.
454
+ - First-in-first-out job execution order is not preserved when a job is retried with incremental back-off.
455
+ - For pessimistic usecases that collisions are expected, use number of threads/processes (e.g., `good_job --queue "serial:1;-serial:5"`) to control concurrency. It is also a good idea to use `perform_limit` as backstop.
451
456
 
452
457
  ### Cron-style repeating/recurring jobs
453
458
 
@@ -562,7 +567,7 @@ If errors do reach GoodJob, you can assign a callable to `GoodJob.on_thread_erro
562
567
 
563
568
  ```ruby
564
569
  # config/initializers/good_job.rb
565
- GoodJob.on_thread_error = -> (exception) { Raven.capture_exception(exception) }
570
+ GoodJob.on_thread_error = -> (exception) { Sentry.capture_exception(exception) }
566
571
  ```
567
572
 
568
573
  #### Retries
@@ -596,13 +601,13 @@ class ApplicationJob < ActiveJob::Base
596
601
  retry_on StandardError, wait: :exponentially_longer, attempts: Float::INFINITY
597
602
 
598
603
  retry_on SpecialError, attempts: 5 do |_job, exception|
599
- Raven.capture_exception(exception)
604
+ Sentry.capture_exception(exception)
600
605
  end
601
606
 
602
607
  around_perform do |_job, block|
603
608
  block.call
604
609
  rescue StandardError => e
605
- Raven.capture_exception(e)
610
+ Sentry.capture_exception(e)
606
611
  raise
607
612
  end
608
613
  # ...
@@ -625,7 +630,7 @@ ActionMailer::MailDeliveryJob.retry_on StandardError, wait: :exponentially_longe
625
630
  ActionMailer::MailDeliveryJob.around_perform do |_job, block|
626
631
  block.call
627
632
  rescue StandardError => e
628
- Raven.capture_exception(e)
633
+ Sentry.capture_exception(e)
629
634
  raise
630
635
  end
631
636
  ```
@@ -35,11 +35,6 @@
35
35
  z-index: 1;
36
36
  }
37
37
 
38
- .translation_missing {
39
- background-color: red;
40
- color: white;
41
- }
42
-
43
38
  .btn-outline-secondary {
44
39
  border-color: #ced4da; /* $gray-400 */
45
40
  }
@@ -20,6 +20,8 @@ module GoodJob
20
20
  self.table_name = 'good_jobs'
21
21
  self.advisory_lockable_column = 'active_job_id'
22
22
 
23
+ define_model_callbacks :perform_unlocked, only: :after
24
+
23
25
  # Parse a string representing a group of queues into a more readable data
24
26
  # structure.
25
27
  # @param string [String] Queue string
@@ -202,13 +204,18 @@ module GoodJob
202
204
  # raised, if any (if the job raised, then the second array entry will be
203
205
  # +nil+). If there were no jobs to execute, returns +nil+.
204
206
  def self.perform_with_advisory_lock(parsed_queues: nil)
207
+ execution = nil
208
+ result = nil
205
209
  unfinished.dequeueing_ordered(parsed_queues).only_scheduled.limit(1).with_advisory_lock(unlock_session: true) do |executions|
206
210
  execution = executions.first
207
211
  break if execution.blank?
208
212
  break :unlocked unless execution&.executable?
209
213
 
210
- execution.perform
214
+ result = execution.perform
211
215
  end
216
+ execution&.run_callbacks(:perform_unlocked)
217
+
218
+ result
212
219
  end
213
220
 
214
221
  # Fetches the scheduled execution time of the next eligible Execution(s).
@@ -4,6 +4,7 @@
4
4
  <header class="list-group-item bg-light">
5
5
  <div class="row small text-muted text-uppercase align-items-center">
6
6
  <div class="col-auto">
7
+ <%= label_tag('toggle_job_ids', "Toggle all jobs", class: "visually-hidden") %>
7
8
  <%= check_box_tag('toggle_job_ids', "1", false, data: { "checkbox-toggle-all": "job_ids" }) %>
8
9
  </div>
9
10
  <div class="col-4">
@@ -5,8 +5,10 @@
5
5
  <%= form_with(url: "", method: :get, local: true, id: "filter_form", class: "container-fluid") do |form| %>
6
6
  <%= hidden_field_tag :poll, params[:poll] %>
7
7
  <%= hidden_field_tag :state, params[:state] %>
8
+ <%= hidden_field_tag :locale, params[:locale] if params[:locale] %>
8
9
  <div class="d-flex flex-row w-100">
9
10
  <div class="me-2">
11
+ <%= label_tag "job_queue_filter", "Queue name", class: "visually-hidden" %>
10
12
  <select name="queue_name" id="job_queue_filter" class="form-select form-select-sm">
11
13
  <option value="" <%= "selected='selected'" if params[:queue_name].blank? %>>All queues</option>
12
14
 
@@ -17,6 +19,7 @@
17
19
  </div>
18
20
 
19
21
  <div class="me-2">
22
+ <%= label_tag "job_class_filter", "Job name", class: "visually-hidden" %>
20
23
  <select name="job_class" id="job_class_filter" class="form-select form-select-sm">
21
24
  <option value="" <%= "selected='selected'" if params[:job_class].blank? %>>All jobs</option>
22
25
 
@@ -27,6 +30,7 @@
27
30
  </div>
28
31
 
29
32
  <div class="me-2 flex-fill">
33
+ <%= label_tag "query", "Search", class: "visually-hidden" %>
30
34
  <%= search_field_tag "query", params[:query], class: "form-control form-control-sm", placeholder: "Search by class, job id, job params, and error text." %>
31
35
  </div>
32
36
 
@@ -72,7 +72,6 @@ en:
72
72
  format: "%n%u"
73
73
  units:
74
74
  billion: B
75
- hundred: ''
76
75
  million: M
77
76
  quadrillion: Q
78
77
  thousand: K
@@ -72,7 +72,6 @@ es:
72
72
  format: "%n%u"
73
73
  units:
74
74
  billion: B
75
- hundred: ''
76
75
  million: M
77
76
  quadrillion: q
78
77
  thousand: k
@@ -72,7 +72,6 @@ nl:
72
72
  format: "%n%u"
73
73
  units:
74
74
  billion: B
75
- hundred: ''
76
75
  million: M
77
76
  quadrillion: Q
78
77
  thousand: K
@@ -96,7 +96,6 @@ ru:
96
96
  format: "%n%u"
97
97
  units:
98
98
  billion: Б
99
- hundred: ''
100
99
  million: М
101
100
  quadrillion: Q
102
101
  thousand: К
@@ -31,11 +31,17 @@ module GoodJob
31
31
  next(block.call) if CurrentThread.active_job_id == job.job_id
32
32
 
33
33
  enqueue_limit = job.class.good_job_concurrency_config[:enqueue_limit]
34
- total_limit = job.class.good_job_concurrency_config[:total_limit]
34
+ enqueue_limit = instance_exec(&enqueue_limit) if enqueue_limit.respond_to?(:call)
35
+ enqueue_limit = nil unless enqueue_limit.present? && (0...Float::INFINITY).cover?(enqueue_limit)
35
36
 
36
- has_limit = (enqueue_limit.present? && (0...Float::INFINITY).cover?(enqueue_limit)) ||
37
- (total_limit.present? && (0...Float::INFINITY).cover?(total_limit))
38
- next(block.call) unless has_limit
37
+ unless enqueue_limit
38
+ total_limit = job.class.good_job_concurrency_config[:total_limit]
39
+ total_limit = instance_exec(&total_limit) if total_limit.respond_to?(:call)
40
+ total_limit = nil unless total_limit.present? && (0...Float::INFINITY).cover?(total_limit)
41
+ end
42
+
43
+ limit = enqueue_limit || total_limit
44
+ next(block.call) unless limit
39
45
 
40
46
  # Only generate the concurrency key on the initial enqueue in case it is dynamic
41
47
  job.good_job_concurrency_key ||= job._good_job_concurrency_key
@@ -50,7 +56,7 @@ module GoodJob
50
56
  end
51
57
 
52
58
  # The job has not yet been enqueued, so check if adding it will go over the limit
53
- block.call unless enqueue_concurrency + 1 > (enqueue_limit || total_limit)
59
+ block.call unless (enqueue_concurrency + 1) > limit
54
60
  end
55
61
  end
56
62
 
@@ -64,11 +70,18 @@ module GoodJob
64
70
  # Don't attempt to enforce concurrency limits with other queue adapters.
65
71
  next unless job.class.queue_adapter.is_a?(GoodJob::Adapter)
66
72
 
67
- perform_limit = job.class.good_job_concurrency_config[:perform_limit] ||
68
- job.class.good_job_concurrency_config[:total_limit]
73
+ perform_limit = job.class.good_job_concurrency_config[:perform_limit]
74
+ perform_limit = instance_exec(&perform_limit) if perform_limit.respond_to?(:call)
75
+ perform_limit = nil unless perform_limit.present? && (0...Float::INFINITY).cover?(perform_limit)
76
+
77
+ unless perform_limit
78
+ total_limit = job.class.good_job_concurrency_config[:total_limit]
79
+ total_limit = instance_exec(&total_limit) if total_limit.respond_to?(:call)
80
+ total_limit = nil unless total_limit.present? && (0...Float::INFINITY).cover?(total_limit)
81
+ end
69
82
 
70
- has_limit = perform_limit.present? && (0...Float::INFINITY).cover?(perform_limit)
71
- next unless has_limit
83
+ limit = perform_limit || total_limit
84
+ next unless limit
72
85
 
73
86
  key = job.good_job_concurrency_key
74
87
  next if key.blank?
@@ -79,7 +92,7 @@ module GoodJob
79
92
  end
80
93
 
81
94
  GoodJob::Execution.advisory_lock_key(key, function: "pg_advisory_lock") do
82
- allowed_active_job_ids = GoodJob::Execution.unfinished.where(concurrency_key: key).advisory_locked.order(Arel.sql("COALESCE(performed_at, scheduled_at, created_at) ASC")).limit(perform_limit).pluck(:active_job_id)
95
+ allowed_active_job_ids = GoodJob::Execution.unfinished.where(concurrency_key: key).advisory_locked.order(Arel.sql("COALESCE(performed_at, scheduled_at, created_at) ASC")).limit(limit).pluck(:active_job_id)
83
96
  # The current job has already been locked and will appear in the previous query
84
97
  raise GoodJob::ActiveJobExtensions::Concurrency::ConcurrencyExceededError unless allowed_active_job_ids.include? job.job_id
85
98
  end
@@ -60,6 +60,7 @@ module GoodJob
60
60
  result = execution.perform
61
61
  ensure
62
62
  execution.advisory_unlock
63
+ execution.run_callbacks(:perform_unlocked)
63
64
  end
64
65
  raise result.unhandled_error if result.unhandled_error
65
66
  else
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  module GoodJob
3
3
  # GoodJob gem version.
4
- VERSION = '3.4.2'
4
+ VERSION = '3.4.5'
5
5
  end
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.2
4
+ version: 3.4.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Sheldon
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-08-13 00:00:00.000000000 Z
11
+ date: 2022-09-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob
@@ -290,20 +290,6 @@ dependencies:
290
290
  - - ">="
291
291
  - !ruby/object:Gem::Version
292
292
  version: '0'
293
- - !ruby/object:Gem::Dependency
294
- name: sigdump
295
- requirement: !ruby/object:Gem::Requirement
296
- requirements:
297
- - - ">="
298
- - !ruby/object:Gem::Version
299
- version: '0'
300
- type: :development
301
- prerelease: false
302
- version_requirements: !ruby/object:Gem::Requirement
303
- requirements:
304
- - - ">="
305
- - !ruby/object:Gem::Version
306
- version: '0'
307
293
  - !ruby/object:Gem::Dependency
308
294
  name: yard
309
295
  requirement: !ruby/object:Gem::Requirement