はじめに
この記事はプログラミング学習中に得た知見を備忘録としてまとめたものです。
知っている人にとっては当たり前であったり理解が足りていないと感じられる内容も含むかと思いますので、あらかじめご了承ください。
この記事で分かること
- Railsにおいてテーブルの主キーにはAUTO INCREMENTが設定されています。
- そのため、値がuniqueになるように自動採番されます
-
$ rails db:reset
でテーブルの再構築をした際、開発環境はAUNO INCREMENTは自動的にリセットされますが、本番環境(Heroku)ではリセットされません - そのため、ゲストユーザーなど初期データで投入するとしても、テーブルのidカラムに依存した処理はNGです。
環境
ローカル環境
- Ruby 6.1.4
- Rails 3.0.2
- mysql2 0.5.3
本番環境
- Heroku
- ClearDB MySQL
背景
####ポートフォリオの作成においてゲストログイン機能を実装
すでにHerokuにデプロイしているアプリケーションにおいて、ゲストログイン機能を新たに実装します
手始めに、users tableにboolean型のguestカラムを作成します。
class AddGuestToUsers < ActiveRecord::Migration[6.1]
def change
add_column :users, :guest, :boolean, default: false
end
end
初期データとしてゲストユーザーを作成します。
# ゲストユーザーの作成
User.create!(
name: 'ゲストユーザー',
email: 'guest@example.com',
password: 'password',
password_confirmation: 'password',
guest: true,
)
コントローラーにゲストログイン機能を実装します。
ちなみにユーザー登録関係にはdevise
を使用しています。
# ゲストログイン
def guest_sign_in
guest_user_id = 1
guest_user = User.find(guest_user_id)
sign_in guest_user
redirect_to root_path, notice: 'ゲストユーザーとしてログインしました。'
end
Herokuに変更を反映し、データベースの再構築を行います。
# herokuアプリケーションに変更をpushする
$ git push heroku master
# herokuのmysqlデータベースの削除
$ heroku run rails db:migrate:reset RAILS_ENV=production DISABLE_DATABASE_ENVIRONMENT_CHECK=1
# マイグレーションの実行と初期データの投入
$ heroku run rails db:migrate
$ heroku run rails db:seed
本番環境でゲストログインが失敗
ここで、ゲストログイン用のリンクをクリックしたところエラーが表示されました。
The Page you were looking for doesn't exist
ログの確認をするとid=1のレコードが見つからないとのことです。
$ heroku logs
~
ActiveRecord::RecordNotFound (Couldn't find User with 'id'=1):
~
原因調査
疑問点
-
seed.rb
ではguest_userしか作成していないのにid=1のデータがないのはなぜか? - そもそもデータベースはちゃんと作成できているのか?
本番環境のデータベース再構築がうまくいっているか確認する
まずどこに原因があるのか調べるために、本番環境で使用しているMySQLのデータベースに接続を行います。
本番環境のHOST_NAMEやPASSWORDなどはheroku configにより確認できます。
MySQLへの接続にはこちらの記事を参考にしています。
# herokuのデータベース設定を確認する
% heroku config
=== beans-cafe Config Vars
~
DB_HOSTNAME: [hostname]
DB_PASSWORD: [password]
DB_USERNAME: [username]
~
# mysqlサーバーを起動
$ mysql.server start
# 本番環境で使用しているデータベースにログイン
$mysql -u username -h hostname -p
> password
まずちゃんとデータベースの再構築ができているのか確認します。
$ heroku run RAILS_ENV=production DISABLE_DATABASE_ENVIRONMENT_CHECK=1 bundle exec rails db:drop
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
+--------------------+
データベースはちゃんと削除されているようなので、herokuでデータベースの作成します。
$ heroku run rails db:create
mysql> show databases;
------------------------+
| Database |
+------------------------+
| information_schema |
| heroku_xxx |
+------------------------+
どうやらデータベースの作成も無事にできているようです。
それでは中身はどうなっているでしょうか?
ちなみにこの原因究明中はClearDBのバックアップデータが邪魔していると考えてました
勝手にデータの中身が作られているのでは?と。
mysql> use heroku_xxx;
mysql> show tables;
Empty set (0.18 sec)
テーブルはなし。
マイグレーションを実行してテーブルを作成します。
$ heroku run rails db:migrate
mysql> show tables;
Current database: heroku_xxx
+----------------------------------+
| Tables_in_heroku_xxx |
+----------------------------------+
| ~ |
| users |
+----------------------------------+
問題なくusersテーブルが作成できています。
最後に初期データの投入をします。
$ heroku run rails db:seed
mysql> select id, email from users;
+----+-------------------+
| id | email |
+----+-------------------+
| 5 | guest@example.com |
+----+-------------------+
なぜidがリセットされていないのか?
ここでようやく原因の糸筋を見つけることができました。
初期データで投入したデータにも関わらず、idが5になっています。
開発環境では問題なく機能していたので考えていませんでしたが、どうやらRailsでMySQLを使用した場合、主キーに対してAUTO INCREMENT制約がかかっているようです。
こちらの記事の回答が分かりやすいです。
開発環境ではrails db:reset
でAUTO INCREMENTがリセットされていましたが、本番環境ではリセットされておらず、初期データとして作成されたゲストユーザーのidがデータベース再構築前から引き継がれていたようです。
解決方法
####ゲストログイン機能の見直し
データベースのAUTO INCREMENTをリセットする方法もあると思いますが、ゲストログイン機能を変更します。
そもそも全くidで検索する必要がない。。。なんのためのguestカラム
# ゲストログイン
def guest_sign_in
guest_user = User.find_by(guest: true)
sign_in guest_user
redirect_to root_path, notice: 'ゲストユーザーとしてログインしました。'
end
結論
- データベースの主キーにはAUTO INCREMENTのようにユニーク性を保つ仕様がある
- データの特定のidに依存した処理は環境変化の影響を受けやすい
- idはデータの重複を避ける大きな役割を担っているため、他の役割は別のカラムに担わせる方が無難
参考記事