good_job 4.5.1 → 4.7.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: 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