1
0

More than 1 year has passed since last update.

<ゲストログイン機能>ゲストユーザーの検索にidを使用するのは避ける方が良い

Posted at

はじめに

この記事はプログラミング学習中に得た知見を備忘録としてまとめたものです。
知っている人にとっては当たり前であったり理解が足りていないと感じられる内容も含むかと思いますので、あらかじめご了承ください。

この記事で分かること

  • 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カラムを作成します。

/db/migrate/xxxx_add_guest_to_users.rb
class AddGuestToUsers < ActiveRecord::Migration[6.1]
  def change
    add_column :users, :guest, :boolean, default: false
  end
end

初期データとしてゲストユーザーを作成します。

/db/seeds.rb
# ゲストユーザーの作成
User.create!(
  name: 'ゲストユーザー',
  email: 'guest@example.com',
  password: 'password',
  password_confirmation: 'password',
  guest: true,
)

コントローラーにゲストログイン機能を実装します。
ちなみにユーザー登録関係にはdeviseを使用しています。

/app/controllers/users/sessions_controller.rb
# ゲストログイン
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
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
+--------------------+

データベースはちゃんと削除されているようなので、herokuでデータベースの作成します。

ターミナル
$ heroku run rails db:create
mysql
mysql> show databases;
------------------------+
| Database               |
+------------------------+
| information_schema     |
| heroku_xxx             |
+------------------------+

どうやらデータベースの作成も無事にできているようです。

それでは中身はどうなっているでしょうか?
ちなみにこの原因究明中はClearDBのバックアップデータが邪魔していると考えてました:disappointed_relieved:
勝手にデータの中身が作られているのでは?と。

mysql
mysql> use heroku_xxx;
mysql> show tables;
Empty set (0.18 sec)

テーブルはなし。
マイグレーションを実行してテーブルを作成します。

ターミナル
$ heroku run rails db:migrate
mysql
mysql> show tables;
Current database: heroku_xxx
+----------------------------------+
| Tables_in_heroku_xxx             |
+----------------------------------+
| ~                                |
| users                            |
+----------------------------------+

問題なくusersテーブルが作成できています。
最後に初期データの投入をします。

ターミナル
$ heroku run rails db:seed
mysql
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カラム:rolling_eyes:

/app/controllers/users/sessions_controller.rb
# ゲストログイン
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はデータの重複を避ける大きな役割を担っているため、他の役割は別のカラムに担わせる方が無難

参考記事

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0