Rails 8のSolid系gem(Queue/Cache/Cable)のdatabase.ymlの書き方【本番・DATABASE_URL対応】

  • このエントリーをはてなブックマークに追加

スポンサーリンク

Rails 8で rails new すると、config/database.yml に primary / queue / cache / cable という4つのデータベース設定が並びます。開発環境では何も気にせず動くのに、いざ本番にデプロイしようとすると「この4つ、環境変数でどう設定すればいいの?」と手が止まった経験はありませんか?

この記事では、Rails 8で標準になった Solid 系 gem(solid_queue / solid_cache / solid_cable)を本番で動かすときの database.yml の書き方を、環境変数を最小限に保つという観点で整理します。よくある3つの選択肢を比べたうえで、私が実際に採用している「DATABASE_URL 1本で回す」設定まで具体的に紹介します。

そもそもSolid系gemとは

Rails 8では、以下の3つの gem がデフォルトで組み込まれるようになりました。いずれも、これまで Redis などのミドルウェアが担っていた役割を、データベースだけで完結させるために作られています。

  • solid_queue … Active Job のバックエンド(バックグラウンドジョブ)
  • solid_cache … Rails.cache のストア(キャッシュ)
  • solid_cable … Action Cable のアダプター(WebSocket)

ねらいはシンプルで、「Redis を立てなくても本番アプリが動く」状態を標準にすることです。運用するミドルウェアが1つ減るのは、少人数のチームにとって地味に大きなメリットです。監視対象もバックアップ対象もデータベースだけで済みます。

Rails 8を Kamal でAWSにデプロイするような構成でも、この設定がそのまま効いてきます。

Rails 8がデフォルトで生成するdatabase.yml

まず、rails new したときに production ブロックに生成される database.yml を見てみましょう。primary のほかに queue / cache / cable の3つが追加されているのがポイントです。

production:
  primary: &primary_production
    <<: *default
    database: app_production
    username: app
    password: <%= ENV["APP_DATABASE_PASSWORD"] %>
  cache:
    <<: *primary_production
    database: app_production_cache
    migrations_paths: db/cache_migrate
  queue:
    <<: *primary_production
    database: app_production_queue
    migrations_paths: db/queue_migrate
  cable:
    <<: *primary_production
    database: app_production_cable
    migrations_paths: db/cable_migrate

このように、Solid 系はそれぞれ 別のデータベース(テーブル群)を使う前提になっています。テーブル名は app_production_queue のように、primary の名前の末尾に _queue / _cache / _cable が付くのが標準です。

なお、公式ドキュメントでも「別データベースを推奨」としつつ、1つのデータベースにまとめる運用もサポートされています。この記事の後半で、どれを選ぶべきかも触れます。

本番で直面する問題:環境変数をどう設定する?

本番のデータベース接続情報は秘匿情報なので、database.yml に直書きせず環境変数で渡すのが基本です。ここで Rails の仕様を1つ押さえておきましょう。

Rails は DATABASE_URL という環境変数があれば、その内容を優先して primary の接続に使うように作られています(Railsガイド)。中身はこんな形式のURLです。

postgresql://app:password@db.example.com:5432/app_production

問題は、Solid 系がそれぞれ別のデータベースを見るため、接続先が primary だけでは足りないことです。この環境変数の渡し方には、大きく3つの選択肢があります。

選択肢1:接続名ごとに環境変数を分ける(Rails標準)

実は Rails には、「接続名を大文字にして _DATABASE_URL を付けた環境変数」を自動で対応する命名規約があります。queue という接続なら QUEUE_DATABASE_URL、cache なら CACHE_DATABASE_URL です。

# これらを設定すれば database.yml 側は url 指定すら不要
DATABASE_URL=postgresql://app:pass@host/app_production        # primary(PRIMARY_DATABASE_URL でも可)
QUEUE_DATABASE_URL=postgresql://app:pass@host/app_production_queue
CACHE_DATABASE_URL=postgresql://app:pass@host/app_production_cache
CABLE_DATABASE_URL=postgresql://app:pass@host/app_production_cable

これは最も Rails 標準に沿ったやり方で、Heroku や一部のPaaSではこの命名で設定するのが定番です。ただ、管理する環境変数が4つに増えるのが難点。同じホスト・同じ認証情報なのに、末尾のDB名だけ違うURLを4本も管理するのは、正直ちょっと冗長に感じます。

選択肢2:DATABASE_URL 1本で回す(おすすめ)

環境変数の数を増やしたくない場合は、DATABASE_URL 1本だけを管理し、Solid 系のURLは database.yml 内で文字列結合して作る方法があります。私はこれを採用しています。

production:
  primary: &primary_production
    <<: *default
    url: <%= ENV["DATABASE_URL"] %>
  cache:
    <<: *primary_production
    url: <%= ENV["DATABASE_URL"]&.+("_cache") %>
    migrations_paths: db/cache_migrate
  queue:
    <<: *primary_production
    url: <%= ENV["DATABASE_URL"]&.+("_queue") %>
    migrations_paths: db/queue_migrate
  cable:
    <<: *primary_production
    url: <%= ENV["DATABASE_URL"]&.+("_cable") %>
    migrations_paths: db/cable_migrate

ポイントを3つに分けて説明します。

  • url: を明示的に書くと、環境変数 DATABASE_URL がある場合でも、この設定で上書きされます。primary も含めて url で統一しておくのがコツです。
  • Solid 系は文字列結合でDB名を作る。DATABASE_URL の末尾に _queue などを足すことで、app_production → app_production_queue というURLを生成しています。
  • &.(ぼっち演算子)でnilガードしている理由は、development 環境などでも database.yml 全体が読み込まれるためです。DATABASE_URL が未設定の環境でも NoMethodError にならないようにしています。

これで、管理すべき環境変数はこれまで通り DATABASE_URL だけになります。デプロイ先に設定する秘匿情報が1つで済むのは、運用の手間とミスの両方を減らせるので気に入っています。

【注意】文字列結合が効かないケース

この文字列結合の方法には、1つ落とし穴があります。それは DATABASE_URL の末尾がデータベース名で終わっていることが前提だという点です。

たとえば、コネクションプール数などをクエリパラメータで付けている場合、単純に末尾へ _queue を足すと、パラメータの後ろにくっついて壊れてしまいます。

# DATABASE_URL の末尾が ?pool=5 のような場合
postgresql://app:pass@host/app_production?pool=5

# 単純に "_queue" を足すと…壊れる
postgresql://app:pass@host/app_production?pool=5_queue   # ← DB名ではなくパラメータ末尾に付く

この方法が使えるのは、DATABASE_URL にクエリパラメータを付けず、末尾がDB名で終わっている構成に限られます。プール数などは database.yml の default 側(pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>)で指定しておけば、URLをシンプルに保てるので相性が良いです。

クエリパラメータをどうしてもURLに含めたい場合は、無理に結合せず、素直に選択肢1(接続名ごとの環境変数)に切り替えるのが安全です。

選択肢3:1つのデータベースにまとめる

「そもそもDBを分けたくない」「小さいアプリなので運用をとにかく簡単にしたい」という場合は、Solid 系をすべて primary の1つのデータベースにまとめることもできます。

この場合は、Solid 系のマイグレーションを通常の db/migrate に取り込んだうえで、config/environments/production.rb などにある connects_to 系の設定を外し、database.yml の queue / cache / cable ブロックを消します。設定はこれだけシンプルになります。

production:
  primary:
    <<: *default
    url: <%= ENV["DATABASE_URL"] %>

ただし公式が別DBを推奨しているのには理由があります。ジョブやキャッシュの書き込みが本体のトランザクションやコネクションプールに影響を与えにくくなる、という分離のメリットです。負荷が上がってきたら分ける前提で、まずは1つで始める、という判断もアリでしょう。

結局どれを選ぶ?

3つの選択肢を、判断軸とあわせて整理します。

  • 選択肢1(接続名ごとの環境変数):URLにクエリパラメータを含める必要がある/PaaS側が推奨している場合。最も標準的で堅い。
  • 選択肢2(DATABASE_URL 1本+文字列結合):DBは分けたいが、管理する環境変数は増やしたくない場合。自前のサーバーやKamalでのデプロイと相性が良い。
  • 選択肢3(単一DB):小規模で、とにかく構成をシンプルにしたい場合。負荷が上がったら分離を検討する。

個人的には、DBは分離しつつ環境変数は1本に抑えられる選択肢2が、運用コストと分離のバランスが取れていて気に入っています。チームの規模やデプロイ先の作法に合わせて選んでみてください。

まとめ

Rails 8のSolid系gemは、Redis なしで本番を回せる便利な仕組みですが、database.yml の書き方は最初につまずきやすいポイントです。要点をおさらいします。

  • Rails 8は primary / queue / cache / cable の4つのDBを前提に生成される
  • 環境変数は「接続名ごとに分ける」か「DATABASE_URL 1本+文字列結合」か「単一DBにまとめる」の3択
  • 文字列結合はクエリパラメータ付きURLでは壊れるので注意

本番デプロイ全体の流れをまとめたい方は、Rails 8 × Kamal 2でAWSにデプロイする手順もあわせてどうぞ。これから Rails を始める方は macOSにRuby on Railsをインストールから読んでみてください。

スポンサーリンク

  • このエントリーをはてなブックマークに追加

コメント

Comments are closed.