はじめに
Railsアプリケーションを運用していると、ActiveRecord::RecordNotUnique という例外に遭遇することがあります。
「ユニーク制約違反なんて発生させちゃダメでしょ!」と思う方もいれば、「競合状態だから仕方ないよね」と考える方もいるでしょう。
この記事では、実際のところどのくらいの頻度なら許容範囲なのかについて、考え方を整理します。
TL;DR
- 完全に防ぐのは困難(分散システムの性質上)
- 業界標準の閾値は存在しない(システムの性質により大きく異なる)
- 重要なのは「想定内かどうか」「ビジネスへの影響」「パフォーマンスへの影響」
- 適切なハンドリングとモニタリングが最重要
なぜ RecordNotUnique の発生は避けられないのか
競合状態(Race Condition)
複数のリクエストが同時に同じレコードを作成しようとする場合、アプリケーションレベルのバリデーションでは完全には防げません。
たとえば、2つのリクエストが同時に「重複チェック」を通過してしまい、その後でデータベースに書き込もうとすると、片方は成功し、もう片方でユニーク制約違反が発生します。
分散システムの特性
複数のアプリケーションサーバーが並行して動作している環境では、さらに競合が発生しやすくなります。サーバーAとサーバーBが、それぞれ独立してバリデーションを行うため、同じレコードを同時に作成しようとする可能性があります。
許容できる発生頻度の目安
⚠️ 前提:明確な業界標準は存在しない
重要: ここで示す考え方は、公式なガイドラインや研究に基づくものではなく、一般的なシステム運用における目安です。実際の許容範囲は以下の要因で大きく変わります:
- システムの性質(ユーザー登録 vs API連携 vs トークン生成など)
- ビジネスへの影響(ユーザー体験が損なわれるか)
- パフォーマンスへの影響(データベース負荷)
- 設計における想定(意図的にリトライを組み込んでいるか)
判断のための考え方
以下は、あくまで考え方の一例として参考にしてください。
🟢 ほとんど発生しない
目安: 月に数回程度
- 本当に稀な競合状態
- 監視ログに記録しておく程度でOK
- ビジネスロジックとして適切にハンドリングできていれば問題なし
🟡 定期的に発生している
目安: 日に数回〜数十回
- 設計を見直す余地があるかもしれない
- ただし、トラフィックが多ければある程度は自然な範囲
- モニタリングを強化し、傾向を観察
- ビジネスへの影響を評価する
🔴 頻繁に発生している
目安: 時間あたり数十回以上、または総リクエスト数に対して目立つ割合
- 明らかに設計に問題がある可能性
- データベースに不要な負荷
- パフォーマンスにも悪影響
- 原因の調査と対策が必要
状況別の判断基準
あなたのシステムでの適切な閾値を決めるには、以下の観点で評価しましょう:
- ユーザー体験への影響: エラー画面が表示されるか、透過的に処理されるか
- データベース負荷: リトライによる無駄なクエリが発生していないか
- ログの肥大化: 調査やアラート対応の工数が増えていないか
- 設計の意図: そもそもリトライを前提とした設計か
ケース1: ユーザー登録(メールアドレス)
判断ポイント: ほぼ発生しないはず
- ユーザーは通常、自分のメールアドレスを登録する
- 頻発する場合は以下を疑う:
- フロントエンドの二重送信
- ボットによる攻撃
- UIの問題(登録ボタンの連打など)
ケース2: 外部APIのデータ同期
判断ポイント: 稀に発生(リトライ時など)
- リトライ処理で同じデータが複数回送られてくる可能性
-
find_or_create_byのような冪等な処理を使えば自然にハンドリング可能 - 設計として想定内なら、ある程度の発生は問題ない
どう対応すべきか
基本的な考え方
RecordNotUnique をエラーとして扱うのではなく、「既に存在する」という正常系の一つとして設計することが重要です。
対策のポイント
-
冪等性を持たせる:
find_or_create_byなどを活用 - 適切な例外ハンドリング: 発生時に既存レコードを返す
- フロントエンドでの二重送信防止: ボタン無効化など
- データベース制約とバリデーションの両方: 二重の防御
モニタリング
発生頻度を継続的に計測し、想定を超えたらアラートを出すようにしましょう。重要なのは、傾向を把握することです。