Skip to content

Commit 064d30c

Browse files
committed
Document async mode and allow configuring via environment variable
For consistency with the other `bin/jobs` options.
1 parent 38bc2c8 commit 064d30c

4 files changed

Lines changed: 45 additions & 16 deletions

File tree

README.md

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ Solid Queue can be used with SQL databases such as MySQL, PostgreSQL, or SQLite,
1414
- [Dashboard UI Setup](#dashboard-ui-setup)
1515
- [Incremental adoption](#incremental-adoption)
1616
- [High performance requirements](#high-performance-requirements)
17+
- [Workers, dispatchers, and scheduler](#workers-dispatchers-and-scheduler)
18+
- [Fork vs. async mode](#fork-vs-async-mode)
1719
- [Configuration](#configuration)
18-
- [Workers, dispatchers, and scheduler](#workers-dispatchers-and-scheduler)
1920
- [Queue order and priorities](#queue-order-and-priorities)
2021
- [Queues specification and performance](#queues-specification-and-performance)
2122
- [Threads, processes, and signals](#threads-processes-and-signals)
@@ -179,9 +180,7 @@ end
179180

180181
Solid Queue was designed for the highest throughput when used with MySQL 8+, MariaDB 10.6+, or PostgreSQL 9.5+, as they support `FOR UPDATE SKIP LOCKED`. You can use it with older versions, but in that case, you might run into lock waits if you run multiple workers for the same queue. You can also use it with SQLite on smaller applications.
181182

182-
## Configuration
183-
184-
### Workers, dispatchers, and scheduler
183+
## Workers, dispatchers, and scheduler
185184

186185
We have several types of actors in Solid Queue:
187186

@@ -190,7 +189,17 @@ We have several types of actors in Solid Queue:
190189
- The _scheduler_ manages [recurring tasks](#recurring-tasks), enqueuing jobs for them when they're due.
191190
- The _supervisor_ runs workers and dispatchers according to the configuration, controls their heartbeats, and stops and starts them when needed.
192191

193-
Solid Queue's supervisor will fork a separate process for each supervised worker/dispatcher/scheduler.
192+
### Fork vs. async mode
193+
194+
By default, Solid Queue runs in `fork` mode. This means the supervisor will fork a separate process for each supervised worker/dispatcher/scheduler. This provides the best isolation and performance, but can have additional memory usage. Alternatively, you can run all workers, dispatchers and schedulers in the same process as the supervisor, in different threads, with an `async` mode. You can choose this mode by running `bin/jobs` as:
195+
196+
```
197+
bin/jobs --mode async
198+
```
199+
200+
Or you can also set the environment variable `SOLID_QUEUE_SUPERVISOR_MODE` to `async`. If you use the `async` mode, the `processes` option in the configuration described below will be ignored.
201+
202+
## Configuration
194203

195204
By default, Solid Queue will try to find your configuration under `config/queue.yml`, but you can set a different path using the environment variable `SOLID_QUEUE_CONFIG` or by using the `-c/--config_file` option with `bin/jobs`, like this:
196205

@@ -254,7 +263,7 @@ Here's an overview of the different options:
254263

255264
- `threads`: this is the max size of the thread pool that each worker will have to run jobs. Each worker will fetch this number of jobs from their queue(s), at most and will post them to the thread pool to be run. By default, this is `3`. Only workers have this setting.
256265
It is recommended to set this value less than or equal to the queue database's connection pool size minus 2, as each worker thread uses one connection, and two additional connections are reserved for polling and heartbeat.
257-
- `processes`: this is the number of worker processes that will be forked by the supervisor with the settings given. By default, this is `1`, just a single process. This setting is useful if you want to dedicate more than one CPU core to a queue or queues with the same configuration. Only workers have this setting.
266+
- `processes`: this is the number of worker processes that will be forked by the supervisor with the settings given. By default, this is `1`, just a single process. This setting is useful if you want to dedicate more than one CPU core to a queue or queues with the same configuration. Only workers have this setting. **Note**: this option will be ignored if [running in `async` mode](#fork-vs-async-mode).
258267
- `concurrency_maintenance`: whether the dispatcher will perform the concurrency maintenance work. This is `true` by default, and it's useful if you don't use any [concurrency controls](#concurrency-controls) and want to disable it or if you run multiple dispatchers and want some of them to just dispatch jobs without doing anything else.
259268

260269

@@ -334,7 +343,7 @@ queues: back*
334343

335344
Workers in Solid Queue use a thread pool to run work in multiple threads, configurable via the `threads` parameter above. Besides this, parallelism can be achieved via multiple processes on one machine (configurable via different workers or the `processes` parameter above) or by horizontal scaling.
336345

337-
The supervisor is in charge of managing these processes, and it responds to the following signals:
346+
The supervisor is in charge of managing these processes, and it responds to the following signals when running in its own process via `bin/jobs` or with [the Puma plugin](#puma-plugin) with the default `fork` mode:
338347
- `TERM`, `INT`: starts graceful termination. The supervisor will send a `TERM` signal to its supervised processes, and it'll wait up to `SolidQueue.shutdown_timeout` time until they're done. If any supervised processes are still around by then, it'll send a `QUIT` signal to them to indicate they must exit.
339348
- `QUIT`: starts immediate termination. The supervisor will send a `QUIT` signal to its supervised processes, causing them to exit immediately.
340349

@@ -603,6 +612,20 @@ that you set in production only. This is what Rails 8's default Puma config look
603612

604613
**Note**: phased restarts are not supported currently because the plugin requires [app preloading](https://github.com/puma/puma?tab=readme-ov-file#cluster-mode) to work.
605614

615+
### Running as a fork or asynchronously
616+
617+
By default, the Puma plugin will fork additional processes for each worker and dispatcher so that they run in different processes. This provides the best isolation and performance, but can have additional memory usage.
618+
619+
Alternatively, workers and dispatchers can be run within the same Puma process(s). To do so just configure the plugin as:
620+
621+
```ruby
622+
plugin :solid_queue
623+
solid_queue_mode :async
624+
```
625+
626+
Note that in this case, the `processes` configuration option will be ignored. See also [Fork vs. async mode](#fork-vs-async-mode).
627+
628+
606629
## Jobs and transactional integrity
607630
:warning: Having your jobs in the same ACID-compliant database as your application data enables a powerful yet sharp tool: taking advantage of transactional integrity to ensure some action in your app is not committed unless your job is also committed and vice versa, and ensuring that your job won't be enqueued until the transaction within which you're enqueuing it is committed. This can be very powerful and useful, but it can also backfire if you base some of your logic on this behaviour, and in the future, you move to another active job backend, or if you simply move Solid Queue to its own database, and suddenly the behaviour changes under you. Because this can be quite tricky and many people shouldn't need to worry about it, by default Solid Queue is configured in a different database as the main app.
608631

lib/puma/plugin/solid_queue.rb

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def start_forked(launcher)
4242
launcher.events.after_booted do
4343
@solid_queue_pid = fork do
4444
Thread.new { monitor_puma }
45-
SolidQueue::Supervisor.start(mode: :fork)
45+
start_solid_queue(mode: :fork)
4646
end
4747
end
4848

@@ -54,29 +54,33 @@ def start_forked(launcher)
5454
def start_async(launcher)
5555
if Gem::Version.new(Puma::Const::VERSION) < Gem::Version.new("7")
5656
launcher.events.on_booted do
57-
@solid_queue_supervisor = SolidQueue::Supervisor.start(mode: :async, standalone: false)
57+
start_solid_queue(mode: :async, standalone: false)
5858
end
5959

60-
launcher.events.on_stopped { @solid_queue_supervisor&.stop }
60+
launcher.events.on_stopped { solid_queue_supervisor&.stop }
6161

6262
launcher.events.on_restart do
6363
solid_queue_supervisor&.stop
64-
@solid_queue_supervisor = SolidQueue::Supervisor.start(mode: :async, standalone: false)
64+
start_solid_queue(mode: :async, standalone: false)
6565
end
6666
else
6767
launcher.events.after_booted do
68-
@solid_queue_supervisor = SolidQueue::Supervisor.start(mode: :async, standalone: false)
68+
start_solid_queue(mode: :async, standalone: false)
6969
end
7070

71-
launcher.events.after_stopped { @solid_queue_supervisor&.stop }
71+
launcher.events.after_stopped { solid_queue_supervisor&.stop }
7272

7373
launcher.events.before_restart do
7474
solid_queue_supervisor&.stop
75-
@solid_queue_supervisor = SolidQueue::Supervisor.start(mode: :async, standalone: false)
75+
start_solid_queue(mode: :async, standalone: false)
7676
end
7777
end
7878
end
7979

80+
def start_solid_queue(**options)
81+
@solid_queue_supervisor = SolidQueue::Supervisor.start(**options)
82+
end
83+
8084
def stop_solid_queue_fork
8185
Process.waitpid(solid_queue_pid, Process::WNOHANG)
8286
log "Stopping Solid Queue..."

lib/solid_queue/cli.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ class Cli < Thor
88
desc: "Path to config file (default: #{Configuration::DEFAULT_CONFIG_FILE_PATH}).",
99
banner: "SOLID_QUEUE_CONFIG"
1010

11-
class_option :mode, type: :string, default: "fork", enum: %w[ fork async ], desc: "Whether to fork processes for workers and dispatchers (fork) or to run these in the same process as the supervisor (async)"
11+
class_option :mode, type: :string, default: "fork", enum: %w[ fork async ],
12+
desc: "Whether to fork processes for workers and dispatchers (fork) or to run these in the same process as the supervisor (async) (default: fork).",
13+
banner: "SOLID_QUEUE_SUPERVISOR_MODE"
1214

1315
class_option :recurring_schedule_file, type: :string,
1416
desc: "Path to recurring schedule definition (default: #{Configuration::DEFAULT_RECURRING_SCHEDULE_FILE_PATH}).",

lib/solid_queue/configuration.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def ensure_correctly_sized_thread_pool
9292

9393
def default_options
9494
{
95-
mode: :fork,
95+
mode: ENV["SOLID_QUEUE_SUPERVISOR_MODE"] || :fork,
9696
standalone: true,
9797
config_file: Rails.root.join(ENV["SOLID_QUEUE_CONFIG"] || DEFAULT_CONFIG_FILE_PATH),
9898
recurring_schedule_file: Rails.root.join(ENV["SOLID_QUEUE_RECURRING_SCHEDULE"] || DEFAULT_RECURRING_SCHEDULE_FILE_PATH),

0 commit comments

Comments
 (0)