RailsのテーブルのIDをULIDにする
ULIDとはいわゆるタイムスタンプ+ランダム要素(計hexで26文字)のID。前16文字でソート可能。加えてランダム要素が付いてるので推測困難になる。
例えばRailsで特に何も考えずにテーブル作成をするとIDがbigintだったりする。こうしたintのIDは公開ページのurlに使われたりすると推測可能なので簡単に全URLを抜き取られたりする。これが問題ない場合は良いが出来るだけ不穏なアクセスを防いでおきたい場合は多い。
そのためにUUIDを使うこともあるがUUIDだと推測困難性が低くソートできないので不便なことも多い。一部DBではパフォーマンスが落ちることもままある。ULIDを使うとそのあたりのユースケースを満たせるのでよく使われる。
以下はテーブルのIDをULIDに変更する流れをメモしていく。
ULID生成
code:rb
require 'ulid'
ULID.generate #=> 01ARZ3NDEKTSV4RRFFQ69G5FAV migrationファイルの作成
id: falseにすることと生成されるカラムの型をbinaryにするだけ。
code:rb
class CreateCategory < ActiveRecord::Migration6.1 def change
create_table :categories, id: false do |t|
t.binary :id, limit: 26, primary_key: true
t.timestamps
end
end
end
関連の方のmigrationファイルにも外部キーのidの変更を加える。
code:rb
class CreateProductCategories < ActiveRecord::Migration6.1 def change
create_table :product_categories do |t|
t.references :product,
type: :binary,
limit: 26,
null: false,
foreign_key: true
t.references :category,
type: :binary,
limit: 26,
null: false,
foreign_key: true
t.timestamps
end
end
end
idの生成を自動化
モデル側でレコードを作成するときに自動でULIDを生成してセットするようにする。
code:rb
require 'ulid'
module UlidPk
extend ActiveSupport::Concern
included { before_create :set_ulid }
def set_ulid
self.id = ULID.generate
end
end
このconcernをULID対応させたいテーブルのモデルでincludeする。
code:rb
class Category
include UlidPk
end
以上で対応完了。
その他
同様にRailsで実装してる記事
MySQL側で発番する方法もあるらしい。
巷の記事だとULIDのカラムの型をstringにしている記事があるが「文字列ではなくbinaryのまま使え。アプリケーション側での変換コストがあるので。」という意見があった。自分も同意見なのでVARBINARYで定義した。