学習することができます。
学ぶ目的
状態の値を1つ増やすと、読む側が知らない
応募の状態に「書類選考中」「面接中」「内定」があるところへ、「辞退」を足すとします。データベースに値を足すのは簡単です。困るのはその値を読んでいる側です。
- 状態ごとに件数を数える集計が、「辞退」をどこにも数えない
- 状態で分岐する画面が、知らない値で何も表示しない
- 通知や外部への連携が、想定外の値で落ちる
第1回の「読む処理が増える」が、ここで効いてきます。状態を足すときは、その状態を読んでいる場所を先に全部探すことから始めます。知らない値が来たときに「落ちる」のか「その他として扱う」のかも、最初に決めておきます。
値を数字で持つなら、間を空けて番号を振るのも1つの手です。naminori の会員の職種は 10・20・30… と10おきに番号を振り、「その他」は 900 にしてあります。あとから間に値を足しても、並び順と既存の番号が崩れません。
一意の制約は、重複が無いのを確かめてからかける
「同じ会員が同じ求人を2回お気に入りに入れられない」ように、一意(ユニーク)の制約を後からかけたいとします。重複がすでにあると、制約は作れません(手元で確認)。
ERROR: could not create unique index "index_favorites_on_member_id_and_job_id"
naminori では2026年7月に、いくつもの表に一意の制約を足しました。そのとき、本番で重複が0件だと確かめたものだけを対象にしています。お気に入りには重複があったので、いちばん古い1件を残して重複を消してからかけました。電話番号は重複があったため、制約をかけるのをやめています。
重い変更は、利用者を待たせる
データベースの変更の中には、終わるまで他の書き込みを待たせるものがあります。たとえば普通に索引(検索を速くするための目次)を作ると、作り終わるまでその表への書き込みが待たされます(手元で確認:別の接続からの書き込みが、待ち時間の上限で打ち切られた)。
表が小さいうちは一瞬です。でも、データが貯まった本番では数分かかることもあり、その間、登録や応募が止まります。
PostgreSQL には、書き込みを止めずに索引を作る CONCURRENTLY という方法があります。500万行の表で約2.3秒かかる索引作りの最中にも、書き込みは通りました。Rails では次のように書きます。
class AddUniqueIndexToFavorites < ActiveRecord::Migration[8.1]
disable_ddl_transaction!
def change
add_index :favorites, %i[member_id job_id],
unique: true, algorithm: :concurrently
end
end
ただし、この方法が途中で失敗すると、使えない索引が残ります(手元で確認)。失敗したら、その索引を消してからやり直します。
消す前に、使われていないことを確かめる
列や設定を消すのは、足すより危険です。消したあとで「まだ使っていた」と分かっても、戻せないことがあるからです。
- コードの中で、どこからも読まれていないかを探す
- 本番のデータで、最近も書き込まれていないかを見る
- 消すときは、まずコードから読まなくする変更を出し、それが本番で動いてから、データの側を消す(古いコードが動いている間に列が消えると、読む処理が失敗する)
戻せない変更は、戻せないと書く
既存の行を埋め直すような変更は、元の値が残っていなければ戻せません。naminori では、そうした変更には「戻せない」とコードに明記し、戻す操作を実行するとエラーで止まるようにしています。戻せると思って実行して、戻らないほうがずっと困るからです。
このレクチャーで学ぶこと
- 状態の値を足すときは、読む側を先に探す
- 一意の制約は、重複が無いのを確かめてから
- 重い変更と、書き込みを止めない作り方
- 消す前の確認と、戻せない変更の明記
質問
まだ質問は投稿されていません。
質問投稿
ログイン後に質問の投稿ができます。