solid_queue 1.1.1 → 1.1.3
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 +13 -4
- data/app/models/solid_queue/scheduled_execution.rb +1 -1
- data/lib/solid_queue/configuration.rb +5 -3
- data/lib/solid_queue/dispatcher.rb +5 -10
- data/lib/solid_queue/engine.rb +8 -0
- data/lib/solid_queue/pool.rb +3 -7
- data/lib/solid_queue/processes/base.rb +1 -1
- data/lib/solid_queue/processes/interruptible.rb +10 -6
- data/lib/solid_queue/processes/og_interruptible.rb +41 -0
- data/lib/solid_queue/scheduler.rb +3 -0
- data/lib/solid_queue/version.rb +1 -1
- data/lib/solid_queue.rb +16 -0
- metadata +20 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0bb548d389cca819bac62ce03ff23b44dd9956c95d1f410cdad9b8ae36ea3e68
|
4
|
+
data.tar.gz: 48b6f46f1e093450cc700a259a80b3275cc0c8d8f60108fa1e3b7f0673bfa58a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 80aac013693e8d6f9b18d3f9ea6c4b7aea456dba1512adda885af0e158cbe23c241bfde2c2c00601b2cb7cf694b6e68f4fa9723bdd386bd6fe337eb3084f6efc
|
7
|
+
data.tar.gz: d6d26580b30c0ae2bf415849fc4ae678e0c1796abde0c647e8587a8b8408ca61b3de8ad12b5c712f4eeb038d2cb436c242335f233da8a086256e4ae3d20d2ab4
|
data/README.md
CHANGED
@@ -9,6 +9,7 @@ Solid Queue can be used with SQL databases such as MySQL, PostgreSQL or SQLite,
|
|
9
9
|
## Table of contents
|
10
10
|
|
11
11
|
- [Installation](#installation)
|
12
|
+
- [Usage in development and other non-production environments](#usage-in-development-and-other-non-production-environments)
|
12
13
|
- [Single database configuration](#single-database-configuration)
|
13
14
|
- [Incremental adoption](#incremental-adoption)
|
14
15
|
- [High performance requirements](#high-performance-requirements)
|
@@ -38,6 +39,8 @@ Solid Queue is configured by default in new Rails 8 applications. But if you're
|
|
38
39
|
1. `bundle add solid_queue`
|
39
40
|
2. `bin/rails solid_queue:install`
|
40
41
|
|
42
|
+
(Note: The minimum supported version of Rails is 7.1 and Ruby is 3.1.6.)
|
43
|
+
|
41
44
|
This will configure Solid Queue as the production Active Job backend, create the configuration files `config/queue.yml` and `config/recurring.yml`, and create the `db/queue_schema.rb`. It'll also create a `bin/jobs` executable wrapper that you can use to start Solid Queue.
|
42
45
|
|
43
46
|
Once you've done that, you will then have to add the configuration for the queue database in `config/database.yml`. If you're using SQLite, it'll look like this:
|
@@ -84,7 +87,7 @@ For example, if you're using SQLite in development, update `database.yml` as fol
|
|
84
87
|
|
85
88
|
```diff
|
86
89
|
development:
|
87
|
-
|
90
|
+
+ primary:
|
88
91
|
<<: *default
|
89
92
|
database: storage/development.sqlite3
|
90
93
|
+ queue:
|
@@ -372,9 +375,9 @@ In Solid queue, you can hook into two different points in the supervisor's life:
|
|
372
375
|
- `start`: after the supervisor has finished booting and right before it forks workers and dispatchers.
|
373
376
|
- `stop`: after receiving a signal (`TERM`, `INT` or `QUIT`) and right before starting graceful or immediate shutdown.
|
374
377
|
|
375
|
-
And into two different points in
|
376
|
-
- `
|
377
|
-
- `
|
378
|
+
And into two different points in the worker's, dispatcher's and scheduler's life:
|
379
|
+
- `(worker|dispatcher|scheduler)_start`: after the worker/dispatcher/scheduler has finished booting and right before it starts the polling loop or loading the recurring schedule.
|
380
|
+
- `(worker|dispatcher|scheduler)_stop`: after receiving a signal (`TERM`, `INT` or `QUIT`) and right before starting graceful or immediate shutdown (which is just `exit!`).
|
378
381
|
|
379
382
|
You can use the following methods with a block to do this:
|
380
383
|
```ruby
|
@@ -383,6 +386,12 @@ SolidQueue.on_stop
|
|
383
386
|
|
384
387
|
SolidQueue.on_worker_start
|
385
388
|
SolidQueue.on_worker_stop
|
389
|
+
|
390
|
+
SolidQueue.on_dispatcher_start
|
391
|
+
SolidQueue.on_dispatcher_stop
|
392
|
+
|
393
|
+
SolidQueue.on_scheduler_start
|
394
|
+
SolidQueue.on_scheduler_stop
|
386
395
|
```
|
387
396
|
|
388
397
|
For example:
|
@@ -14,7 +14,7 @@ module SolidQueue
|
|
14
14
|
def dispatch_next_batch(batch_size)
|
15
15
|
transaction do
|
16
16
|
job_ids = next_batch(batch_size).non_blocking_lock.pluck(:job_id)
|
17
|
-
if job_ids.empty? then
|
17
|
+
if job_ids.empty? then 0
|
18
18
|
else
|
19
19
|
SolidQueue.instrument(:dispatch_scheduled, batch_size: batch_size) do |payload|
|
20
20
|
payload[:size] = dispatch_jobs(job_ids)
|
@@ -141,8 +141,8 @@ module SolidQueue
|
|
141
141
|
|
142
142
|
def recurring_tasks
|
143
143
|
@recurring_tasks ||= recurring_tasks_config.map do |id, options|
|
144
|
-
RecurringTask.from_configuration(id, **options)
|
145
|
-
end
|
144
|
+
RecurringTask.from_configuration(id, **options) if options&.has_key?(:schedule)
|
145
|
+
end.compact
|
146
146
|
end
|
147
147
|
|
148
148
|
def processes_config
|
@@ -153,7 +153,9 @@ module SolidQueue
|
|
153
153
|
end
|
154
154
|
|
155
155
|
def recurring_tasks_config
|
156
|
-
@recurring_tasks_config ||=
|
156
|
+
@recurring_tasks_config ||= begin
|
157
|
+
config_from options[:recurring_schedule_file]
|
158
|
+
end
|
157
159
|
end
|
158
160
|
|
159
161
|
|
@@ -2,10 +2,13 @@
|
|
2
2
|
|
3
3
|
module SolidQueue
|
4
4
|
class Dispatcher < Processes::Poller
|
5
|
+
include LifecycleHooks
|
5
6
|
attr_accessor :batch_size, :concurrency_maintenance
|
6
7
|
|
8
|
+
after_boot :run_start_hooks
|
7
9
|
after_boot :start_concurrency_maintenance
|
8
10
|
before_shutdown :stop_concurrency_maintenance
|
11
|
+
after_shutdown :run_stop_hooks
|
9
12
|
|
10
13
|
def initialize(**options)
|
11
14
|
options = options.dup.with_defaults(SolidQueue::Configuration::DISPATCHER_DEFAULTS)
|
@@ -25,7 +28,7 @@ module SolidQueue
|
|
25
28
|
def poll
|
26
29
|
batch = dispatch_next_batch
|
27
30
|
|
28
|
-
batch.
|
31
|
+
batch.zero? ? polling_interval : 0.seconds
|
29
32
|
end
|
30
33
|
|
31
34
|
def dispatch_next_batch
|
@@ -38,20 +41,12 @@ module SolidQueue
|
|
38
41
|
concurrency_maintenance&.start
|
39
42
|
end
|
40
43
|
|
41
|
-
def schedule_recurring_tasks
|
42
|
-
recurring_schedule.schedule_tasks
|
43
|
-
end
|
44
|
-
|
45
44
|
def stop_concurrency_maintenance
|
46
45
|
concurrency_maintenance&.stop
|
47
46
|
end
|
48
47
|
|
49
|
-
def unschedule_recurring_tasks
|
50
|
-
recurring_schedule.unschedule_tasks
|
51
|
-
end
|
52
|
-
|
53
48
|
def all_work_completed?
|
54
|
-
SolidQueue::ScheduledExecution.none?
|
49
|
+
SolidQueue::ScheduledExecution.none?
|
55
50
|
end
|
56
51
|
|
57
52
|
def set_procline
|
data/lib/solid_queue/engine.rb
CHANGED
@@ -37,5 +37,13 @@ module SolidQueue
|
|
37
37
|
include ActiveJob::ConcurrencyControls
|
38
38
|
end
|
39
39
|
end
|
40
|
+
|
41
|
+
initializer "solid_queue.include_interruptible_concern" do
|
42
|
+
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.2")
|
43
|
+
SolidQueue::Processes::Base.include SolidQueue::Processes::Interruptible
|
44
|
+
else
|
45
|
+
SolidQueue::Processes::Base.include SolidQueue::Processes::OgInterruptible
|
46
|
+
end
|
47
|
+
end
|
40
48
|
end
|
41
49
|
end
|
data/lib/solid_queue/pool.rb
CHANGED
@@ -18,20 +18,16 @@ module SolidQueue
|
|
18
18
|
def post(execution)
|
19
19
|
available_threads.decrement
|
20
20
|
|
21
|
-
|
21
|
+
Concurrent::Promises.future_on(executor, execution) do |thread_execution|
|
22
22
|
wrap_in_app_executor do
|
23
23
|
thread_execution.perform
|
24
24
|
ensure
|
25
25
|
available_threads.increment
|
26
26
|
mutex.synchronize { on_idle.try(:call) if idle? }
|
27
27
|
end
|
28
|
+
end.on_rejection! do |e|
|
29
|
+
handle_thread_error(e)
|
28
30
|
end
|
29
|
-
|
30
|
-
future.add_observer do |_, _, error|
|
31
|
-
handle_thread_error(error) if error
|
32
|
-
end
|
33
|
-
|
34
|
-
future.execute
|
35
31
|
end
|
36
32
|
|
37
33
|
def idle_threads
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module SolidQueue::Processes
|
4
4
|
module Interruptible
|
5
|
+
include SolidQueue::AppExecutor
|
6
|
+
|
5
7
|
def wake_up
|
6
8
|
interrupt
|
7
9
|
end
|
@@ -13,17 +15,19 @@ module SolidQueue::Processes
|
|
13
15
|
end
|
14
16
|
|
15
17
|
# Sleeps for 'time'. Can be interrupted asynchronously and return early via wake_up.
|
16
|
-
# @param time [Numeric] the time to sleep. 0 returns immediately.
|
17
|
-
# @return [true, nil]
|
18
|
-
# * returns `true` if an interrupt was requested via #wake_up between the
|
19
|
-
# last call to `interruptible_sleep` and now, resulting in an early return.
|
20
|
-
# * returns `nil` if it slept the full `time` and was not interrupted.
|
18
|
+
# @param time [Numeric, Duration] the time to sleep. 0 returns immediately.
|
21
19
|
def interruptible_sleep(time)
|
22
20
|
# Invoking this from the main thread may result in significant slowdown.
|
23
21
|
# Utilizing asynchronous execution (Futures) addresses this performance issue.
|
24
22
|
Concurrent::Promises.future(time) do |timeout|
|
25
|
-
queue.pop(timeout:).
|
23
|
+
queue.clear unless queue.pop(timeout:).nil?
|
24
|
+
end.on_rejection! do |e|
|
25
|
+
wrapped_exception = RuntimeError.new("Interruptible#interruptible_sleep - #{e.class}: #{e.message}")
|
26
|
+
wrapped_exception.set_backtrace(e.backtrace)
|
27
|
+
handle_thread_error(wrapped_exception)
|
26
28
|
end.value
|
29
|
+
|
30
|
+
nil
|
27
31
|
end
|
28
32
|
|
29
33
|
def queue
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
module SolidQueue::Processes
|
6
|
+
# The original implementation of Interruptible that works
|
7
|
+
# with Ruby 3.1 and earlier
|
8
|
+
module OgInterruptible
|
9
|
+
def wake_up
|
10
|
+
interrupt
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
SELF_PIPE_BLOCK_SIZE = 11
|
15
|
+
|
16
|
+
def interrupt
|
17
|
+
self_pipe[:writer].write_nonblock(".")
|
18
|
+
rescue Errno::EAGAIN, Errno::EINTR
|
19
|
+
# Ignore writes that would block and retry
|
20
|
+
# if another signal arrived while writing
|
21
|
+
retry
|
22
|
+
end
|
23
|
+
|
24
|
+
def interruptible_sleep(time)
|
25
|
+
if time > 0 && self_pipe[:reader].wait_readable(time)
|
26
|
+
loop { self_pipe[:reader].read_nonblock(SELF_PIPE_BLOCK_SIZE) }
|
27
|
+
end
|
28
|
+
rescue Errno::EAGAIN, Errno::EINTR
|
29
|
+
end
|
30
|
+
|
31
|
+
# Self-pipe for signal-handling (http://cr.yp.to/docs/selfpipe.html)
|
32
|
+
def self_pipe
|
33
|
+
@self_pipe ||= create_self_pipe
|
34
|
+
end
|
35
|
+
|
36
|
+
def create_self_pipe
|
37
|
+
reader, writer = IO.pipe
|
38
|
+
{ reader: reader, writer: writer }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -3,11 +3,14 @@
|
|
3
3
|
module SolidQueue
|
4
4
|
class Scheduler < Processes::Base
|
5
5
|
include Processes::Runnable
|
6
|
+
include LifecycleHooks
|
6
7
|
|
7
8
|
attr_accessor :recurring_schedule
|
8
9
|
|
10
|
+
after_boot :run_start_hooks
|
9
11
|
after_boot :schedule_recurring_tasks
|
10
12
|
before_shutdown :unschedule_recurring_tasks
|
13
|
+
before_shutdown :run_stop_hooks
|
11
14
|
|
12
15
|
def initialize(recurring_tasks:, **options)
|
13
16
|
@recurring_schedule = RecurringSchedule.new(recurring_tasks)
|
data/lib/solid_queue/version.rb
CHANGED
data/lib/solid_queue.rb
CHANGED
@@ -51,6 +51,22 @@ module SolidQueue
|
|
51
51
|
Worker.on_stop(...)
|
52
52
|
end
|
53
53
|
|
54
|
+
def on_dispatcher_start(...)
|
55
|
+
Dispatcher.on_start(...)
|
56
|
+
end
|
57
|
+
|
58
|
+
def on_dispatcher_stop(...)
|
59
|
+
Dispatcher.on_stop(...)
|
60
|
+
end
|
61
|
+
|
62
|
+
def on_scheduler_start(...)
|
63
|
+
Scheduler.on_start(...)
|
64
|
+
end
|
65
|
+
|
66
|
+
def on_scheduler_stop(...)
|
67
|
+
Scheduler.on_stop(...)
|
68
|
+
end
|
69
|
+
|
54
70
|
def supervisor?
|
55
71
|
supervisor
|
56
72
|
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: 1.1.
|
4
|
+
version: 1.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rosa Gutierrez
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-01-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -220,6 +220,20 @@ dependencies:
|
|
220
220
|
- - ">="
|
221
221
|
- !ruby/object:Gem::Version
|
222
222
|
version: '0'
|
223
|
+
- !ruby/object:Gem::Dependency
|
224
|
+
name: zeitwerk
|
225
|
+
requirement: !ruby/object:Gem::Requirement
|
226
|
+
requirements:
|
227
|
+
- - '='
|
228
|
+
- !ruby/object:Gem::Version
|
229
|
+
version: 2.6.0
|
230
|
+
type: :development
|
231
|
+
prerelease: false
|
232
|
+
version_requirements: !ruby/object:Gem::Requirement
|
233
|
+
requirements:
|
234
|
+
- - '='
|
235
|
+
- !ruby/object:Gem::Version
|
236
|
+
version: 2.6.0
|
223
237
|
description: Database-backed Active Job backend.
|
224
238
|
email:
|
225
239
|
- rosa@37signals.com
|
@@ -281,6 +295,7 @@ files:
|
|
281
295
|
- lib/solid_queue/processes/base.rb
|
282
296
|
- lib/solid_queue/processes/callbacks.rb
|
283
297
|
- lib/solid_queue/processes/interruptible.rb
|
298
|
+
- lib/solid_queue/processes/og_interruptible.rb
|
284
299
|
- lib/solid_queue/processes/poller.rb
|
285
300
|
- lib/solid_queue/processes/process_exit_error.rb
|
286
301
|
- lib/solid_queue/processes/process_missing_error.rb
|
@@ -307,15 +322,8 @@ metadata:
|
|
307
322
|
homepage_uri: https://github.com/rails/solid_queue
|
308
323
|
source_code_uri: https://github.com/rails/solid_queue
|
309
324
|
post_install_message: |
|
310
|
-
Upgrading
|
311
|
-
|
312
|
-
Upgrading to Solid Queue 0.8.0 from < 0.6.0? You need to upgrade to 0.6.0 first.
|
313
|
-
|
314
|
-
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,
|
315
|
-
configuration and new migrations.
|
316
|
-
|
317
|
-
--> Check https://github.com/rails/solid_queue/blob/main/UPGRADING.md
|
318
|
-
for upgrade instructions.
|
325
|
+
Upgrading from Solid Queue < 1.0? Check details on breaking changes and upgrade instructions
|
326
|
+
--> https://github.com/rails/solid_queue/blob/main/UPGRADING.md
|
319
327
|
rdoc_options: []
|
320
328
|
require_paths:
|
321
329
|
- lib
|
@@ -323,7 +331,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
323
331
|
requirements:
|
324
332
|
- - ">="
|
325
333
|
- !ruby/object:Gem::Version
|
326
|
-
version: '
|
334
|
+
version: '3.1'
|
327
335
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
328
336
|
requirements:
|
329
337
|
- - ">="
|