はじめに
現在、未経験からWebエンジニアを目指して、Rails 8でポートフォリオアプリ(デスク環境共有アプリ『@dEvicE』)を開発しています。
RenderとNeon(PostgreSQL)を使って無事に本番環境へデプロイできた!と喜んでいたのも束の間……。
いざアプリで画像アップロードなどの非同期処理(Active Job)を実行しようとすると、エラーでアプリが止まってしまいました。
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: relation "solid_queue_jobs" does not exist
LINE 10: WHERE a.attrelid = '"solid_queue_jobs"'::regclass
直訳すると 「 solid_queue_jobs なんていうテーブルは存在しないよ!」 というエラーです。
この記事では、Rails 8から標準搭載された「Solid Queue」と「Solid Cache」を、コストを抑えて 「1つのデータベース」で運用しようとした時にハマる罠 と、 安全な解決策 を備忘録としてまとめます。
同じように「無料枠のデータベース1つでRails 8を動かしたい!」という初学者の参考になれば嬉しいです。
🛠 実行環境
- Ruby: 3.3.10
- Ruby on Rails: 8.0.4
- ホスティング / DB: Render (Web Service) + Neon (PostgreSQL)
- 画像ストレージ: AWS S3 (IAMによるアクセス制御)
🔍 原因:Rails 8は「データベースを分ける」のが前提
エラーの原因はとてもシンプルで、 「非同期処理に必要なテーブルが本番のデータベースに作られていなかったから」 です。
Rails 8からは、非同期処理(Solid Queue)やキャッシュ(Solid Cache)が標準で使えるようになりました。公式ドキュメント通りに進めるなら、本番環境では「メインのデータ用」「キュー用」「キャッシュ用」とデータベース自体を3つに分けるのが理想的とされています。
しかし、個人開発でデータベースを3つも契約するのはコスト的に厳しい。
そこで今回は、 database.yml を工夫して、すべて同じ1つのデータベース(Neon)の中に間借りさせる設定にしていました。
😱失敗談:あわや全データ消失の危機!
そもそもの話、私自身データベースを3つに分けるという前提を知ったのが本エラーログが出たタイミングになるのです。
そこで、手っ取り早く解決するならば 「テーブルがないなら、コマンドで作ればいいじゃん!」 と考えました。
そうして私は、Renderのデプロイ設定(Build Command)で、公式ドキュメントにある以下のコマンドを実行しようとしました。
bundle exec rails db:schema:load:queue
しかし、デプロイ結果は大失敗。ログには次のような強烈な警告文が表示されていました。
bin/rails aborted!
ActiveRecord::ProtectedEnvironmentError: You are attempting to run a destructive action against your 'production' database. (ActiveRecord::ProtectedEnvironmentError)
直訳すると、 「本番環境のデータベースに対して、破壊的な操作を行おうとしています!!」 というRailsからの警告です。
なぜ怒られたのか?
実は db:schema:load というのは、 「対象のデータベースを一度空っぽ(全消去)にしてから、テーブルを綺麗に作り直す」 という超強力なリセットコマンドでした。
今回、1つのデータベースに「ユーザー情報」などのメインデータも同居させていたため、もしこのコマンドが実行されていたら、 今までの本番データが全て吹き飛ぶところでした。 Railsの安全装置(ProtectedEnvironmentError)が作動して、実行をブロックしてくれたおかげで大惨事にならずに済みました。
💡 解決策:マイグレーションファイルを手動で「追加」する
「リセットして作り直す(load)」のが危険なら、 「今のデータベースを壊さずに、必要なテーブルだけを"追加"する」 しかありません。
しかし、Railsには「キューやキャッシュのテーブル"だけ"を追加する」都合のいいコマンドは用意されていませんでした。
ネット上の記事を調べても解決策が見当たらず行き詰まりましたが、生成AIを活用してエラーの根本原因を分析。その結果、 「手動でマイグレーションファイルを作成し、Railsの裏側にあるSchemaファイルの中身を直接移植する」 という裏技的な手法を実行することにしました。
手順1: 空のマイグレーションファイルを作成
まずはターミナルで、テーブルを追加するための空のファイルを作ります。
bundle exec rails g migration AddSolidQueueAndCacheTables
手順2: schemaファイルからテーブル定義を移植
Railsが自動生成してくれている db/queue_schema.rb と db/cache_schema.rb というファイルを開きます。
その中にある create_table や add_foreign_key の部分を丸ごとコピーし、 手順1 で作った空のマイグレーションファイルに貼り付けます。
class AddSolidQueueAndCacheTables < ActiveRecord::Migration[8.0]
def change
# ===== Cache のテーブルを追加 =====
create_table "solid_cache_entries", force: :cascade do |t|
# ... (db/cache_schema.rb の中身をコピー) ...
end
# ===== Queue のテーブルを追加 =====
create_table "solid_queue_blocked_executions", force: :cascade do |t|
# ... (db/queue_schema.rb の中身をコピー) ...
end
# ... その他関連テーブルや外部キー制約もすべてコピーして貼り付ける ...
end
end
手順3: デプロイして db:migrate を実行
あとはこのコードをGitHubへプッシュし、Render側で通常通り bundle exec rails db:migrate が実行されるようにするだけです。
この方法なら、既存のメインデータは一切消去されず、安全に solid_queue_jobs などの必要なテーブルだけが本番環境のデータベースに追加されます!無事に画像アップロードも成功するようになりました🎉
🙇 おわりに
Rails 8の恩恵を手軽に受けられる一方で、実務で使われる「マルチデータベース構成」と、個人開発における「単一データベース構成」の違いを理解していないと、思わぬデータ消失の危機に直面することがよく分かりました。
エラー文をちゃんと読んで立ち止まることの大切さを身をもって学びました。
同じように、無料枠のデータベース1つでRails 8を運用しようとしてエラーに詰まっている方の参考になれば幸いです!
💡 ポートフォリオ紹介記事のご案内
今回のエラーを乗り越えて完成させた、デスク環境共有アプリ『@dEvicE』の全体像や、その他のこだわった機能については、以下の記事で詳しく紹介しています。ぜひご覧ください!
👉 【Rails8】 元溶接工が未経験でポートフォリオアプリ『@dEvicE』を作った話
まだまだ機能追加やコードの改善などやりたいことが山ほどあるので、今後も学習を継続し、より良いアプリへと改修していきます。
最後までお読みいただき、ありがとうございました!