solid_queue 0.7.0 → 0.7.1

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: 23836c755cda6dd9bec594148ad92ea19740da4125e9115d17c44bd2543d752c
4
- data.tar.gz: 40d91182c13f155a86879f04abcf4232a50f3502f4038b5e9e07a3c15da8def2
3
+ metadata.gz: a7234dc4430998648196bf2d3905b66bb85e265c2393121a7c5343fed36b1996
4
+ data.tar.gz: 216a0918e29194e6d11fe4bf365183228127f8390658a11ace329523ad41468c
5
5
  SHA512:
6
- metadata.gz: f8ac683f1a0e635762d0d93b9b74f712c5fc5465381ea51230448dc2f176de67aafb0ee7031ae9e9cfee73edcee7bd70352e78aefd49ae25ea71a7485af95aff
7
- data.tar.gz: b02eac304a30fb8c7e9e2627446a31bb56d22740e863b1d2749838429416f886da78d19d2000b533e3672576387ee075b8efb3c330f6177b5cd76a27955cd433
6
+ metadata.gz: a080aedf20f39940d8c25e8a14628811801b8fd4832fbcb926beecd0d1ce4c694ec8d80b608656f9de80cc8cc69b525f62d4fe7bc7258408aa6d6af62292d4fd
7
+ data.tar.gz: 66d24c1bb1c7cb1b9afbcf8a0b667df624dd3f47cea92887fbcd69d93b1832af3c44afb570d1cfb459c37e2b3c163900e0dd2febb067ee9179b4c49514e4b359
data/README.md CHANGED
@@ -208,17 +208,47 @@ Finally, run the migrations:
208
208
  $ bin/rails db:migrate
209
209
  ```
210
210
 
211
+ ## Lifecycle hooks
212
+
213
+ In Solid queue, you can hook into two different points in the supervisor's life:
214
+ - `start`: after the supervisor has finished booting and right before it forks workers and dispatchers.
215
+ - `stop`: after receiving a signal (`TERM`, `INT` or `QUIT`) and right before starting graceful or immediate shutdown.
216
+
217
+ And into two different points in a worker's life:
218
+ - `worker_start`: after the worker has finished booting and right before it starts the polling loop.
219
+ - `worker_stop`: after receiving a signal (`TERM`, `INT` or `QUIT`) and right before starting graceful or immediate shutdown (which is just `exit!`).
220
+
221
+ You can use the following methods with a block to do this:
222
+ ```ruby
223
+ SolidQueue.on_start
224
+ SolidQueue.on_stop
225
+
226
+ SolidQueue.on_worker_start
227
+ SolidQueue.on_worker_stop
228
+ ```
229
+
230
+ For example:
231
+ ```ruby
232
+ SolidQueue.on_start { start_metrics_server }
233
+ SolidQueue.on_stop { stop_metrics_server }
234
+ ```
235
+
236
+ These can be called several times to add multiple hooks, but it needs to happen before Solid Queue is started. An initializer would be a good place to do this.
237
+
238
+
211
239
  ### Other configuration settings
212
240
  _Note_: The settings in this section should be set in your `config/application.rb` or your environment config like this: `config.solid_queue.silence_polling = true`
213
241
 
214
242
  There are several settings that control how Solid Queue works that you can set as well:
215
243
  - `logger`: the logger you want Solid Queue to use. Defaults to the app logger.
216
244
  - `app_executor`: the [Rails executor](https://guides.rubyonrails.org/threading_and_code_execution.html#executor) used to wrap asynchronous operations, defaults to the app executor
217
- - `on_thread_error`: custom lambda/Proc to call when there's an error within a thread that takes the exception raised as argument. Defaults to
245
+ - `on_thread_error`: custom lambda/Proc to call when there's an error within a Solid Queue thread that takes the exception raised as argument. Defaults to
218
246
 
219
247
  ```ruby
220
248
  -> (exception) { Rails.error.report(exception, handled: false) }
221
249
  ```
250
+ **This is not used for errors raised within a job execution**. Errors happening in jobs are handled by Active Job's `retry_on` or `discard_on`, and ultimately will result in [failed jobs](#failed-jobs-and-retries). This is for errors happening within Solid Queue itself.
251
+
222
252
  - `use_skip_locked`: whether to use `FOR UPDATE SKIP LOCKED` when performing locking reads. This will be automatically detected in the future, and for now, you'd only need to set this to `false` if your database doesn't support it. For MySQL, that'd be versions < 8, and for PostgreSQL, versions < 9.5. If you use SQLite, this has no effect, as writes are sequential.
223
253
  - `process_heartbeat_interval`: the heartbeat interval that all processes will follow—defaults to 60 seconds.
224
254
  - `process_alive_threshold`: how long to wait until a process is considered dead after its last heartbeat—defaults to 5 minutes.
@@ -283,6 +313,8 @@ In this case, if we have a `Box::MovePostingsByContactToDesignatedBoxJob` job en
283
313
 
284
314
  Note that the `duration` setting depends indirectly on the value for `concurrency_maintenance_interval` that you set for your dispatcher(s), as that'd be the frequency with which blocked jobs are checked and unblocked. In general, you should set `duration` in a way that all your jobs would finish well under that duration and think of the concurrency maintenance task as a failsafe in case something goes wrong.
285
315
 
316
+ Jobs are unblocked in order of priority but queue order is not taken into account for unblocking jobs. That means that if you have a group of jobs that share a concurrency group but are in different queues, or jobs of the same class that you enqueue in different queues, the queue order you set for a worker is not taken into account when unblocking blocked ones. The reason is that a job that runs unblocks the next one, and the job itself doesn't know about a particular worker's queue order (you could even have different workers with different queue orders), it can only know about priority. Once blocked jobs are unblocked and available for polling, they'll be picked up by a worker following its queue order.
317
+
286
318
  Finally, failed jobs that are automatically or manually retried work in the same way as new jobs that get enqueued: they get in the queue for gaining the lock, and whenever they get it, they'll be run. It doesn't matter if they had gained the lock already in the past.
287
319
 
288
320
  ## Failed jobs and retries
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidQueue
4
+ module LifecycleHooks
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ mattr_reader :lifecycle_hooks, default: { start: [], stop: [] }
9
+ end
10
+
11
+ class_methods do
12
+ def on_start(&block)
13
+ self.lifecycle_hooks[:start] << block
14
+ end
15
+
16
+ def on_stop(&block)
17
+ self.lifecycle_hooks[:stop] << block
18
+ end
19
+
20
+ def clear_hooks
21
+ self.lifecycle_hooks[:start] = []
22
+ self.lifecycle_hooks[:stop] = []
23
+ end
24
+ end
25
+
26
+ private
27
+ def run_start_hooks
28
+ run_hooks_for :start
29
+ end
30
+
31
+ def run_stop_hooks
32
+ run_hooks_for :stop
33
+ end
34
+
35
+ def run_hooks_for(event)
36
+ self.class.lifecycle_hooks.fetch(event, []).each do |block|
37
+ block.call
38
+ rescue Exception => exception
39
+ handle_thread_error(exception)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -7,11 +7,7 @@ module SolidQueue::Processes
7
7
  attr_writer :mode
8
8
 
9
9
  def start
10
- @stopped = false
11
-
12
- SolidQueue.instrument(:start_process, process: self) do
13
- run_callbacks(:boot) { boot }
14
- end
10
+ boot
15
11
 
16
12
  if running_async?
17
13
  @thread = create_thread { run }
@@ -25,10 +21,6 @@ module SolidQueue::Processes
25
21
  @thread&.join
26
22
  end
27
23
 
28
- def alive?
29
- !running_async? || @thread.alive?
30
- end
31
-
32
24
  private
33
25
  DEFAULT_MODE = :async
34
26
 
@@ -37,9 +29,15 @@ module SolidQueue::Processes
37
29
  end
38
30
 
39
31
  def boot
40
- if running_as_fork?
41
- register_signal_handlers
42
- set_procline
32
+ SolidQueue.instrument(:start_process, process: self) do
33
+ run_callbacks(:boot) do
34
+ @stopped = false
35
+
36
+ if running_as_fork?
37
+ register_signal_handlers
38
+ set_procline
39
+ end
40
+ end
43
41
  end
44
42
  end
45
43
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  module SolidQueue
4
4
  class Supervisor < Processes::Base
5
+ include LifecycleHooks
5
6
  include Maintenance, Signals, Pidfiled
6
7
 
7
8
  class << self
@@ -27,6 +28,7 @@ module SolidQueue
27
28
 
28
29
  def start
29
30
  boot
31
+ run_start_hooks
30
32
 
31
33
  start_processes
32
34
  launch_maintenance_task
@@ -36,6 +38,7 @@ module SolidQueue
36
38
 
37
39
  def stop
38
40
  @stopped = true
41
+ run_stop_hooks
39
42
  end
40
43
 
41
44
  private
@@ -1,3 +1,3 @@
1
1
  module SolidQueue
2
- VERSION = "0.7.0"
2
+ VERSION = "0.7.1"
3
3
  end
@@ -2,6 +2,11 @@
2
2
 
3
3
  module SolidQueue
4
4
  class Worker < Processes::Poller
5
+ include LifecycleHooks
6
+
7
+ after_boot :run_start_hooks
8
+ before_shutdown :run_stop_hooks
9
+
5
10
  attr_accessor :queues, :pool
6
11
 
7
12
  def initialize(**options)
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.7.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-09-02 00:00:00.000000000 Z
11
+ date: 2024-09-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -253,6 +253,7 @@ files:
253
253
  - lib/solid_queue/dispatcher/concurrency_maintenance.rb
254
254
  - lib/solid_queue/dispatcher/recurring_schedule.rb
255
255
  - lib/solid_queue/engine.rb
256
+ - lib/solid_queue/lifecycle_hooks.rb
256
257
  - lib/solid_queue/log_subscriber.rb
257
258
  - lib/solid_queue/pool.rb
258
259
  - lib/solid_queue/processes/base.rb