good_job 4.5.0 → 4.6.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: 3f0a8926625f33b36c56def63a1507cfb0f8981a876449e907396aa2338b0b78
4
- data.tar.gz: ad45bd8133d5f68bcf190bac3f7656864e10df2e49be477024e9f0c8422ce222
3
+ metadata.gz: 4e6676f189c0e22c751837d8792b84ece28dfcc118941077abfcdfac7e352c18
4
+ data.tar.gz: 891e630829ec28fb022d516e50d5d9c61e51dc81531ce3f20c25f2facfee09c6
5
5
  SHA512:
6
- metadata.gz: 2f2e5c4ac4020dfd7aaed53bfb9b7e7480ca91e0aa7cfcbc385ac60d91a4b08e40397734993ffdffc9d5121f40afab04540d363fef925bf8a30bf987938514ab
7
- data.tar.gz: 3460da22048d4fad135e12644c043723b2dcba8137a69a3b5bebf3b5df11e1c1a0ed0f94a8924b64cab4d1ce8b76bd55e6090a25c43f23afde41b27d398cf59a
6
+ metadata.gz: d443d783208de5f01cd003773943199c618f397a46a1ecb5d7e9167a9eaedc0511f9e4b3cec6122c17cd0be328b490230727024041b7622ad1f9f8afcaf817e1
7
+ data.tar.gz: fc1171540d6227d0e7757a09904b617791289daeec89c3045251b46e8d82959b9df4b5ecd0b4c812a2804ac7c7626712ebea93d3718984fb08a1592c097db068
data/CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  # Changelog
2
2
 
3
+ ## [v4.6.0](https://github.com/bensheldon/good_job/tree/v4.6.0) (2024-12-12)
4
+
5
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v4.5.1...v4.6.0)
6
+
7
+ **Implemented enhancements:**
8
+
9
+ - 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))
10
+
11
+ **Closed issues:**
12
+
13
+ - Attaching metadata to jobs [\#1558](https://github.com/bensheldon/good_job/issues/1558)
14
+ - Lower Ruby Thread priority for jobs by default when running in Async mode [\#1554](https://github.com/bensheldon/good_job/issues/1554)
15
+ - NoMethodError: undefined method `\<' for nil \(process.rb:125 in stale?\) [\#1363](https://github.com/bensheldon/good_job/issues/1363)
16
+ - Install PgHero on the Demo app [\#1166](https://github.com/bensheldon/good_job/issues/1166)
17
+
18
+ **Merged pull requests:**
19
+
20
+ - 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))
21
+ - Add PGHero to the demo app [\#1294](https://github.com/bensheldon/good_job/pull/1294) ([mec](https://github.com/mec))
22
+
23
+ ## [v4.5.1](https://github.com/bensheldon/good_job/tree/v4.5.1) (2024-11-29)
24
+
25
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v4.5.0...v4.5.1)
26
+
27
+ **Implemented enhancements:**
28
+
29
+ - GoodJob.cleanup\_preserved\_jobs: add :include\_discarded option [\#1550](https://github.com/bensheldon/good_job/pull/1550) ([jonleighton](https://github.com/jonleighton))
30
+
31
+ **Fixed bugs:**
32
+
33
+ - Fix compatibility with `rails-head` when duplicated advisory lockable column [\#1553](https://github.com/bensheldon/good_job/pull/1553) ([Earlopain](https://github.com/Earlopain))
34
+
35
+ **Closed issues:**
36
+
37
+ - `PG::AmbiguousColumn` after upgrade to 4.5.0 [\#1551](https://github.com/bensheldon/good_job/issues/1551)
38
+
39
+ **Merged pull requests:**
40
+
41
+ - Remove usage of COALESCE from dashboard chart [\#1306](https://github.com/bensheldon/good_job/pull/1306) ([bananatron](https://github.com/bananatron))
42
+
3
43
  ## [v4.5.0](https://github.com/bensheldon/good_job/tree/v4.5.0) (2024-11-22)
4
44
 
5
45
  [Full Changelog](https://github.com/bensheldon/good_job/compare/v4.4.2...v4.5.0)
@@ -5,7 +5,7 @@ module GoodJob
5
5
  def data
6
6
  table_name = GoodJob::Execution.table_name
7
7
 
8
- sum_query = Arel.sql(GoodJob::Job.pg_or_jdbc_query(<<~SQL.squish))
8
+ sum_query = <<~SQL.squish
9
9
  SELECT *
10
10
  FROM generate_series(
11
11
  date_trunc('hour', $1::timestamp),
@@ -21,7 +21,7 @@ module GoodJob
21
21
  table_name = GoodJob::Execution.table_name
22
22
 
23
23
  interval_entries = BUCKET_INTERVALS.map { "interval '#{_1}'" }.join(",")
24
- sum_query = Arel.sql(GoodJob::Job.pg_or_jdbc_query(<<~SQL.squish))
24
+ sum_query = <<~SQL.squish
25
25
  SELECT
26
26
  WIDTH_BUCKET(duration, ARRAY[#{interval_entries}]) as bucket_index,
27
27
  COUNT(WIDTH_BUCKET(duration, ARRAY[#{interval_entries}])) AS count
@@ -8,9 +8,7 @@ module GoodJob
8
8
  end
9
9
 
10
10
  def data
11
- table_name = GoodJob::Job.table_name
12
-
13
- count_query = Arel.sql(GoodJob::Job.pg_or_jdbc_query(<<~SQL.squish))
11
+ count_query = <<~SQL.squish
14
12
  SELECT *
15
13
  FROM generate_series(
16
14
  date_trunc('hour', $1::timestamp),
@@ -23,7 +21,7 @@ module GoodJob
23
21
  queue_name,
24
22
  count(*) AS count
25
23
  FROM (
26
- #{@filter.filtered_query.except(:select, :order).select('queue_name', "COALESCE(#{table_name}.scheduled_at, #{table_name}.created_at)::timestamp AS scheduled_at").to_sql}
24
+ #{@filter.filtered_query.except(:select, :order).select(:queue_name, :scheduled_at).to_sql}
27
25
  ) sources
28
26
  GROUP BY date_trunc('hour', scheduled_at), queue_name
29
27
  ) sources ON sources.scheduled_at = timestamp
@@ -41,8 +41,16 @@ module GoodJob
41
41
  scope :advisory_lock, (lambda do |column: _advisory_lockable_column, function: advisory_lockable_function, select_limit: nil|
42
42
  original_query = self
43
43
 
44
+ primary_key_for_select = primary_key.to_sym
45
+ column_for_select = column.to_sym
46
+
44
47
  cte_table = Arel::Table.new(:rows)
45
- cte_query = original_query.select(primary_key, column).except(:limit)
48
+ cte_query = original_query.except(:limit)
49
+ cte_query = if primary_key_for_select == column_for_select
50
+ cte_query.select(primary_key_for_select)
51
+ else
52
+ cte_query.select(primary_key_for_select, column_for_select)
53
+ end
46
54
  cte_query = cte_query.limit(select_limit) if select_limit
47
55
  cte_type = supports_cte_materialization_specifiers? ? :MATERIALIZED : :""
48
56
  composed_cte = Arel::Nodes::As.new(cte_table, Arel::Nodes::UnaryOperation.new(cte_type, cte_query.arel))
@@ -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.0'
5
+ VERSION = '4.6.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
@@ -203,11 +203,11 @@ module GoodJob
203
203
  # If you are preserving job records this way, use this method regularly to
204
204
  # destroy old records and preserve space in your database.
205
205
  # @param older_than [nil,Numeric,ActiveSupport::Duration] Jobs older than this will be destroyed (default: +86400+).
206
+ # @param include_discarded [Boolean] Whether or not to destroy discarded jobs (default: per +cleanup_discarded_jobs+ config option)
206
207
  # @return [Integer] Number of job execution records and batches that were destroyed.
207
- def self.cleanup_preserved_jobs(older_than: nil, in_batches_of: 1_000)
208
+ def self.cleanup_preserved_jobs(older_than: nil, in_batches_of: 1_000, include_discarded: GoodJob.configuration.cleanup_discarded_jobs?)
208
209
  older_than ||= GoodJob.configuration.cleanup_preserved_jobs_before_seconds_ago
209
210
  timestamp = Time.current - older_than
210
- include_discarded = GoodJob.configuration.cleanup_discarded_jobs?
211
211
 
212
212
  ActiveSupport::Notifications.instrument("cleanup_preserved_jobs.good_job", { older_than: older_than, timestamp: timestamp }) do |payload|
213
213
  deleted_jobs_count = 0
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.0
4
+ version: 4.6.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-22 00:00:00.000000000 Z
11
+ date: 2024-12-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob