既存のRails appにsolid_queueを入れてDBでJobを実行できるようにした
Rails8から導入されたsolid_*
の1つであるsolid_queue
を既存のRails appにいれることでActiveJobをRedisではなくDB(今回はSQLite)で実行できるようにします
以下の状況でsolid_queueを導入してみました
Rails 8.0.1
Ruby 3.4.1
cssbundling-rails 1.4.1
jsbundling-rails 1.3.1
solid_queue 1.1.2
導入
まずはGemfileに記述してインストールします
gem 'solid_queue'
その後Gemのインストール、solid_queueの初期ファイルの作成をします。
$ bundle
$ bin/rails solid_queue:install
create config/queue.yml
create config/recurring.yml
create db/queue_schema.rb
create bin/jobs
gsub config/environments/production.rb
4つのファイルと1つのファイルの書き換えが実行されました。
ファイルの中身
config/queue.yml
ではスレッド数やプロセス数などの設定を環境ごとに行うことが出来ます。
config/recurring.yml
はデフォルトでコメントアウトされていますが環境ごとに定期的に実行するJobやコマンドを指定することが出来ます。Jobに対して引数やいつ実行するかを指定出来て便利だなと思いました。
db/queue_schema.rb
でsolid_queueを実行するためのDBのschemaが記述されています。
以下のようにversion指定もあるので今後solid_queueがアップデートされてDBの構造が変わった際はここもバージョンアップされていくのかなと思います。
+ ActiveRecord::Schema[7.1].define(version: 1) do
...
+ end
bin/jobs
ではsolid_queueをcli経由で起動しています。内容的にはこのような内容です。
#!/usr/bin/env ruby
require_relative "../config/environment"
require "solid_queue/cli"
SolidQueue::Cli.start(ARGV)
config/environments/production.rb
では以下のようにadapterなどの設定がsolid_queueに置き換わっています。
# Replace the default in-process and non-durable queuing backend for Active Job.
+ config.active_job.queue_adapter = :solid_queue
+ config.solid_queue.connects_to = { database: { writing: :queue } }
Procfileで別プロセスで起動
既存のアプリ内ではProcfileを用いて複数プロセスを起動するようにしていたのでローカルで実行できるようにProcfile.dev
に以下のように書き込んでプロセスで実行できるようにします。
+ job: bin/jobs
pumaにpluginとしてsolid_queueを追加してrails serverと一緒に起動することも出来るそうです。
READMEのこちらにサンプルが書いてあります。
database.ymlの設定
今回は別のsqliteファイルで実行したいのでconfig/database.yml
にも追加で記述します。
development:
+ primary:
+ <<: *default
+ database: storage/development.sqlite3
+ queue:
+ <<: *default
+ database: storage/development_queue.sqlite3
+ migrations_paths: db/queue_migrate
これで準備が完了したので動作テストをしていきます。
動作テスト
ひとまずのJobなので簡単に作成します。
$ bin/rails g job TestJob
invoke rspec
create spec/jobs/test_job_spec.rb
create app/jobs/test_job.rb
TestJobの中身はこんな感じでとりあえず書いておきます。
class TestJob < ApplicationJob
queue_as :default
def perform(*args)
puts "Hello, SolidQueue! Args: #{args}"
end
end
このあとbin/jobs
を起動しておきます。自分の場合はProcfile.devに書いたのでbin/dev
でプロセスを起動しておきます。
$ bin/dev
そしてconsoleを通してJobをQueueに登録します。
$ bin/rails c
Loading development environment (Rails 8.0.1)
hoge-app(dev)> TestJob.perform_later("Argument")
こうすることでプロセスを起動したログで以下のような記述が確認出来ます。
00:00:00 job.1 | Hello, SolidQueue! Args: ["Argument"]
無事にSQLiteを通してJobを実行することができました🎉
perform_later
で実行出来ることも確認します。同様にconsoleで以下のようにして1分後に実行するようにQueueを登録します。
hoge-app(dev)> TestJob.set(wait: 1.minute).perform_later("Delayed")
そうすると1分後に以下のログが出力されます。
00:00:00 job.1 | Hello, SolidQueue! Args: ["Delayed"]
無事に動作確認が出来ました!
最後に
個人開発やスモールサイズのアプリなどでRedisを入れてJobを実行するのはオーバースペック感があったのですがDBを利用してJob実行出来るようになったのはとても便利だなと感じました。
またSQLiteを使えばFly.ioやrenderなどにデプロイするときにも同一machineの中で完結することが出来るので料金も抑えられるのが魅力的だなと感じました。
SQLiteだと競合しやすくなってしまうかなと心配でしたが別ファイルで実行出来るのであればその心配もなくなるでしょうし、いい選択肢が増えたなと感じました。
まだローカルでのみでしか動かしていないのでプロダクション環境で動かしたりFly.ioなどにデプロイしたときに何につまずくかも確認したいと思います。
Discussion