solid_queue 0.4.1 → 0.7.1
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 +4 -4
- data/README.md +45 -25
- data/UPGRADING.md +102 -0
- data/app/jobs/solid_queue/recurring_job.rb +9 -0
- data/app/models/solid_queue/claimed_execution.rb +21 -8
- data/app/models/solid_queue/process/executor.rb +13 -1
- data/app/models/solid_queue/process/prunable.rb +8 -1
- data/app/models/solid_queue/process.rb +13 -6
- data/app/models/solid_queue/recurring_execution.rb +17 -4
- data/app/models/solid_queue/recurring_task/arguments.rb +17 -0
- data/app/models/solid_queue/recurring_task.rb +122 -0
- data/app/models/solid_queue/semaphore.rb +18 -5
- data/db/migrate/20240719134516_create_recurring_tasks.rb +20 -0
- data/db/migrate/20240811173327_add_name_to_processes.rb +5 -0
- data/db/migrate/20240813160053_make_name_not_null.rb +16 -0
- data/db/migrate/20240819165045_change_solid_queue_recurring_tasks_static_to_not_null.rb +5 -0
- data/lib/generators/solid_queue/install/USAGE +1 -0
- data/lib/generators/solid_queue/install/install_generator.rb +21 -7
- data/lib/generators/solid_queue/install/templates/jobs +6 -0
- data/lib/puma/plugin/solid_queue.rb +10 -32
- data/lib/solid_queue/cli.rb +20 -0
- data/lib/solid_queue/configuration.rb +40 -29
- data/lib/solid_queue/dispatcher/recurring_schedule.rb +21 -12
- data/lib/solid_queue/dispatcher.rb +8 -8
- data/lib/solid_queue/lifecycle_hooks.rb +43 -0
- data/lib/solid_queue/log_subscriber.rb +13 -6
- data/lib/solid_queue/processes/base.rb +11 -0
- data/lib/solid_queue/processes/poller.rb +8 -4
- data/lib/solid_queue/processes/process_exit_error.rb +20 -0
- data/lib/solid_queue/processes/process_missing_error.rb +9 -0
- data/lib/solid_queue/processes/process_pruned_error.rb +11 -0
- data/lib/solid_queue/processes/registrable.rb +1 -0
- data/lib/solid_queue/processes/runnable.rb +10 -16
- data/lib/solid_queue/supervisor/maintenance.rb +5 -3
- data/lib/solid_queue/supervisor.rb +126 -10
- data/lib/solid_queue/version.rb +1 -1
- data/lib/solid_queue/worker.rb +5 -0
- data/lib/solid_queue.rb +10 -0
- metadata +33 -7
- data/lib/solid_queue/dispatcher/recurring_task.rb +0 -99
- data/lib/solid_queue/supervisor/async_supervisor.rb +0 -44
- data/lib/solid_queue/supervisor/fork_supervisor.rb +0 -108
@@ -2,24 +2,33 @@
|
|
2
2
|
|
3
3
|
module SolidQueue
|
4
4
|
class Supervisor < Processes::Base
|
5
|
-
include
|
5
|
+
include LifecycleHooks
|
6
|
+
include Maintenance, Signals, Pidfiled
|
6
7
|
|
7
8
|
class << self
|
8
|
-
def start(
|
9
|
+
def start(load_configuration_from: nil)
|
9
10
|
SolidQueue.supervisor = true
|
10
|
-
configuration = Configuration.new(
|
11
|
+
configuration = Configuration.new(load_from: load_configuration_from)
|
11
12
|
|
12
|
-
|
13
|
-
|
13
|
+
if configuration.configured_processes.any?
|
14
|
+
new(configuration).tap(&:start)
|
15
|
+
else
|
16
|
+
abort "No workers or processed configured. Exiting..."
|
17
|
+
end
|
14
18
|
end
|
15
19
|
end
|
16
20
|
|
17
21
|
def initialize(configuration)
|
18
22
|
@configuration = configuration
|
23
|
+
@forks = {}
|
24
|
+
@configured_processes = {}
|
25
|
+
|
26
|
+
super
|
19
27
|
end
|
20
28
|
|
21
29
|
def start
|
22
30
|
boot
|
31
|
+
run_start_hooks
|
23
32
|
|
24
33
|
start_processes
|
25
34
|
launch_maintenance_task
|
@@ -29,10 +38,11 @@ module SolidQueue
|
|
29
38
|
|
30
39
|
def stop
|
31
40
|
@stopped = true
|
41
|
+
run_stop_hooks
|
32
42
|
end
|
33
43
|
|
34
44
|
private
|
35
|
-
attr_reader :configuration
|
45
|
+
attr_reader :configuration, :forks, :configured_processes
|
36
46
|
|
37
47
|
def boot
|
38
48
|
SolidQueue.instrument(:start_process, process: self) do
|
@@ -44,18 +54,66 @@ module SolidQueue
|
|
44
54
|
end
|
45
55
|
|
46
56
|
def start_processes
|
47
|
-
configuration.
|
57
|
+
configuration.configured_processes.each { |configured_process| start_process(configured_process) }
|
58
|
+
end
|
59
|
+
|
60
|
+
def supervise
|
61
|
+
loop do
|
62
|
+
break if stopped?
|
63
|
+
|
64
|
+
set_procline
|
65
|
+
process_signal_queue
|
66
|
+
|
67
|
+
unless stopped?
|
68
|
+
reap_and_replace_terminated_forks
|
69
|
+
interruptible_sleep(1.second)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
ensure
|
73
|
+
shutdown
|
74
|
+
end
|
75
|
+
|
76
|
+
def start_process(configured_process)
|
77
|
+
process_instance = configured_process.instantiate.tap do |instance|
|
78
|
+
instance.supervised_by process
|
79
|
+
instance.mode = :fork
|
80
|
+
end
|
81
|
+
|
82
|
+
pid = fork do
|
83
|
+
process_instance.start
|
84
|
+
end
|
85
|
+
|
86
|
+
configured_processes[pid] = configured_process
|
87
|
+
forks[pid] = process_instance
|
48
88
|
end
|
49
89
|
|
50
90
|
def stopped?
|
51
91
|
@stopped
|
52
92
|
end
|
53
93
|
|
54
|
-
def
|
94
|
+
def set_procline
|
95
|
+
procline "supervising #{supervised_processes.join(", ")}"
|
55
96
|
end
|
56
97
|
|
57
|
-
def
|
58
|
-
|
98
|
+
def terminate_gracefully
|
99
|
+
SolidQueue.instrument(:graceful_termination, process_id: process_id, supervisor_pid: ::Process.pid, supervised_processes: supervised_processes) do |payload|
|
100
|
+
term_forks
|
101
|
+
|
102
|
+
Timer.wait_until(SolidQueue.shutdown_timeout, -> { all_forks_terminated? }) do
|
103
|
+
reap_terminated_forks
|
104
|
+
end
|
105
|
+
|
106
|
+
unless all_forks_terminated?
|
107
|
+
payload[:shutdown_timeout_exceeded] = true
|
108
|
+
terminate_immediately
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def terminate_immediately
|
114
|
+
SolidQueue.instrument(:immediate_termination, process_id: process_id, supervisor_pid: ::Process.pid, supervised_processes: supervised_processes) do
|
115
|
+
quit_forks
|
116
|
+
end
|
59
117
|
end
|
60
118
|
|
61
119
|
def shutdown
|
@@ -69,5 +127,63 @@ module SolidQueue
|
|
69
127
|
def sync_std_streams
|
70
128
|
STDOUT.sync = STDERR.sync = true
|
71
129
|
end
|
130
|
+
|
131
|
+
def supervised_processes
|
132
|
+
forks.keys
|
133
|
+
end
|
134
|
+
|
135
|
+
def term_forks
|
136
|
+
signal_processes(forks.keys, :TERM)
|
137
|
+
end
|
138
|
+
|
139
|
+
def quit_forks
|
140
|
+
signal_processes(forks.keys, :QUIT)
|
141
|
+
end
|
142
|
+
|
143
|
+
def reap_and_replace_terminated_forks
|
144
|
+
loop do
|
145
|
+
pid, status = ::Process.waitpid2(-1, ::Process::WNOHANG)
|
146
|
+
break unless pid
|
147
|
+
|
148
|
+
replace_fork(pid, status)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def reap_terminated_forks
|
153
|
+
loop do
|
154
|
+
pid, status = ::Process.waitpid2(-1, ::Process::WNOHANG)
|
155
|
+
break unless pid
|
156
|
+
|
157
|
+
if (terminated_fork = forks.delete(pid)) && (!status.exited? || status.exitstatus > 0)
|
158
|
+
handle_claimed_jobs_by(terminated_fork, status)
|
159
|
+
end
|
160
|
+
|
161
|
+
configured_processes.delete(pid)
|
162
|
+
end
|
163
|
+
rescue SystemCallError
|
164
|
+
# All children already reaped
|
165
|
+
end
|
166
|
+
|
167
|
+
def replace_fork(pid, status)
|
168
|
+
SolidQueue.instrument(:replace_fork, supervisor_pid: ::Process.pid, pid: pid, status: status) do |payload|
|
169
|
+
if terminated_fork = forks.delete(pid)
|
170
|
+
payload[:fork] = terminated_fork
|
171
|
+
handle_claimed_jobs_by(terminated_fork, status)
|
172
|
+
|
173
|
+
start_process(configured_processes.delete(pid))
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def handle_claimed_jobs_by(terminated_fork, status)
|
179
|
+
if registered_process = process.supervisees.find_by(name: terminated_fork.name)
|
180
|
+
error = Processes::ProcessExitError.new(status)
|
181
|
+
registered_process.fail_all_claimed_executions_with(error)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def all_forks_terminated?
|
186
|
+
forks.empty?
|
187
|
+
end
|
72
188
|
end
|
73
189
|
end
|
data/lib/solid_queue/version.rb
CHANGED
data/lib/solid_queue/worker.rb
CHANGED
data/lib/solid_queue.rb
CHANGED
@@ -43,6 +43,16 @@ module SolidQueue
|
|
43
43
|
mattr_accessor :clear_finished_jobs_after, default: 1.day
|
44
44
|
mattr_accessor :default_concurrency_control_period, default: 3.minutes
|
45
45
|
|
46
|
+
delegate :on_start, :on_stop, to: Supervisor
|
47
|
+
|
48
|
+
def on_worker_start(...)
|
49
|
+
Worker.on_start(...)
|
50
|
+
end
|
51
|
+
|
52
|
+
def on_worker_stop(...)
|
53
|
+
Worker.on_stop(...)
|
54
|
+
end
|
55
|
+
|
46
56
|
def supervisor?
|
47
57
|
supervisor
|
48
58
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: solid_queue
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rosa Gutierrez
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-09-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -80,6 +80,20 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: 1.11.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: thor
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 1.3.1
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 1.3.1
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
98
|
name: debug
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -188,6 +202,8 @@ files:
|
|
188
202
|
- MIT-LICENSE
|
189
203
|
- README.md
|
190
204
|
- Rakefile
|
205
|
+
- UPGRADING.md
|
206
|
+
- app/jobs/solid_queue/recurring_job.rb
|
191
207
|
- app/models/solid_queue/blocked_execution.rb
|
192
208
|
- app/models/solid_queue/claimed_execution.rb
|
193
209
|
- app/models/solid_queue/execution.rb
|
@@ -210,39 +226,48 @@ files:
|
|
210
226
|
- app/models/solid_queue/ready_execution.rb
|
211
227
|
- app/models/solid_queue/record.rb
|
212
228
|
- app/models/solid_queue/recurring_execution.rb
|
229
|
+
- app/models/solid_queue/recurring_task.rb
|
230
|
+
- app/models/solid_queue/recurring_task/arguments.rb
|
213
231
|
- app/models/solid_queue/scheduled_execution.rb
|
214
232
|
- app/models/solid_queue/semaphore.rb
|
215
233
|
- config/routes.rb
|
216
234
|
- db/migrate/20231211200639_create_solid_queue_tables.rb
|
217
235
|
- db/migrate/20240110143450_add_missing_index_to_blocked_executions.rb
|
218
236
|
- db/migrate/20240218110712_create_recurring_executions.rb
|
237
|
+
- db/migrate/20240719134516_create_recurring_tasks.rb
|
238
|
+
- db/migrate/20240811173327_add_name_to_processes.rb
|
239
|
+
- db/migrate/20240813160053_make_name_not_null.rb
|
240
|
+
- db/migrate/20240819165045_change_solid_queue_recurring_tasks_static_to_not_null.rb
|
219
241
|
- lib/active_job/concurrency_controls.rb
|
220
242
|
- lib/active_job/queue_adapters/solid_queue_adapter.rb
|
221
243
|
- lib/generators/solid_queue/install/USAGE
|
222
244
|
- lib/generators/solid_queue/install/install_generator.rb
|
223
245
|
- lib/generators/solid_queue/install/templates/config.yml
|
246
|
+
- lib/generators/solid_queue/install/templates/jobs
|
224
247
|
- lib/puma/plugin/solid_queue.rb
|
225
248
|
- lib/solid_queue.rb
|
226
249
|
- lib/solid_queue/app_executor.rb
|
250
|
+
- lib/solid_queue/cli.rb
|
227
251
|
- lib/solid_queue/configuration.rb
|
228
252
|
- lib/solid_queue/dispatcher.rb
|
229
253
|
- lib/solid_queue/dispatcher/concurrency_maintenance.rb
|
230
254
|
- lib/solid_queue/dispatcher/recurring_schedule.rb
|
231
|
-
- lib/solid_queue/dispatcher/recurring_task.rb
|
232
255
|
- lib/solid_queue/engine.rb
|
256
|
+
- lib/solid_queue/lifecycle_hooks.rb
|
233
257
|
- lib/solid_queue/log_subscriber.rb
|
234
258
|
- lib/solid_queue/pool.rb
|
235
259
|
- lib/solid_queue/processes/base.rb
|
236
260
|
- lib/solid_queue/processes/callbacks.rb
|
237
261
|
- lib/solid_queue/processes/interruptible.rb
|
238
262
|
- lib/solid_queue/processes/poller.rb
|
263
|
+
- lib/solid_queue/processes/process_exit_error.rb
|
264
|
+
- lib/solid_queue/processes/process_missing_error.rb
|
265
|
+
- lib/solid_queue/processes/process_pruned_error.rb
|
239
266
|
- lib/solid_queue/processes/procline.rb
|
240
267
|
- lib/solid_queue/processes/registrable.rb
|
241
268
|
- lib/solid_queue/processes/runnable.rb
|
242
269
|
- lib/solid_queue/processes/supervised.rb
|
243
270
|
- lib/solid_queue/supervisor.rb
|
244
|
-
- lib/solid_queue/supervisor/async_supervisor.rb
|
245
|
-
- lib/solid_queue/supervisor/fork_supervisor.rb
|
246
271
|
- lib/solid_queue/supervisor/maintenance.rb
|
247
272
|
- lib/solid_queue/supervisor/pidfile.rb
|
248
273
|
- lib/solid_queue/supervisor/pidfiled.rb
|
@@ -258,8 +283,9 @@ metadata:
|
|
258
283
|
homepage_uri: https://github.com/rails/solid_queue
|
259
284
|
source_code_uri: https://github.com/rails/solid_queue
|
260
285
|
post_install_message: |
|
261
|
-
Upgrading to Solid Queue 0.4.x? There are some breaking changes about how Solid Queue is started
|
262
|
-
https://github.com/rails/solid_queue/blob/main/UPGRADING.md
|
286
|
+
Upgrading to Solid Queue 0.4.x, 0.5.x, 0.6.x or 0.7.x? There are some breaking changes about how Solid Queue is started,
|
287
|
+
configuration and new migrations. Check https://github.com/rails/solid_queue/blob/main/UPGRADING.md
|
288
|
+
for upgrade instructions.
|
263
289
|
rdoc_options: []
|
264
290
|
require_paths:
|
265
291
|
- lib
|
@@ -1,99 +0,0 @@
|
|
1
|
-
require "fugit"
|
2
|
-
|
3
|
-
module SolidQueue
|
4
|
-
class Dispatcher::RecurringTask
|
5
|
-
class << self
|
6
|
-
def wrap(args)
|
7
|
-
args.is_a?(self) ? args : from_configuration(args.first, **args.second)
|
8
|
-
end
|
9
|
-
|
10
|
-
def from_configuration(key, **options)
|
11
|
-
new(key, class_name: options[:class], schedule: options[:schedule], arguments: options[:args])
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
attr_reader :key, :schedule, :class_name, :arguments
|
16
|
-
|
17
|
-
def initialize(key, class_name:, schedule:, arguments: nil)
|
18
|
-
@key = key
|
19
|
-
@class_name = class_name
|
20
|
-
@schedule = schedule
|
21
|
-
@arguments = Array(arguments)
|
22
|
-
end
|
23
|
-
|
24
|
-
def delay_from_now
|
25
|
-
[ (next_time - Time.current).to_f, 0 ].max
|
26
|
-
end
|
27
|
-
|
28
|
-
def next_time
|
29
|
-
parsed_schedule.next_time.utc
|
30
|
-
end
|
31
|
-
|
32
|
-
def enqueue(at:)
|
33
|
-
SolidQueue.instrument(:enqueue_recurring_task, task: key, at: at) do |payload|
|
34
|
-
active_job = if using_solid_queue_adapter?
|
35
|
-
perform_later_and_record(run_at: at)
|
36
|
-
else
|
37
|
-
payload[:other_adapter] = true
|
38
|
-
|
39
|
-
perform_later do |job|
|
40
|
-
unless job.successfully_enqueued?
|
41
|
-
payload[:enqueue_error] = job.enqueue_error&.message
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
payload[:active_job_id] = active_job.job_id if active_job
|
47
|
-
rescue RecurringExecution::AlreadyRecorded
|
48
|
-
payload[:skipped] = true
|
49
|
-
rescue Job::EnqueueError => error
|
50
|
-
payload[:enqueue_error] = error.message
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def valid?
|
55
|
-
parsed_schedule.instance_of?(Fugit::Cron)
|
56
|
-
end
|
57
|
-
|
58
|
-
def to_s
|
59
|
-
"#{class_name}.perform_later(#{arguments.map(&:inspect).join(",")}) [ #{parsed_schedule.original} ]"
|
60
|
-
end
|
61
|
-
|
62
|
-
def to_h
|
63
|
-
{
|
64
|
-
schedule: schedule,
|
65
|
-
class_name: class_name,
|
66
|
-
arguments: arguments
|
67
|
-
}
|
68
|
-
end
|
69
|
-
|
70
|
-
private
|
71
|
-
def using_solid_queue_adapter?
|
72
|
-
job_class.queue_adapter_name.inquiry.solid_queue?
|
73
|
-
end
|
74
|
-
|
75
|
-
def perform_later_and_record(run_at:)
|
76
|
-
RecurringExecution.record(key, run_at) { perform_later }
|
77
|
-
end
|
78
|
-
|
79
|
-
def perform_later(&block)
|
80
|
-
job_class.perform_later(*arguments_with_kwargs, &block)
|
81
|
-
end
|
82
|
-
|
83
|
-
def arguments_with_kwargs
|
84
|
-
if arguments.last.is_a?(Hash)
|
85
|
-
arguments[0...-1] + [ Hash.ruby2_keywords_hash(arguments.last) ]
|
86
|
-
else
|
87
|
-
arguments
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
def parsed_schedule
|
92
|
-
@parsed_schedule ||= Fugit.parse(schedule)
|
93
|
-
end
|
94
|
-
|
95
|
-
def job_class
|
96
|
-
@job_class ||= class_name.safe_constantize
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
@@ -1,44 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module SolidQueue
|
4
|
-
class Supervisor::AsyncSupervisor < Supervisor
|
5
|
-
def initialize(*)
|
6
|
-
super
|
7
|
-
@threads = Concurrent::Map.new
|
8
|
-
end
|
9
|
-
|
10
|
-
def kind
|
11
|
-
"Supervisor(async)"
|
12
|
-
end
|
13
|
-
|
14
|
-
def stop
|
15
|
-
super
|
16
|
-
stop_threads
|
17
|
-
threads.clear
|
18
|
-
|
19
|
-
shutdown
|
20
|
-
end
|
21
|
-
|
22
|
-
private
|
23
|
-
attr_reader :threads
|
24
|
-
|
25
|
-
def start_process(configured_process)
|
26
|
-
configured_process.supervised_by process
|
27
|
-
configured_process.start
|
28
|
-
|
29
|
-
threads[configured_process.name] = configured_process
|
30
|
-
end
|
31
|
-
|
32
|
-
def stop_threads
|
33
|
-
stop_threads = threads.values.map do |thr|
|
34
|
-
Thread.new { thr.stop }
|
35
|
-
end
|
36
|
-
|
37
|
-
stop_threads.each { |thr| thr.join(SolidQueue.shutdown_timeout) }
|
38
|
-
end
|
39
|
-
|
40
|
-
def all_threads_terminated?
|
41
|
-
threads.values.none?(&:alive?)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
@@ -1,108 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module SolidQueue
|
4
|
-
class Supervisor::ForkSupervisor < Supervisor
|
5
|
-
include Signals, Pidfiled
|
6
|
-
|
7
|
-
def initialize(*)
|
8
|
-
super
|
9
|
-
@forks = {}
|
10
|
-
end
|
11
|
-
|
12
|
-
def kind
|
13
|
-
"Supervisor(fork)"
|
14
|
-
end
|
15
|
-
|
16
|
-
private
|
17
|
-
attr_reader :forks
|
18
|
-
|
19
|
-
def supervise
|
20
|
-
loop do
|
21
|
-
break if stopped?
|
22
|
-
|
23
|
-
procline "supervising #{forks.keys.join(", ")}"
|
24
|
-
process_signal_queue
|
25
|
-
|
26
|
-
unless stopped?
|
27
|
-
reap_and_replace_terminated_forks
|
28
|
-
interruptible_sleep(1.second)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
ensure
|
32
|
-
shutdown
|
33
|
-
end
|
34
|
-
|
35
|
-
def start_process(configured_process)
|
36
|
-
configured_process.supervised_by process
|
37
|
-
configured_process.mode = :fork
|
38
|
-
|
39
|
-
pid = fork do
|
40
|
-
configured_process.start
|
41
|
-
end
|
42
|
-
|
43
|
-
forks[pid] = configured_process
|
44
|
-
end
|
45
|
-
|
46
|
-
def terminate_gracefully
|
47
|
-
SolidQueue.instrument(:graceful_termination, process_id: process_id, supervisor_pid: ::Process.pid, supervised_processes: forks.keys) do |payload|
|
48
|
-
term_forks
|
49
|
-
|
50
|
-
Timer.wait_until(SolidQueue.shutdown_timeout, -> { all_forks_terminated? }) do
|
51
|
-
reap_terminated_forks
|
52
|
-
end
|
53
|
-
|
54
|
-
unless all_forks_terminated?
|
55
|
-
payload[:shutdown_timeout_exceeded] = true
|
56
|
-
terminate_immediately
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def terminate_immediately
|
62
|
-
SolidQueue.instrument(:immediate_termination, process_id: process_id, supervisor_pid: ::Process.pid, supervised_processes: forks.keys) do
|
63
|
-
quit_forks
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def term_forks
|
68
|
-
signal_processes(forks.keys, :TERM)
|
69
|
-
end
|
70
|
-
|
71
|
-
def quit_forks
|
72
|
-
signal_processes(forks.keys, :QUIT)
|
73
|
-
end
|
74
|
-
|
75
|
-
def reap_and_replace_terminated_forks
|
76
|
-
loop do
|
77
|
-
pid, status = ::Process.waitpid2(-1, ::Process::WNOHANG)
|
78
|
-
break unless pid
|
79
|
-
|
80
|
-
replace_fork(pid, status)
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
def reap_terminated_forks
|
85
|
-
loop do
|
86
|
-
pid, status = ::Process.waitpid2(-1, ::Process::WNOHANG)
|
87
|
-
break unless pid
|
88
|
-
|
89
|
-
forks.delete(pid)
|
90
|
-
end
|
91
|
-
rescue SystemCallError
|
92
|
-
# All children already reaped
|
93
|
-
end
|
94
|
-
|
95
|
-
def replace_fork(pid, status)
|
96
|
-
SolidQueue.instrument(:replace_fork, supervisor_pid: ::Process.pid, pid: pid, status: status) do |payload|
|
97
|
-
if supervised_fork = forks.delete(pid)
|
98
|
-
payload[:fork] = supervised_fork
|
99
|
-
start_process(supervised_fork)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
def all_forks_terminated?
|
105
|
-
forks.empty?
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|