solid_queue 0.3.3 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +88 -14
  3. data/app/models/solid_queue/claimed_execution.rb +10 -3
  4. data/app/models/solid_queue/failed_execution.rb +37 -1
  5. data/app/models/solid_queue/job.rb +7 -0
  6. data/app/models/solid_queue/process/executor.rb +20 -0
  7. data/app/models/solid_queue/process/prunable.rb +15 -11
  8. data/app/models/solid_queue/process.rb +10 -9
  9. data/app/models/solid_queue/recurring_execution.rb +7 -3
  10. data/lib/puma/plugin/solid_queue.rb +39 -11
  11. data/lib/solid_queue/configuration.rb +18 -22
  12. data/lib/solid_queue/dispatcher/concurrency_maintenance.rb +1 -1
  13. data/lib/solid_queue/dispatcher/recurring_schedule.rb +4 -0
  14. data/lib/solid_queue/dispatcher/recurring_task.rb +14 -6
  15. data/lib/solid_queue/dispatcher.rb +7 -4
  16. data/lib/solid_queue/log_subscriber.rb +22 -13
  17. data/lib/solid_queue/processes/callbacks.rb +0 -7
  18. data/lib/solid_queue/processes/poller.rb +10 -11
  19. data/lib/solid_queue/processes/procline.rb +1 -1
  20. data/lib/solid_queue/processes/registrable.rb +9 -1
  21. data/lib/solid_queue/processes/runnable.rb +38 -7
  22. data/lib/solid_queue/processes/supervised.rb +2 -3
  23. data/lib/solid_queue/supervisor/async_supervisor.rb +44 -0
  24. data/lib/solid_queue/supervisor/fork_supervisor.rb +108 -0
  25. data/lib/solid_queue/supervisor/maintenance.rb +34 -0
  26. data/lib/solid_queue/{processes → supervisor}/pidfile.rb +2 -2
  27. data/lib/solid_queue/supervisor/pidfiled.rb +25 -0
  28. data/lib/solid_queue/supervisor/signals.rb +67 -0
  29. data/lib/solid_queue/supervisor.rb +32 -142
  30. data/lib/solid_queue/tasks.rb +1 -11
  31. data/lib/solid_queue/timer.rb +28 -0
  32. data/lib/solid_queue/version.rb +1 -1
  33. data/lib/solid_queue/worker.rb +4 -5
  34. metadata +13 -5
  35. 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 Processes::Signals
5
+ include Maintenance
6
6
 
7
7
  class << self
8
- def start(mode: :work, load_configuration_from: nil)
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
- new(*configuration.processes).start
12
+ klass = mode == :fork ? ForkSupervisor : AsyncSupervisor
13
+ klass.new(configuration).tap(&:start)
13
14
  end
14
15
  end
15
16
 
16
- def initialize(*configured_processes)
17
- @configured_processes = Array(configured_processes)
18
- @forks = {}
17
+ def initialize(configuration)
18
+ @configuration = configuration
19
19
  end
20
20
 
21
21
  def start
22
- run_callbacks(:boot) { boot }
22
+ boot
23
+
24
+ start_processes
25
+ launch_maintenance_task
23
26
 
24
- start_forks
25
- launch_process_prune
26
27
  supervise
27
- rescue Processes::GracefulTerminationRequested
28
- graceful_termination
29
- rescue Processes::ImmediateTerminationRequested
30
- immediate_termination
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 :configured_processes, :forks
35
+ attr_reader :configuration
37
36
 
38
37
  def boot
39
- sync_std_streams
40
- setup_pidfile
41
- register_signal_handlers
42
- end
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 immediate_termination
95
- SolidQueue.instrument(:immediate_termination, supervisor_pid: ::Process.pid, supervised_pids: forks.keys) do
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 term_forks
101
- signal_processes(forks.keys, :TERM)
50
+ def stopped?
51
+ @stopped
102
52
  end
103
53
 
104
- def quit_forks
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 all_forks_terminated?
160
- forks.empty?
57
+ def start_process(configured_process)
58
+ raise NotImplementedError
161
59
  end
162
60
 
163
- def wait_until(timeout, condition, &block)
164
- if timeout > 0
165
- deadline = monotonic_time_now + timeout
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 monotonic_time_now
180
- ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
69
+ def sync_std_streams
70
+ STDOUT.sync = STDERR.sync = true
181
71
  end
182
72
  end
183
73
  end
@@ -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(mode: :all)
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
@@ -1,3 +1,3 @@
1
1
  module SolidQueue
2
- VERSION = "0.3.3"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -1,17 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SolidQueue
4
- class Worker < Processes::Base
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, process.id)
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.3.3
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-06-11 00:00:00.000000000 Z
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