Skip to content

Commit 564047d

Browse files
authored
Slim down installer and have it use a final schema instead of migrations (#321)
Following the same pattern we took with Solid Cache. This entails collapsing all the migrations into a premade `queue_schema.rb` and switching the default configuration to use a database configuration under the queue key. Drop all mentions and focus on running SQ off the same database.
1 parent 0948076 commit 564047d

15 files changed

+208
-322
lines changed

README.md

Lines changed: 46 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -6,43 +6,52 @@ Besides regular job enqueuing and processing, Solid Queue supports delayed jobs,
66

77
Solid Queue can be used with SQL databases such as MySQL, PostgreSQL or SQLite, and it leverages the `FOR UPDATE SKIP LOCKED` clause, if available, to avoid blocking and waiting on locks when polling jobs. It relies on Active Job for retries, discarding, error handling, serialization, or delays, and it's compatible with Ruby on Rails multi-threading.
88

9-
## Installation and usage
10-
Add this line to your application's Gemfile:
9+
## Installation
1110

12-
```ruby
13-
gem "solid_queue"
14-
```
11+
Solid Queue is configured by default in new Rails 8 applications. But if you're running an earlier version, you can add it manually following these steps:
1512

16-
And then execute:
17-
```bash
18-
$ bundle
19-
```
13+
1. `bundle add solid_queue`
14+
2. `bin/rails solid_queue:install`
2015

21-
Or install it yourself as:
22-
```bash
23-
$ gem install solid_queue
24-
```
16+
This will configure Solid Queue as the production Active Job backend, create `config/solid_queue.yml`, and create the `db/queue_schema.rb`.
2517

26-
Now, you need to install the necessary migrations and configure the Active Job's adapter. You can do both at once using the provided generator:
18+
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:
2719

28-
```bash
29-
$ bin/rails generate solid_queue:install
20+
```yaml
21+
production:
22+
primary:
23+
<<: *default
24+
database: storage/production.sqlite3
25+
queue:
26+
<<: *default
27+
database: storage/production_queue.sqlite3
28+
migrations_paths: db/queue_migrate
3029
```
3130
32-
This will set `solid_queue` as the Active Job's adapter in production, and will copy the required migration over to your app.
31+
...or if you're using MySQL/PostgreSQL/Trilogy:
3332
34-
Alternatively, you can skip setting the Active Job's adapter with:
35-
```bash
36-
$ bin/rails generate solid_queue:install --skip_adapter
33+
```yaml
34+
production:
35+
primary: &primary_production
36+
<<: *default
37+
database: app_production
38+
username: app
39+
password: <%= ENV["APP_DATABASE_PASSWORD"] %>
40+
queue:
41+
<<: *primary_production
42+
database: app_production_queue
43+
migrations_paths: db/queue_migrate
3744
```
3845
39-
And set Solid Queue as your Active Job's queue backend manually, in your environment config:
40-
```ruby
41-
# config/environments/production.rb
42-
config.active_job.queue_adapter = :solid_queue
43-
```
46+
Then run `db:prepare` in production to ensure the database is created and the schema is loaded.
47+
48+
Now you're ready to start processing jobs by running `bin/jobs` on the server that's doing the work. This will start processing jobs in all queues using the default configuration. See [below](#configuration) to learn more about configuring Solid Queue.
49+
50+
For small projects, you can run Solid Queue on the same machine as your webserver. When you're ready to scale, Solid Queue supports horizontal scaling out-of-the-box. You can run Solid Queue on a separate server from your webserver, or even run `bin/jobs` on multiple machines at the same time. Depending on the configuration, you can designate some machines to run only dispatchers or only workers. See the [configuration](#configuration) section for more details on this.
51+
52+
## Incremental adoption
4453

45-
Or you can set only specific jobs to use Solid Queue as their backend if you're migrating from another adapter and want to move jobs progressively:
54+
If you're planning to adopt Solid Queue incrementally by switching one job at the time, you can do so by leaving the `config.active_job.queue_adapter` set to your old backend, and then set the `queue_adapter` directly in the jobs you're moving:
4655

4756
```ruby
4857
# app/jobs/my_job.rb
@@ -52,24 +61,9 @@ class MyJob < ApplicationJob
5261
# ...
5362
end
5463
```
64+
## High performance requirements
5565

56-
Finally, you need to run the migrations:
57-
58-
```bash
59-
$ bin/rails db:migrate
60-
```
61-
62-
After this, you'll be ready to enqueue jobs using Solid Queue, but you need to start Solid Queue's supervisor to run them. You can use the provided binstub:`
63-
```bash
64-
$ bin/jobs
65-
```
66-
67-
This will start processing jobs in all queues using the default configuration. See [below](#configuration) to learn more about configuring Solid Queue.
68-
69-
For small projects, you can run Solid Queue on the same machine as your webserver. When you're ready to scale, Solid Queue supports horizontal scaling out-of-the-box. You can run Solid Queue on a separate server from your webserver, or even run `bin/jobs` on multiple machines at the same time. Depending on the configuration, you can designate some machines to run only dispatchers or only workers. See the [configuration](#configuration) section for more details on this.
70-
71-
## Requirements
72-
Besides Rails 7.1, Solid Queue works best with MySQL 8+ 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.
66+
Solid Queue was designed for the highest throughput when used with MySQL 8+ 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.
7367

7468
## Configuration
7569

@@ -135,8 +129,8 @@ Here's an overview of the different options:
135129
- `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.
136130
- `recurring_tasks`: a list of recurring tasks the dispatcher will manage. Read more details about this one in the [Recurring tasks](#recurring-tasks) section.
137131

138-
139132
### Queue order and priorities
133+
140134
As mentioned above, if you specify a list of queues for a worker, these will be polled in the order given, such as for the list `real_time,background`, no jobs will be taken from `background` unless there aren't any more jobs waiting in `real_time`.
141135

142136
Active Job also supports positive integer priorities when enqueuing jobs. In Solid Queue, the smaller the value, the higher the priority. The default is `0`.
@@ -159,54 +153,11 @@ When receiving a `QUIT` signal, if workers still have jobs in-flight, these will
159153
If processes have no chance of cleaning up before exiting (e.g. if someone pulls a cable somewhere), in-flight jobs might remain claimed by the processes executing them. Processes send heartbeats, and the supervisor checks and prunes processes with expired heartbeats, which will release any claimed jobs back to their queues. You can configure both the frequency of heartbeats and the threshold to consider a process dead. See the section below for this.
160154

161155

162-
### Dedicated database configuration
163-
164-
Solid Queue can be configured to run on a different database than the main application.
165-
166-
Configure the `connects_to` option in `config/application.rb` or your environment config, with the custom database configuration that will be used in the abstract `SolidQueue::Record` Active Record model.
167-
168-
```ruby
169-
# Use a separate DB for Solid Queue
170-
config.solid_queue.connects_to = { database: { writing: :solid_queue_primary, reading: :solid_queue_replica } }
171-
```
172-
173-
Add the dedicated database configuration to `config/database.yml`, differentiating between the main app's database and the dedicated `solid_queue` database. Make sure to include the `migrations_paths` for the solid queue database. This is where migration files for Solid Queue tables will reside.
174-
175-
```yml
176-
default: &default
177-
adapter: sqlite3
178-
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
179-
timeout: 5000
180-
181-
solid_queue: &solid_queue
182-
<<: *default
183-
migrations_paths: db/solid_queue_migrate
184-
185-
development:
186-
primary:
187-
<<: *default
188-
# ...
189-
solid_queue_primary:
190-
<<: *solid_queue
191-
# ...
192-
solid_queue_replica:
193-
<<: *solid_queue
194-
# ...
195-
```
196-
197-
Install migrations and specify the dedicated database name with the `--database` option. This will create the Solid Queue migration files in a separate directory, matching the value provided in `migrations_paths` in `config/database.yml`.
198-
199-
```bash
200-
$ bin/rails g solid_queue:install --database solid_queue
201-
```
202-
203-
Note: If you've already run the solid queue install command (`bin/rails generate solid_queue:install`) without a `--database` option, the migration files will have already been generated under the primary database's `db/migrate/` directory. You can remove these files and keep the ones generated by the database-specific migration installation above.
156+
### Database configuration
204157

205-
Finally, run the migrations:
158+
You can configure the database used by Solid Queue via the `config.solid_queue.connects_to` option in the `config/application.rb` or `config/environments/production.rb` config files. By default, a single database is used for both writing and reading called `queue` to match the database configuration you set up during the install.
206159

207-
```bash
208-
$ bin/rails db:migrate
209-
```
160+
All the options available to Active Record for multiple databases can be used here.
210161

211162
## Lifecycle hooks
212163

@@ -235,8 +186,8 @@ SolidQueue.on_stop { stop_metrics_server }
235186

236187
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.
237188

238-
239189
### Other configuration settings
190+
240191
_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`
241192

242193
There are several settings that control how Solid Queue works that you can set as well:
@@ -261,11 +212,13 @@ There are several settings that control how Solid Queue works that you can set a
261212
- `enqueue_after_transaction_commit`: whether the job queuing is deferred to after the current Active Record transaction is committed. The default is `false`. [Read more](https://github.com/rails/rails/pull/51426).
262213

263214
## Errors when enqueuing
215+
264216
Solid Queue will raise a `SolidQueue::Job::EnqueueError` for any Active Record errors that happen when enqueuing a job. The reason for not raising `ActiveJob::EnqueueError` is that this one gets handled by Active Job, causing `perform_later` to return `false` and set `job.enqueue_error`, yielding the job to a block that you need to pass to `perform_later`. This works very well for your own jobs, but makes failure very hard to handle for jobs enqueued by Rails or other gems, such as `Turbo::Streams::BroadcastJob` or `ActiveStorage::AnalyzeJob`, because you don't control the call to `perform_later` in that cases.
265217

266218
In the case of recurring tasks, if such error is raised when enqueuing the job corresponding to the task, it'll be handled and logged but it won't bubble up.
267219

268220
## Concurrency controls
221+
269222
Solid Queue extends Active Job with concurrency controls, that allows you to limit how many jobs of a certain type or with certain arguments can run at the same time. When limited in this way, jobs will be blocked from running, and they'll stay blocked until another job finishes and unblocks them, or after the set expiry time (concurrency limit's _duration_) elapses. Jobs are never discarded or lost, only blocked.
270223

271224
```ruby
@@ -331,35 +284,15 @@ failed_execution.discard # This will delete the job from the system
331284
However, we recommend taking a look at [mission_control-jobs](https://github.com/rails/mission_control-jobs), a dashboard where, among other things, you can examine and retry/discard failed jobs.
332285

333286
## Puma plugin
287+
334288
We provide a Puma plugin if you want to run the Solid Queue's supervisor together with Puma and have Puma monitor and manage it. You just need to add
335289
```ruby
336290
plugin :solid_queue
337291
```
338292
to your `puma.rb` configuration.
339293

340-
341-
## Jobs and transactional integrity
342-
: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. 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.
343-
344-
By default, Solid Queue runs in the same DB as your app, and job enqueuing is _not_ deferred until any ongoing transaction is committed, which means that by default, you'll be taking advantage of this transactional integrity.
345-
346-
If you prefer not to rely on this, or avoid relying on it unintentionally, you should make sure that:
347-
- You set [`config.active_job.enqueue_after_transaction_commit`](https://edgeguides.rubyonrails.org/configuring.html#config-active-job-enqueue-after-transaction-commit) to `always`, if you're using Rails 7.2+.
348-
- Or, your jobs relying on specific records are always enqueued on [`after_commit` callbacks](https://guides.rubyonrails.org/active_record_callbacks.html#after-commit-and-after-rollback) or otherwise from a place where you're certain that whatever data the job will use has been committed to the database before the job is enqueued.
349-
- Or, you configure a different database for Solid Queue, even if it's the same as your app, ensuring that a different connection on the thread handling requests or running jobs for your app will be used to enqueue jobs. For example:
350-
351-
```ruby
352-
class ApplicationRecord < ActiveRecord::Base
353-
self.abstract_class = true
354-
355-
connects_to database: { writing: :primary, reading: :replica }
356-
```
357-
358-
```ruby
359-
config.solid_queue.connects_to = { database: { writing: :primary, reading: :replica } }
360-
```
361-
362294
## Recurring tasks
295+
363296
Solid Queue supports defining recurring tasks that run at specific times in the future, on a regular basis like cron jobs. These are managed by dispatcher processes and as such, they can be defined in the dispatcher's configuration like this:
364297
```yml
365298
dispatchers:

db/migrate/20231211200639_create_solid_queue_tables.rb

Lines changed: 0 additions & 100 deletions
This file was deleted.

db/migrate/20240110143450_add_missing_index_to_blocked_executions.rb

Lines changed: 0 additions & 5 deletions
This file was deleted.

db/migrate/20240218110712_create_recurring_executions.rb

Lines changed: 0 additions & 14 deletions
This file was deleted.

db/migrate/20240719134516_create_recurring_tasks.rb

Lines changed: 0 additions & 20 deletions
This file was deleted.

db/migrate/20240811173327_add_name_to_processes.rb

Lines changed: 0 additions & 5 deletions
This file was deleted.

0 commit comments

Comments
 (0)