good_job 4.5.1 → 4.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6c4f55ae789dad6bc4920527bdb107d7bc4422cbaec192dfc036b01ada67c54c
4
- data.tar.gz: 8757dec952a510559eae0f7bf1a3210128b72e21d6ed3d17763507b47c4628e8
3
+ metadata.gz: 00f71f047b33832b758797f8fb9c7987a13eae1abb1750467dcbbab12c7680b0
4
+ data.tar.gz: 4a9f501a236f45781904d2468cef99d05cea42ac5d76568d8b72ae437823e3e6
5
5
  SHA512:
6
- metadata.gz: da3bf2a776ffca2181d2d205250e015bc138bc23a95004a65c703631d195e1891bd9eafc86733017dbc10233fb1b978615dadac4cbcb384ce9823c28f7b5e36f
7
- data.tar.gz: a32557214eb62cc0cecb1d17750e83f074de0aef958b771c4846ff7e543432a1e9d474d0f922a241880b21beb07f96e6397bf0445f12286f169d5891cd61496c
6
+ metadata.gz: 575e7338adaaaa661f4f0d219ddbaac20dff3547f9878098d51ab71a54dce5660467d2f45d492dd6fa79496143f6482e170557a7b8a230e25853297c9811eba6
7
+ data.tar.gz: 5bdd469825d81387e4071900576ea664e820c98c74f7d6a710d3d39fc5c750af4db0a63fc4d0e0af9faf5481e73f1b6107b3ff88c77a257c9c49d02dda7667f8
data/CHANGELOG.md CHANGED
@@ -1,5 +1,38 @@
1
1
  # Changelog
2
2
 
3
+ ## [v4.7.0](https://github.com/bensheldon/good_job/tree/v4.7.0) (2024-12-31)
4
+
5
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v4.6.0...v4.7.0)
6
+
7
+ **Implemented enhancements:**
8
+
9
+ - Expose good job labels in dashboard [\#1561](https://github.com/bensheldon/good_job/pull/1561) ([BClark88](https://github.com/BClark88))
10
+
11
+ **Merged pull requests:**
12
+
13
+ - Allow cron configuration `class` value to be a proc [\#1566](https://github.com/bensheldon/good_job/pull/1566) ([bensheldon](https://github.com/bensheldon))
14
+ - Add Ruby 3.4 to CI [\#1565](https://github.com/bensheldon/good_job/pull/1565) ([Earlopain](https://github.com/Earlopain))
15
+
16
+ ## [v4.6.0](https://github.com/bensheldon/good_job/tree/v4.6.0) (2024-12-12)
17
+
18
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v4.5.1...v4.6.0)
19
+
20
+ **Implemented enhancements:**
21
+
22
+ - Set job execution thread priority to `-3` when in async mode [\#1560](https://github.com/bensheldon/good_job/pull/1560) ([bensheldon](https://github.com/bensheldon))
23
+
24
+ **Closed issues:**
25
+
26
+ - Attaching metadata to jobs [\#1558](https://github.com/bensheldon/good_job/issues/1558)
27
+ - Lower Ruby Thread priority for jobs by default when running in Async mode [\#1554](https://github.com/bensheldon/good_job/issues/1554)
28
+ - NoMethodError: undefined method `\<' for nil \(process.rb:125 in stale?\) [\#1363](https://github.com/bensheldon/good_job/issues/1363)
29
+ - Install PgHero on the Demo app [\#1166](https://github.com/bensheldon/good_job/issues/1166)
30
+
31
+ **Merged pull requests:**
32
+
33
+ - Bump rails-html-sanitizer from 1.6.0 to 1.6.1 [\#1557](https://github.com/bensheldon/good_job/pull/1557) ([dependabot[bot]](https://github.com/apps/dependabot))
34
+ - Add PGHero to the demo app [\#1294](https://github.com/bensheldon/good_job/pull/1294) ([mec](https://github.com/mec))
35
+
3
36
  ## [v4.5.1](https://github.com/bensheldon/good_job/tree/v4.5.1) (2024-11-29)
4
37
 
5
38
  [Full Changelog](https://github.com/bensheldon/good_job/compare/v4.5.0...v4.5.1)
@@ -101,8 +101,12 @@ module GoodJob # :nodoc:
101
101
  current_thread.cron_key = key
102
102
  current_thread.cron_at = cron_at
103
103
 
104
- configured_job = job_class.constantize.set(set_value)
105
104
  I18n.with_locale(I18n.default_locale) do
105
+ job_klass = job_class_value
106
+ job_klass = job_klass.constantize if job_klass.is_a?(String)
107
+ next unless job_klass.is_a?(Class)
108
+
109
+ configured_job = job_klass.set(set_value)
106
110
  kwargs_value.present? ? configured_job.perform_later(*args_value, **kwargs_value) : configured_job.perform_later(*args_value)
107
111
  end
108
112
  end
@@ -118,6 +122,7 @@ module GoodJob # :nodoc:
118
122
  set: display_property(set),
119
123
  description: display_property(description),
120
124
  }.tap do |properties|
125
+ properties[:class] = display_property(job_class) if job_class.present?
121
126
  properties[:args] = display_property(args) if args.present?
122
127
  properties[:kwargs] = display_property(kwargs) if kwargs.present?
123
128
  end
@@ -160,6 +165,11 @@ module GoodJob # :nodoc:
160
165
  @_fugit ||= Fugit.parse(cron)
161
166
  end
162
167
 
168
+ def job_class_value
169
+ value = job_class || nil
170
+ value.respond_to?(:call) ? value.call : value
171
+ end
172
+
163
173
  def set_value
164
174
  value = set || {}
165
175
  value.respond_to?(:call) ? value.call : value
@@ -5,6 +5,7 @@
5
5
  <div class="col-4"><%= t("good_job.models.batch.jobs") %></div>
6
6
  <div class="d-none d-lg-block col-lg-1 text-lg-center"><%= t "good_job.models.job.queue" %></div>
7
7
  <div class="d-none d-lg-block col-lg-1 text-lg-end"><%= t "good_job.models.job.priority" %></div>
8
+ <div class="d-none d-lg-block col-lg-1 text-lg-end"><%= t "good_job.models.job.labels" %></div>
8
9
  <div class="d-none d-lg-block col-lg-1 text-lg-end"><%= t "good_job.models.job.attempts" %></div>
9
10
  <div class="col text-end">
10
11
  <%= tag.button type: "button", class: "btn btn-sm text-muted", role: "button",
@@ -33,6 +34,12 @@
33
34
  <div class="d-lg-none small text-muted mt-1"><%= t "good_job.models.job.priority" %></div>
34
35
  <span class="font-monospace fw-bold"><%= job.priority %></span>
35
36
  </div>
37
+ <div class="col-4 col-lg-1 text-lg-end">
38
+ <div class="d-lg-none small text-muted mt-1"><%= t "good_job.models.job.labels" %></div>
39
+ <% job.labels&.each do |label| %>
40
+ <span class="badge rounded-pill bg-secondary font-monospace"><%= label %></span>
41
+ <% end %>
42
+ </div>
36
43
  <div class="col-4 col-lg-1 text-lg-end">
37
44
  <div class="d-lg-none small text-muted mt-1"><%= t "good_job.models.job.attempts" %></div>
38
45
  <% if job.error %>
@@ -35,6 +35,7 @@
35
35
  </div>
36
36
  <div class="d-none d-lg-block col-lg-1 text-lg-center"><%= t "good_job.models.job.queue" %></div>
37
37
  <div class="d-none d-lg-block col-lg-1 text-lg-end"><%= t "good_job.models.job.priority" %></div>
38
+ <div class="d-none d-lg-block col-lg-1 text-lg-end"><%= t "good_job.models.job.labels" %></div>
38
39
  <div class="d-none d-lg-block col-lg-1 text-lg-end"><%= t "good_job.models.job.attempts" %></div>
39
40
  <div class="col text-end">
40
41
  <%= tag.button type: "button", class: "btn btn-sm text-muted", role: "button",
@@ -82,6 +83,12 @@
82
83
  <div class="d-lg-none small text-muted mt-1"><%= t "good_job.models.job.priority" %></div>
83
84
  <%= job.priority %>
84
85
  </div>
86
+ <div class="col-4 col-lg-1 text-wrap text-lg-end">
87
+ <div class="d-lg-none small text-muted mt-1"><%= t "good_job.models.job.labels" %></div>
88
+ <% job.labels&.each do |label| %>
89
+ <span class="badge rounded-pill bg-secondary font-monospace"><%= label %></span>
90
+ <% end %>
91
+ </div>
85
92
  <div class="col-4 col-lg-1 text-lg-end">
86
93
  <div class="d-lg-none small text-muted mt-1"><%= t "good_job.models.job.attempts" %></div>
87
94
  <% if job.error %>
@@ -194,6 +194,7 @@ de:
194
194
  job:
195
195
  arguments: Argumente
196
196
  attempts: Versuche
197
+ labels: Etiketten
197
198
  priority: Priorität
198
199
  queue: Warteschlange
199
200
  number:
@@ -194,6 +194,7 @@ en:
194
194
  job:
195
195
  arguments: Arguments
196
196
  attempts: Attempts
197
+ labels: Labels
197
198
  priority: Priority
198
199
  queue: Queue
199
200
  number:
@@ -194,6 +194,7 @@ es:
194
194
  job:
195
195
  arguments: Argumentos
196
196
  attempts: Intentos
197
+ labels: Etiquetas
197
198
  priority: Prioridad
198
199
  queue: Cola
199
200
  number:
@@ -194,6 +194,7 @@ fr:
194
194
  job:
195
195
  arguments: Paramètres
196
196
  attempts: Tentatives
197
+ labels: Étiquettes
197
198
  priority: Priorité
198
199
  queue: File d'attente
199
200
  number:
@@ -194,6 +194,7 @@ it:
194
194
  job:
195
195
  arguments: Argomenti
196
196
  attempts: Tentativi
197
+ labels: Etichette
197
198
  priority: Priorità
198
199
  queue: Coda
199
200
  number:
@@ -194,6 +194,7 @@ ja:
194
194
  job:
195
195
  arguments: 引数
196
196
  attempts: 試行回数
197
+ labels: ラベル
197
198
  priority: 優先度
198
199
  queue: キュー
199
200
  number:
@@ -194,6 +194,7 @@ ko:
194
194
  job:
195
195
  arguments: 인수
196
196
  attempts: 시도 횟수
197
+ labels: 레이블
197
198
  priority: 우선순위
198
199
  queue: 큐
199
200
  number:
@@ -194,6 +194,7 @@ nl:
194
194
  job:
195
195
  arguments: Argumenten
196
196
  attempts: Pogingen
197
+ labels: Labels
197
198
  priority: Prioriteit
198
199
  queue: Wachtrij
199
200
  number:
@@ -194,6 +194,7 @@ pt-BR:
194
194
  job:
195
195
  arguments: Argumentos
196
196
  attempts: Tentativas
197
+ labels: Rótulos
197
198
  priority: Prioridade
198
199
  queue: Fila
199
200
  number:
@@ -220,6 +220,7 @@ ru:
220
220
  job:
221
221
  arguments: Параметры
222
222
  attempts: Попытки
223
+ labels: Метки
223
224
  priority: Приоритет
224
225
  queue: Очередь исполнения
225
226
  number:
@@ -194,6 +194,7 @@ tr:
194
194
  job:
195
195
  arguments: Argümanlar
196
196
  attempts: Denemeler
197
+ labels: Etiketler
197
198
  priority: Öncelik
198
199
  queue: Kuyruk
199
200
  number:
@@ -220,6 +220,7 @@ uk:
220
220
  job:
221
221
  arguments: Аргументи
222
222
  attempts: Спроби
223
+ labels: Мітки
223
224
  priority: Пріоритет
224
225
  queue: Черга
225
226
  number:
@@ -216,6 +216,7 @@ module GoodJob
216
216
  return unless execute_async?
217
217
 
218
218
  @capsule.start
219
+ @capsule.lower_thread_priority = true if GoodJob.configuration.lower_thread_priority.in?([true, nil])
219
220
  @_async_started = true
220
221
  end
221
222
 
@@ -24,6 +24,7 @@ module GoodJob
24
24
 
25
25
  @shared_executor = GoodJob::SharedExecutor.new
26
26
  @tracker = GoodJob::CapsuleTracker.new(executor: @shared_executor)
27
+ @lower_thread_priority = nil
27
28
 
28
29
  self.class.instances << self
29
30
  end
@@ -38,7 +39,9 @@ module GoodJob
38
39
 
39
40
  @notifier = GoodJob::Notifier.new(enable_listening: configuration.enable_listen_notify, capsule: self, executor: @shared_executor)
40
41
  @poller = GoodJob::Poller.new(poll_interval: configuration.poll_interval)
41
- @multi_scheduler = GoodJob::MultiScheduler.from_configuration(configuration, capsule: self, warm_cache_on_initialize: true)
42
+ @multi_scheduler = GoodJob::MultiScheduler.from_configuration(configuration, capsule: self, warm_cache_on_initialize: true).tap do |multischeduler|
43
+ multischeduler.lower_thread_priority = @lower_thread_priority unless @lower_thread_priority.nil?
44
+ end
42
45
  @notifier.recipients.push([@multi_scheduler, :create_thread])
43
46
  @poller.recipients.push(-> { @multi_scheduler.create_thread({ fanout: true }) })
44
47
 
@@ -110,6 +113,11 @@ module GoodJob
110
113
  @tracker.process_id
111
114
  end
112
115
 
116
+ def lower_thread_priority=(value)
117
+ @lower_thread_priority = value
118
+ @multi_scheduler&.lower_thread_priority = value
119
+ end
120
+
113
121
  private
114
122
 
115
123
  def configuration
@@ -383,6 +383,14 @@ module GoodJob
383
383
  end || false
384
384
  end
385
385
 
386
+ def lower_thread_priority
387
+ return options[:lower_thread_priority] unless options[:lower_thread_priority].nil?
388
+ return rails_config[:lower_thread_priority] unless rails_config[:lower_thread_priority].nil?
389
+ return ActiveModel::Type::Boolean.new.cast(env['GOOD_JOB_LOWER_THREAD_PRIORITY']) unless env['GOOD_JOB_LOWER_THREAD_PRIORITY'].nil?
390
+
391
+ nil
392
+ end
393
+
386
394
  # Whether to take an advisory lock on the process record in the notifier reactor.
387
395
  # @return [Boolean]
388
396
  def advisory_lock_heartbeat
@@ -19,7 +19,8 @@ module GoodJob
19
19
  max_cache: configuration.max_cache,
20
20
  warm_cache_on_initialize: warm_cache_on_initialize,
21
21
  cleanup_interval_seconds: configuration.cleanup_interval_seconds,
22
- cleanup_interval_jobs: configuration.cleanup_interval_jobs
22
+ cleanup_interval_jobs: configuration.cleanup_interval_jobs,
23
+ lower_thread_priority: configuration.lower_thread_priority
23
24
  )
24
25
  end
25
26
 
@@ -85,6 +86,12 @@ module GoodJob
85
86
  end
86
87
  end
87
88
 
89
+ def lower_thread_priority=(value)
90
+ schedulers.each do |scheduler|
91
+ scheduler.lower_thread_priority = value
92
+ end
93
+ end
94
+
88
95
  def stats
89
96
  scheduler_stats = schedulers.map(&:stats)
90
97
 
@@ -29,6 +29,9 @@ module GoodJob # :nodoc:
29
29
  fallback_policy: :discard,
30
30
  }.freeze
31
31
 
32
+ # In CRuby, this sets the thread quantum to ~12.5ms ( 100ms * 2^(-3) ).
33
+ LOW_THREAD_PRIORITY = -3
34
+
32
35
  # @!attribute [r] instances
33
36
  # @!scope class
34
37
  # List of all instantiated Schedulers in the current process.
@@ -39,13 +42,18 @@ module GoodJob # :nodoc:
39
42
  # @return [String]
40
43
  attr_reader :name
41
44
 
45
+ # Whether to lower the thread priority to a fixed value
46
+ # @return [Boolean]
47
+ attr_accessor :lower_thread_priority
48
+
42
49
  # @param performer [GoodJob::JobPerformer]
43
50
  # @param max_threads [Numeric, nil] number of seconds between polls for jobs
44
51
  # @param max_cache [Numeric, nil] maximum number of scheduled jobs to cache in memory
45
52
  # @param warm_cache_on_initialize [Boolean] whether to warm the cache immediately, or manually by calling +warm_cache+
46
53
  # @param cleanup_interval_seconds [Numeric, nil] number of seconds between cleaning up job records
47
54
  # @param cleanup_interval_jobs [Numeric, nil] number of executed jobs between cleaning up job records
48
- def initialize(performer, max_threads: nil, max_cache: nil, warm_cache_on_initialize: false, cleanup_interval_seconds: nil, cleanup_interval_jobs: nil)
55
+ # @param lower_thread_priority [Boolean] whether to lower the thread priority of execution threads
56
+ def initialize(performer, max_threads: nil, max_cache: nil, warm_cache_on_initialize: false, cleanup_interval_seconds: nil, cleanup_interval_jobs: nil, lower_thread_priority: false)
49
57
  raise ArgumentError, "Performer argument must implement #next" unless performer.respond_to?(:next)
50
58
 
51
59
  @performer = performer
@@ -62,6 +70,8 @@ module GoodJob # :nodoc:
62
70
  @cleanup_tracker = CleanupTracker.new(cleanup_interval_seconds: cleanup_interval_seconds, cleanup_interval_jobs: cleanup_interval_jobs)
63
71
  @executor_options[:name] = name
64
72
 
73
+ self.lower_thread_priority = lower_thread_priority
74
+
65
75
  create_executor
66
76
  warm_cache if warm_cache_on_initialize
67
77
  self.class.instances << self
@@ -271,6 +281,7 @@ module GoodJob # :nodoc:
271
281
  future = Concurrent::ScheduledTask.new(delay, args: [self, performer], executor: executor, timer_set: timer_set) do |thr_scheduler, thr_performer|
272
282
  Thread.current.name = Thread.current.name.sub("-worker-", "-thread-") if Thread.current.name
273
283
  Thread.current[:good_job_scheduler] = thr_scheduler
284
+ Thread.current.priority = -3 if thr_scheduler.lower_thread_priority
274
285
 
275
286
  Rails.application.reloader.wrap do
276
287
  thr_performer.next do |found|
@@ -2,7 +2,7 @@
2
2
 
3
3
  module GoodJob
4
4
  # GoodJob gem version.
5
- VERSION = '4.5.1'
5
+ VERSION = '4.7.0'
6
6
 
7
7
  # GoodJob version as Gem::Version object
8
8
  GEM_VERSION = Gem::Version.new(VERSION)
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: 4.5.1
4
+ version: 4.7.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-29 00:00:00.000000000 Z
11
+ date: 2024-12-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob