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 +4 -4
- data/CHANGELOG.md +27 -0
- data/README.md +1 -0
- data/lib/generators/good_job/templates/install/migrations/create_good_jobs.rb.erb +3 -3
- data/lib/generators/good_job/templates/update/migrations/01_create_good_jobs.rb.erb +1 -1
- data/lib/generators/good_job/templates/update/migrations/07_recreate_good_job_cron_indexes_with_conditional.rb.erb +45 -0
- data/lib/good_job/capsule.rb +19 -5
- data/lib/good_job/cli.rb +10 -2
- data/lib/good_job/configuration.rb +10 -0
- data/lib/good_job/job_performer/metrics.rb +0 -1
- data/lib/good_job/multi_scheduler.rb +1 -0
- data/lib/good_job/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b2b769f6444bb3572bd5739f046469179945f8fc6f13c9fa5bd3de03b25e8e43
|
4
|
+
data.tar.gz: c7b0cd19944dea29a8cb75dce00ed92434879d08f07718781d91123a295f6a11
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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:
|
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: :
|
78
|
-
add_index :good_jobs, [:cron_key, :cron_at],
|
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:
|
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
|
data/lib/good_job/capsule.rb
CHANGED
@@ -15,7 +15,7 @@ module GoodJob
|
|
15
15
|
def initialize(configuration: GoodJob.configuration)
|
16
16
|
@configuration = configuration
|
17
17
|
@startable = true
|
18
|
-
@
|
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
|
-
@
|
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
|
-
@
|
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
|
-
@
|
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
|
-
!@
|
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(
|
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?
|
@@ -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
|
data/lib/good_job/version.rb
CHANGED
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.
|
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
|
+
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
|