solid_queue 0.3.3 → 0.4.0
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 +88 -14
- data/app/models/solid_queue/claimed_execution.rb +10 -3
- data/app/models/solid_queue/failed_execution.rb +37 -1
- data/app/models/solid_queue/job.rb +7 -0
- data/app/models/solid_queue/process/executor.rb +20 -0
- data/app/models/solid_queue/process/prunable.rb +15 -11
- data/app/models/solid_queue/process.rb +10 -9
- data/app/models/solid_queue/recurring_execution.rb +7 -3
- data/lib/puma/plugin/solid_queue.rb +39 -11
- data/lib/solid_queue/configuration.rb +18 -22
- data/lib/solid_queue/dispatcher/concurrency_maintenance.rb +1 -1
- data/lib/solid_queue/dispatcher/recurring_schedule.rb +4 -0
- data/lib/solid_queue/dispatcher/recurring_task.rb +14 -6
- data/lib/solid_queue/dispatcher.rb +7 -4
- data/lib/solid_queue/log_subscriber.rb +22 -13
- data/lib/solid_queue/processes/callbacks.rb +0 -7
- data/lib/solid_queue/processes/poller.rb +10 -11
- data/lib/solid_queue/processes/procline.rb +1 -1
- data/lib/solid_queue/processes/registrable.rb +9 -1
- data/lib/solid_queue/processes/runnable.rb +38 -7
- data/lib/solid_queue/processes/supervised.rb +2 -3
- data/lib/solid_queue/supervisor/async_supervisor.rb +44 -0
- data/lib/solid_queue/supervisor/fork_supervisor.rb +108 -0
- data/lib/solid_queue/supervisor/maintenance.rb +34 -0
- data/lib/solid_queue/{processes → supervisor}/pidfile.rb +2 -2
- data/lib/solid_queue/supervisor/pidfiled.rb +25 -0
- data/lib/solid_queue/supervisor/signals.rb +67 -0
- data/lib/solid_queue/supervisor.rb +32 -142
- data/lib/solid_queue/tasks.rb +1 -11
- data/lib/solid_queue/timer.rb +28 -0
- data/lib/solid_queue/version.rb +1 -1
- data/lib/solid_queue/worker.rb +4 -5
- metadata +13 -5
- data/lib/solid_queue/processes/signals.rb +0 -69
@@ -2,182 +2,72 @@
|
|
2
2
|
|
3
3
|
module SolidQueue
|
4
4
|
class Supervisor < Processes::Base
|
5
|
-
include
|
5
|
+
include Maintenance
|
6
6
|
|
7
7
|
class << self
|
8
|
-
def start(mode: :
|
8
|
+
def start(mode: :fork, load_configuration_from: nil)
|
9
9
|
SolidQueue.supervisor = true
|
10
10
|
configuration = Configuration.new(mode: mode, load_from: load_configuration_from)
|
11
11
|
|
12
|
-
|
12
|
+
klass = mode == :fork ? ForkSupervisor : AsyncSupervisor
|
13
|
+
klass.new(configuration).tap(&:start)
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
16
|
-
def initialize(
|
17
|
-
@
|
18
|
-
@forks = {}
|
17
|
+
def initialize(configuration)
|
18
|
+
@configuration = configuration
|
19
19
|
end
|
20
20
|
|
21
21
|
def start
|
22
|
-
|
22
|
+
boot
|
23
|
+
|
24
|
+
start_processes
|
25
|
+
launch_maintenance_task
|
23
26
|
|
24
|
-
start_forks
|
25
|
-
launch_process_prune
|
26
27
|
supervise
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
ensure
|
32
|
-
run_callbacks(:shutdown) { shutdown }
|
28
|
+
end
|
29
|
+
|
30
|
+
def stop
|
31
|
+
@stopped = true
|
33
32
|
end
|
34
33
|
|
35
34
|
private
|
36
|
-
attr_reader :
|
35
|
+
attr_reader :configuration
|
37
36
|
|
38
37
|
def boot
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
def supervise
|
45
|
-
loop do
|
46
|
-
procline "supervising #{forks.keys.join(", ")}"
|
47
|
-
|
48
|
-
process_signal_queue
|
49
|
-
reap_and_replace_terminated_forks
|
50
|
-
interruptible_sleep(1.second)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def sync_std_streams
|
55
|
-
STDOUT.sync = STDERR.sync = true
|
56
|
-
end
|
57
|
-
|
58
|
-
def setup_pidfile
|
59
|
-
@pidfile = if SolidQueue.supervisor_pidfile
|
60
|
-
Processes::Pidfile.new(SolidQueue.supervisor_pidfile).tap(&:setup)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def start_forks
|
65
|
-
configured_processes.each { |configured_process| start_fork(configured_process) }
|
66
|
-
end
|
67
|
-
|
68
|
-
def launch_process_prune
|
69
|
-
@prune_task = Concurrent::TimerTask.new(run_now: true, execution_interval: SolidQueue.process_alive_threshold) { prune_dead_processes }
|
70
|
-
@prune_task.execute
|
71
|
-
end
|
72
|
-
|
73
|
-
def shutdown
|
74
|
-
stop_process_prune
|
75
|
-
restore_default_signal_handlers
|
76
|
-
delete_pidfile
|
77
|
-
end
|
78
|
-
|
79
|
-
def graceful_termination
|
80
|
-
SolidQueue.instrument(:graceful_termination, supervisor_pid: ::Process.pid, supervised_pids: forks.keys) do |payload|
|
81
|
-
term_forks
|
82
|
-
|
83
|
-
wait_until(SolidQueue.shutdown_timeout, -> { all_forks_terminated? }) do
|
84
|
-
reap_terminated_forks
|
85
|
-
end
|
86
|
-
|
87
|
-
unless all_forks_terminated?
|
88
|
-
payload[:shutdown_timeout_exceeded] = true
|
89
|
-
immediate_termination
|
38
|
+
SolidQueue.instrument(:start_process, process: self) do
|
39
|
+
run_callbacks(:boot) do
|
40
|
+
@stopped = false
|
41
|
+
sync_std_streams
|
90
42
|
end
|
91
43
|
end
|
92
44
|
end
|
93
45
|
|
94
|
-
def
|
95
|
-
|
96
|
-
quit_forks
|
97
|
-
end
|
46
|
+
def start_processes
|
47
|
+
configuration.processes.each { |configured_process| start_process(configured_process) }
|
98
48
|
end
|
99
49
|
|
100
|
-
def
|
101
|
-
|
50
|
+
def stopped?
|
51
|
+
@stopped
|
102
52
|
end
|
103
53
|
|
104
|
-
def
|
105
|
-
signal_processes(forks.keys, :QUIT)
|
106
|
-
end
|
107
|
-
|
108
|
-
def stop_process_prune
|
109
|
-
@prune_task&.shutdown
|
110
|
-
end
|
111
|
-
|
112
|
-
def delete_pidfile
|
113
|
-
@pidfile&.delete
|
114
|
-
end
|
115
|
-
|
116
|
-
def prune_dead_processes
|
117
|
-
wrap_in_app_executor { SolidQueue::Process.prune }
|
118
|
-
end
|
119
|
-
|
120
|
-
def start_fork(configured_process)
|
121
|
-
configured_process.supervised_by process
|
122
|
-
|
123
|
-
pid = fork do
|
124
|
-
configured_process.start
|
125
|
-
end
|
126
|
-
|
127
|
-
forks[pid] = configured_process
|
128
|
-
end
|
129
|
-
|
130
|
-
def reap_and_replace_terminated_forks
|
131
|
-
loop do
|
132
|
-
pid, status = ::Process.waitpid2(-1, ::Process::WNOHANG)
|
133
|
-
break unless pid
|
134
|
-
|
135
|
-
replace_fork(pid, status)
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
def reap_terminated_forks
|
140
|
-
loop do
|
141
|
-
pid, status = ::Process.waitpid2(-1, ::Process::WNOHANG)
|
142
|
-
break unless pid
|
143
|
-
|
144
|
-
forks.delete(pid)
|
145
|
-
end
|
146
|
-
rescue SystemCallError
|
147
|
-
# All children already reaped
|
148
|
-
end
|
149
|
-
|
150
|
-
def replace_fork(pid, status)
|
151
|
-
SolidQueue.instrument(:replace_fork, supervisor_pid: ::Process.pid, pid: pid, status: status) do |payload|
|
152
|
-
if supervised_fork = forks.delete(pid)
|
153
|
-
payload[:fork] = supervised_fork
|
154
|
-
start_fork(supervised_fork)
|
155
|
-
end
|
156
|
-
end
|
54
|
+
def supervise
|
157
55
|
end
|
158
56
|
|
159
|
-
def
|
160
|
-
|
57
|
+
def start_process(configured_process)
|
58
|
+
raise NotImplementedError
|
161
59
|
end
|
162
60
|
|
163
|
-
def
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
while monotonic_time_now < deadline && !condition.call
|
168
|
-
sleep 0.1
|
169
|
-
block.call
|
170
|
-
end
|
171
|
-
else
|
172
|
-
while !condition.call
|
173
|
-
sleep 0.5
|
174
|
-
block.call
|
61
|
+
def shutdown
|
62
|
+
SolidQueue.instrument(:shutdown_process, process: self) do
|
63
|
+
run_callbacks(:shutdown) do
|
64
|
+
stop_maintenance_task
|
175
65
|
end
|
176
66
|
end
|
177
67
|
end
|
178
68
|
|
179
|
-
def
|
180
|
-
|
69
|
+
def sync_std_streams
|
70
|
+
STDOUT.sync = STDERR.sync = true
|
181
71
|
end
|
182
72
|
end
|
183
73
|
end
|
data/lib/solid_queue/tasks.rb
CHANGED
@@ -1,16 +1,6 @@
|
|
1
1
|
namespace :solid_queue do
|
2
2
|
desc "start solid_queue supervisor to dispatch and process jobs"
|
3
3
|
task start: :environment do
|
4
|
-
SolidQueue::Supervisor.start
|
5
|
-
end
|
6
|
-
|
7
|
-
desc "start solid_queue supervisor to process jobs"
|
8
|
-
task work: :environment do
|
9
|
-
SolidQueue::Supervisor.start(mode: :work)
|
10
|
-
end
|
11
|
-
|
12
|
-
desc "start solid_queue dispatcher to enqueue scheduled jobs"
|
13
|
-
task dispatch: :environment do
|
14
|
-
SolidQueue::Supervisor.start(mode: :dispatch)
|
4
|
+
SolidQueue::Supervisor.start
|
15
5
|
end
|
16
6
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidQueue
|
4
|
+
module Timer
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def wait_until(timeout, condition, &block)
|
8
|
+
if timeout > 0
|
9
|
+
deadline = monotonic_time_now + timeout
|
10
|
+
|
11
|
+
while monotonic_time_now < deadline && !condition.call
|
12
|
+
sleep 0.1
|
13
|
+
block.call
|
14
|
+
end
|
15
|
+
else
|
16
|
+
while !condition.call
|
17
|
+
sleep 0.5
|
18
|
+
block.call
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
def monotonic_time_now
|
25
|
+
::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/solid_queue/version.rb
CHANGED
data/lib/solid_queue/worker.rb
CHANGED
@@ -1,17 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module SolidQueue
|
4
|
-
class Worker < Processes::
|
5
|
-
include Processes::Poller
|
6
|
-
|
4
|
+
class Worker < Processes::Poller
|
7
5
|
attr_accessor :queues, :pool
|
8
6
|
|
9
7
|
def initialize(**options)
|
10
8
|
options = options.dup.with_defaults(SolidQueue::Configuration::WORKER_DEFAULTS)
|
11
9
|
|
12
|
-
@polling_interval = options[:polling_interval]
|
13
10
|
@queues = Array(options[:queues])
|
14
11
|
@pool = Pool.new(options[:threads], on_idle: -> { wake_up })
|
12
|
+
|
13
|
+
super(**options)
|
15
14
|
end
|
16
15
|
|
17
16
|
def metadata
|
@@ -31,7 +30,7 @@ module SolidQueue
|
|
31
30
|
|
32
31
|
def claim_executions
|
33
32
|
with_polling_volume do
|
34
|
-
SolidQueue::ReadyExecution.claim(queues, pool.idle_threads,
|
33
|
+
SolidQueue::ReadyExecution.claim(queues, pool.idle_threads, process_id)
|
35
34
|
end
|
36
35
|
end
|
37
36
|
|
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.4.0
|
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-08-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -203,6 +203,7 @@ files:
|
|
203
203
|
- app/models/solid_queue/job/schedulable.rb
|
204
204
|
- app/models/solid_queue/pause.rb
|
205
205
|
- app/models/solid_queue/process.rb
|
206
|
+
- app/models/solid_queue/process/executor.rb
|
206
207
|
- app/models/solid_queue/process/prunable.rb
|
207
208
|
- app/models/solid_queue/queue.rb
|
208
209
|
- app/models/solid_queue/queue_selector.rb
|
@@ -234,15 +235,20 @@ files:
|
|
234
235
|
- lib/solid_queue/processes/base.rb
|
235
236
|
- lib/solid_queue/processes/callbacks.rb
|
236
237
|
- lib/solid_queue/processes/interruptible.rb
|
237
|
-
- lib/solid_queue/processes/pidfile.rb
|
238
238
|
- lib/solid_queue/processes/poller.rb
|
239
239
|
- lib/solid_queue/processes/procline.rb
|
240
240
|
- lib/solid_queue/processes/registrable.rb
|
241
241
|
- lib/solid_queue/processes/runnable.rb
|
242
|
-
- lib/solid_queue/processes/signals.rb
|
243
242
|
- lib/solid_queue/processes/supervised.rb
|
244
243
|
- lib/solid_queue/supervisor.rb
|
244
|
+
- lib/solid_queue/supervisor/async_supervisor.rb
|
245
|
+
- lib/solid_queue/supervisor/fork_supervisor.rb
|
246
|
+
- lib/solid_queue/supervisor/maintenance.rb
|
247
|
+
- lib/solid_queue/supervisor/pidfile.rb
|
248
|
+
- lib/solid_queue/supervisor/pidfiled.rb
|
249
|
+
- lib/solid_queue/supervisor/signals.rb
|
245
250
|
- lib/solid_queue/tasks.rb
|
251
|
+
- lib/solid_queue/timer.rb
|
246
252
|
- lib/solid_queue/version.rb
|
247
253
|
- lib/solid_queue/worker.rb
|
248
254
|
homepage: https://github.com/rails/solid_queue
|
@@ -251,7 +257,9 @@ licenses:
|
|
251
257
|
metadata:
|
252
258
|
homepage_uri: https://github.com/rails/solid_queue
|
253
259
|
source_code_uri: https://github.com/rails/solid_queue
|
254
|
-
post_install_message:
|
260
|
+
post_install_message: |
|
261
|
+
Upgrading to Solid Queue 0.4.x? There are some breaking changes about how Solid Queue is started. Check
|
262
|
+
https://github.com/rails/solid_cache/blob/main/UPGRADING.md for upgrade instructions.
|
255
263
|
rdoc_options: []
|
256
264
|
require_paths:
|
257
265
|
- lib
|
@@ -1,69 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module SolidQueue::Processes
|
4
|
-
class GracefulTerminationRequested < Interrupt; end
|
5
|
-
class ImmediateTerminationRequested < Interrupt; end
|
6
|
-
|
7
|
-
module Signals
|
8
|
-
extend ActiveSupport::Concern
|
9
|
-
|
10
|
-
private
|
11
|
-
SIGNALS = %i[ QUIT INT TERM ]
|
12
|
-
|
13
|
-
def register_signal_handlers
|
14
|
-
SIGNALS.each do |signal|
|
15
|
-
trap(signal) do
|
16
|
-
signal_queue << signal
|
17
|
-
interrupt
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def restore_default_signal_handlers
|
23
|
-
SIGNALS.each do |signal|
|
24
|
-
trap(signal, :DEFAULT)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def process_signal_queue
|
29
|
-
while signal = signal_queue.shift
|
30
|
-
handle_signal(signal)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def handle_signal(signal)
|
35
|
-
case signal
|
36
|
-
when :TERM, :INT
|
37
|
-
request_graceful_termination
|
38
|
-
when :QUIT
|
39
|
-
request_immediate_termination
|
40
|
-
else
|
41
|
-
SolidQueue.instrument :unhandled_signal_error, signal: signal
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def request_graceful_termination
|
46
|
-
raise GracefulTerminationRequested
|
47
|
-
end
|
48
|
-
|
49
|
-
def request_immediate_termination
|
50
|
-
raise ImmediateTerminationRequested
|
51
|
-
end
|
52
|
-
|
53
|
-
def signal_processes(pids, signal)
|
54
|
-
pids.each do |pid|
|
55
|
-
signal_process pid, signal
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def signal_process(pid, signal)
|
60
|
-
::Process.kill signal, pid
|
61
|
-
rescue Errno::ESRCH
|
62
|
-
# Ignore, process died before
|
63
|
-
end
|
64
|
-
|
65
|
-
def signal_queue
|
66
|
-
@signal_queue ||= []
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|