📖
テーブル設計 Tips 日記
最近知った Tips だけど、名前が分からないので日記にしたw
問題
以下のようなテーブルがある
Foo |
---|
id |
Table A |
---|
id |
foo_id |
status (open or close) |
Foo は複数の Table A レコードを持つ。 Table A の status は open か close の2種類。
特定の foo_id に対して open なレコードは常に一つで、 close のレコードは複数できる。
この場合、どのカラムにも uniq 制約を貼れないため、Table A のレコードを find_or_create_by したい場合に同時アクセスがあると open なレコードが重複してしまう。
ロックをかけないとしたら、どう解決するか。
1つの解決策
親テーブルを作成し last_table_a_id
を持たせて、foo_id にユニーク制約をつける。
Parent Table |
---|
id |
foo_id (uniq) |
last_table_a_id |
Table A |
---|
id |
status (open or close) |
そして、実際の作成処理では以下のようにトランザクションで囲む。
current_parent_table = ParentTable.find_or_create_by!(foo_id: foo_id) # 重複してたらエラー
return "オープン済み" if current_parent_table.last_table_a&.status == "open"
ActiveRecord::Base.transaction do
new_table = TableA.create
# 別の人が先に Table A 作って古い方を参照していたら、 last_table_a_id がミスマッチで更新できない
raise "Created by Other" unless ParentTable
.where(id: current_parent_table.id, last_table_a_id: current_parent_table.last_table_a_id)
.update_all(last_table_a_id: new_table.id) == 1
end
こんな方法もあるのねと思った案でした。
Discussion