good_job 3.21.2 → 3.21.3

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: dd97ba03dea3561d77db9ebb2f534d03aafed6e39b2327429c5e5eea3f971d29
4
- data.tar.gz: b83947c459532713022c32d2845e8e139113cad8dc91012a4eeb45ed20e08f3b
3
+ metadata.gz: b2b769f6444bb3572bd5739f046469179945f8fc6f13c9fa5bd3de03b25e8e43
4
+ data.tar.gz: c7b0cd19944dea29a8cb75dce00ed92434879d08f07718781d91123a295f6a11
5
5
  SHA512:
6
- metadata.gz: 349b6201b1add40178d726aaaf62887b0e89a36fed4f2546838d2251e7e593ce60e24ca4f08eac8c0067fd57c095f8ed1620bf2987805c5eac9fc7f327129b5d
7
- data.tar.gz: b29eaf8018d61233b4245bbdf3fdc8204d8390995a583044166e25d824abd5db47f0d60c30b3ab9c1782387f2d9114d0eebf94d7aee9076bc819e719743ea659
6
+ metadata.gz: fff83dabf6211e76b3439bd1a6486ba2009bb5fc9ae689673deefbf5728de44245e1a982e9d56c524dc07780d6c831328765e2c3c8b3aaef950f7969ffdb1a15
7
+ data.tar.gz: 289f6573c18d539a7ca6918d8288fdf6f510732df036138f44a816e1bebae53529695f766146488fda2967aa120b97e4d938ea0453a69b91c636286064dbec61
data/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # Changelog
2
2
 
3
+ ## [v3.21.3](https://github.com/bensheldon/good_job/tree/v3.21.3) (2023-12-10)
4
+
5
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.21.2...v3.21.3)
6
+
7
+ **Implemented enhancements:**
8
+
9
+ - Add `--idle-timeout` CLI option to create temporary processes that exit when inactive [\#1159](https://github.com/bensheldon/good_job/pull/1159) ([bensheldon](https://github.com/bensheldon))
10
+
11
+ **Fixed bugs:**
12
+
13
+ - Add correct paths to $LOAD\_PATH [\#1169](https://github.com/bensheldon/good_job/pull/1169) ([jklina](https://github.com/jklina))
14
+ - Recreate cron indexes to be conditional [\#1163](https://github.com/bensheldon/good_job/pull/1163) ([defkode](https://github.com/defkode))
15
+
16
+ **Closed issues:**
17
+
18
+ - Use partial indices for cron\_key? [\#1161](https://github.com/bensheldon/good_job/issues/1161)
19
+ - Mass Update Error [\#1157](https://github.com/bensheldon/good_job/issues/1157)
20
+ - v3 roadmap plan [\#705](https://github.com/bensheldon/good_job/issues/705)
21
+ - Allow customisation of the dashboard controller parent class [\#687](https://github.com/bensheldon/good_job/issues/687)
22
+
23
+ **Merged pull requests:**
24
+
25
+ - \[minor\] Use symbol form of index name [\#1171](https://github.com/bensheldon/good_job/pull/1171) ([andyatkinson](https://github.com/andyatkinson))
26
+ - Fix development schema.rb to include conditional index name change [\#1168](https://github.com/bensheldon/good_job/pull/1168) ([bensheldon](https://github.com/bensheldon))
27
+ - Create new conditional Cron indexes before dropping old indexes [\#1165](https://github.com/bensheldon/good_job/pull/1165) ([bensheldon](https://github.com/bensheldon))
28
+ - Fix test that references Rails logger for Rails 7.2a change [\#1160](https://github.com/bensheldon/good_job/pull/1160) ([bensheldon](https://github.com/bensheldon))
29
+
3
30
  ## [v3.21.2](https://github.com/bensheldon/good_job/tree/v3.21.2) (2023-11-24)
4
31
 
5
32
  [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.21.1...v3.21.2)
data/README.md CHANGED
@@ -184,6 +184,7 @@ Options:
184
184
  [--shutdown-timeout=SECONDS] # Number of seconds to wait for jobs to finish when shutting down before stopping the thread. (env var: GOOD_JOB_SHUTDOWN_TIMEOUT, default: -1 (forever))
185
185
  [--enable-cron] # Whether to run cron process (default: false)
186
186
  [--enable-listen-notify] # Whether to enqueue and read jobs with Postgres LISTEN/NOTIFY (default: true)
187
+ [--idle-timeout=SECONDS] # Exit process when no jobs have been performed for this many seconds (env var: GOOD_JOB_IDLE_TIMEOUT, default: nil)
187
188
  [--daemonize] # Run as a background daemon (default: false)
188
189
  [--pidfile=PIDFILE] # Path to write daemonized Process ID (env var: GOOD_JOB_PIDFILE, default: tmp/pids/good_job.pid)
189
190
  [--probe-port=PORT] # Port for http health check (env var: GOOD_JOB_PROBE_PORT, default: nil)
@@ -70,12 +70,12 @@ class CreateGoodJobs < ActiveRecord::Migration<%= migration_version %>
70
70
  t.index :key, unique: true
71
71
  end
72
72
 
73
- add_index :good_jobs, :scheduled_at, where: "(finished_at IS NULL)", name: "index_good_jobs_on_scheduled_at"
73
+ add_index :good_jobs, :scheduled_at, where: "(finished_at IS NULL)", name: :index_good_jobs_on_scheduled_at
74
74
  add_index :good_jobs, [:queue_name, :scheduled_at], where: "(finished_at IS NULL)", name: :index_good_jobs_on_queue_name_and_scheduled_at
75
75
  add_index :good_jobs, [:active_job_id, :created_at], name: :index_good_jobs_on_active_job_id_and_created_at
76
76
  add_index :good_jobs, :concurrency_key, where: "(finished_at IS NULL)", name: :index_good_jobs_on_concurrency_key_when_unfinished
77
- add_index :good_jobs, [:cron_key, :created_at], name: :index_good_jobs_on_cron_key_and_created_at
78
- add_index :good_jobs, [:cron_key, :cron_at], name: :index_good_jobs_on_cron_key_and_cron_at, unique: true
77
+ 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
+ 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
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 },
@@ -28,7 +28,7 @@ class CreateGoodJobs < ActiveRecord::Migration<%= migration_version %>
28
28
  t.jsonb :state
29
29
  end
30
30
 
31
- add_index :good_jobs, :scheduled_at, where: "(finished_at IS NULL)", name: "index_good_jobs_on_scheduled_at"
31
+ add_index :good_jobs, :scheduled_at, where: "(finished_at IS NULL)", name: :index_good_jobs_on_scheduled_at
32
32
  add_index :good_jobs, [:queue_name, :scheduled_at], where: "(finished_at IS NULL)", name: :index_good_jobs_on_queue_name_and_scheduled_at
33
33
  add_index :good_jobs, [:active_job_id, :created_at], name: :index_good_jobs_on_active_job_id_and_created_at
34
34
  add_index :good_jobs, :concurrency_key, where: "(finished_at IS NULL)", name: :index_good_jobs_on_concurrency_key_when_unfinished
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ class RecreateGoodJobCronIndexesWithConditional < 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_cron_key_and_created_at_cond)
10
+ add_index :good_jobs, [:cron_key, :created_at], where: "(cron_key IS NOT NULL)",
11
+ name: :index_good_jobs_on_cron_key_and_created_at_cond, algorithm: :concurrently
12
+ end
13
+ unless connection.index_name_exists?(:good_jobs, :index_good_jobs_on_cron_key_and_cron_at_cond)
14
+ add_index :good_jobs, [:cron_key, :cron_at], where: "(cron_key IS NOT NULL)", unique: true,
15
+ name: :index_good_jobs_on_cron_key_and_cron_at_cond, algorithm: :concurrently
16
+ end
17
+
18
+ if connection.index_name_exists?(:good_jobs, :index_good_jobs_on_cron_key_and_created_at)
19
+ remove_index :good_jobs, name: :index_good_jobs_on_cron_key_and_created_at
20
+ end
21
+ if connection.index_name_exists?(:good_jobs, :index_good_jobs_on_cron_key_and_cron_at)
22
+ remove_index :good_jobs, name: :index_good_jobs_on_cron_key_and_cron_at
23
+ end
24
+ end
25
+
26
+ dir.down do
27
+ unless connection.index_name_exists?(:good_jobs, :index_good_jobs_on_cron_key_and_created_at)
28
+ add_index :good_jobs, [:cron_key, :created_at], where: "(cron_key IS NOT NULL)",
29
+ name: :index_good_jobs_on_cron_key_and_created_at, algorithm: :concurrently
30
+ end
31
+ unless connection.index_name_exists?(:good_jobs, :index_good_jobs_on_cron_key_and_cron_at)
32
+ add_index :good_jobs, [:cron_key, :cron_at], where: "(cron_key IS NOT NULL)", unique: true,
33
+ name: :index_good_jobs_on_cron_key_and_cron_at, algorithm: :concurrently
34
+ end
35
+
36
+ if connection.index_name_exists?(:good_jobs, :index_good_jobs_on_cron_key_and_created_at_cond)
37
+ remove_index :good_jobs, name: :index_good_jobs_on_cron_key_and_created_at_cond
38
+ end
39
+ if connection.index_name_exists?(:good_jobs, :index_good_jobs_on_cron_key_and_cron_at_cond)
40
+ remove_index :good_jobs, name: :index_good_jobs_on_cron_key_and_cron_at_cond
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -15,7 +15,7 @@ module GoodJob
15
15
  def initialize(configuration: GoodJob.configuration)
16
16
  @configuration = configuration
17
17
  @startable = true
18
- @running = false
18
+ @started_at = nil
19
19
  @mutex = Mutex.new
20
20
 
21
21
  self.class.instances << self
@@ -39,7 +39,7 @@ module GoodJob
39
39
  @cron_manager = GoodJob::CronManager.new(@configuration.cron_entries, start_on_initialize: true, executor: @shared_executor.executor) if @configuration.enable_cron?
40
40
 
41
41
  @startable = false
42
- @running = true
42
+ @started_at = Time.current
43
43
  end
44
44
  end
45
45
 
@@ -54,7 +54,7 @@ module GoodJob
54
54
  timeout = @configuration.shutdown_timeout if timeout == NONE
55
55
  GoodJob._shutdown_all([@shared_executor, @notifier, @poller, @multi_scheduler, @cron_manager].compact, timeout: timeout)
56
56
  @startable = false
57
- @running = false
57
+ @started_at = nil
58
58
  end
59
59
 
60
60
  # Shutdown and then start the capsule again.
@@ -69,7 +69,7 @@ module GoodJob
69
69
 
70
70
  # @return [Boolean] Whether the capsule is currently running.
71
71
  def running?
72
- @running
72
+ @started_at.present?
73
73
  end
74
74
 
75
75
  # @return [Boolean] Whether the capsule has been shutdown.
@@ -77,6 +77,20 @@ module GoodJob
77
77
  [@shared_executor, @notifier, @poller, @multi_scheduler, @cron_manager].compact.all?(&:shutdown?)
78
78
  end
79
79
 
80
+ # @param duration [nil, Numeric] Length of idleness to check for (in seconds).
81
+ # @return [Boolean] Whether the capsule is idle
82
+ def idle?(duration = nil)
83
+ scheduler_stats = @multi_scheduler&.stats || {}
84
+ is_idle = scheduler_stats.fetch(:active_execution_thread_count, 0).zero?
85
+
86
+ if is_idle && duration
87
+ active_at = scheduler_stats.fetch(:execution_at, nil) || @started_at
88
+ active_at.nil? || (Time.current - active_at >= duration)
89
+ else
90
+ is_idle
91
+ end
92
+ end
93
+
80
94
  # Creates an execution thread(s) with the given attributes.
81
95
  # @param job_state [Hash, nil] See {GoodJob::Scheduler#create_thread}.
82
96
  # @return [Boolean, nil] Whether the thread was created.
@@ -88,7 +102,7 @@ module GoodJob
88
102
  private
89
103
 
90
104
  def startable?(force: false)
91
- !@running && (@startable || force)
105
+ !@started_at && (@startable || force)
92
106
  end
93
107
  end
94
108
  end
data/lib/good_job/cli.rb CHANGED
@@ -20,6 +20,9 @@ module GoodJob
20
20
  # Number of seconds between checking shutdown conditions
21
21
  SHUTDOWN_EVENT_TIMEOUT = 10
22
22
 
23
+ # Number of seconds between checking shutdown conditions when idle-timeout is enabled
24
+ SHUTDOWN_EVENT_TIMEOUT_FOR_IDLE_TIMEOUT = 1
25
+
23
26
  class << self
24
27
  # Whether the CLI is running from the executable
25
28
  # @return [Boolean, nil]
@@ -74,6 +77,10 @@ module GoodJob
74
77
  type: :numeric,
75
78
  banner: 'SECONDS',
76
79
  desc: "Number of seconds to wait for jobs to finish when shutting down before stopping the thread. (env var: GOOD_JOB_SHUTDOWN_TIMEOUT, default: -1 (forever))"
80
+ method_option :idle_timeout,
81
+ type: :numeric,
82
+ banner: 'SECONDS',
83
+ desc: 'Exit process when no jobs have been performed for this many seconds (env var: GOOD_JOB_IDLE_TIMEOUT, default: nil)'
77
84
  method_option :enable_cron,
78
85
  type: :boolean,
79
86
  desc: "Whether to run cron process (default: false)"
@@ -115,9 +122,10 @@ module GoodJob
115
122
  trap(signal) { Thread.new { @stop_good_job_executable.set }.join }
116
123
  end
117
124
 
125
+ loop_wait = configuration.idle_timeout ? SHUTDOWN_EVENT_TIMEOUT_FOR_IDLE_TIMEOUT : SHUTDOWN_EVENT_TIMEOUT
118
126
  Kernel.loop do
119
- @stop_good_job_executable.wait(SHUTDOWN_EVENT_TIMEOUT)
120
- break if @stop_good_job_executable.set? || capsule.shutdown?
127
+ @stop_good_job_executable.wait(loop_wait)
128
+ break if @stop_good_job_executable.set? || capsule.shutdown? || (configuration.idle_timeout && capsule.idle?(configuration.idle_timeout))
121
129
  end
122
130
 
123
131
  systemd.stop do
@@ -228,6 +228,16 @@ module GoodJob
228
228
  )&.to_i
229
229
  end
230
230
 
231
+ # The number of seconds that a good_job process will idle with out running a job before exiting
232
+ # @return [Integer, nil] Number of seconds or nil means do not idle out.
233
+ def idle_timeout
234
+ (
235
+ options[:idle_timeout] ||
236
+ rails_config[:idle_timeout] ||
237
+ env['GOOD_JOB_IDLE_TIMEOUT']
238
+ )&.to_i || nil
239
+ end
240
+
231
241
  # Whether to automatically destroy discarded jobs that have been preserved.
232
242
  # @return [Boolean]
233
243
  def cleanup_discarded_jobs?
@@ -32,7 +32,6 @@ module GoodJob # :nodoc:
32
32
  # Increments number of dequeue attempts with no executions.
33
33
  # @return [Integer]
34
34
  def increment_empty_executions
35
- @execution_at = Time.current
36
35
  @empty_executions.increment
37
36
  end
38
37
 
@@ -95,6 +95,7 @@ module GoodJob
95
95
  succeeded_executions_count: scheduler_stats.sum { |stats| stats.fetch(:succeeded_executions_count, 0) },
96
96
  total_executions_count: scheduler_stats.sum { |stats| stats.fetch(:total_executions_count, 0) },
97
97
  execution_at: scheduler_stats.map { |stats| stats.fetch(:execution_at, nil) }.compact.max,
98
+ active_execution_thread_count: scheduler_stats.sum { |stats| stats.fetch(:active_threads, 0) },
98
99
  check_queue_at: scheduler_stats.map { |stats| stats.fetch(:check_queue_at, nil) }.compact.max,
99
100
  }
100
101
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module GoodJob
4
4
  # GoodJob gem version.
5
- VERSION = '3.21.2'
5
+ VERSION = '3.21.3'
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: 3.21.2
4
+ version: 3.21.3
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-11-24 00:00:00.000000000 Z
11
+ date: 2023-12-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob
@@ -344,6 +344,7 @@ files:
344
344
  - lib/generators/good_job/templates/update/migrations/04_create_good_job_batches.rb.erb
345
345
  - lib/generators/good_job/templates/update/migrations/05_create_good_job_executions.rb.erb
346
346
  - lib/generators/good_job/templates/update/migrations/06_create_good_jobs_error_event.rb.erb
347
+ - lib/generators/good_job/templates/update/migrations/07_recreate_good_job_cron_indexes_with_conditional.rb.erb
347
348
  - lib/generators/good_job/update_generator.rb
348
349
  - lib/good_job.rb
349
350
  - lib/good_job/active_job_extensions/batches.rb