記事一覧へ

保存は成功したのに、もう一度押すと500になる

運用ツールの関連付け処理を、同じ要求が再送されても壊れない形に直しました。重複データだけでなく、イベントも増やさないための見直しです。

自分で作っている運用ツールで、プロジェクトと対象データを関連付ける処理を直しました。一度目の保存は成功するのに、同じ操作を再送すると500エラーになる問題です。2026年8月の修正でした。

データベースには、すでに欲しかった関連付けが存在しています。それでも画面にエラーが出れば、使う人には保存できたのか分かりません。通信が遅いときにもう一度押すだけでも、この状態になり得ます。

「すでにある」を成功として扱う

この操作で欲しいのは、指定した関連付けが存在する状態です。同じ関連付けを二つ作る必要はありません。

そこで、すでに関連付けられている場合は成功を返すようにしました。初回に作成する場合と、再送で既存の状態を確認する場合を分けます。同じ要求を繰り返しても結果が変わらない性質は、冪等性と呼ばれます。

ここでいう成功は、エラーをまとめて握りつぶすことではありません。期待した関連付けがすでに存在する場合だけ、目的を達成済みと判断します。違う理由で保存できなかった場合まで成功にすると、今度は本当の失敗が見えなくなります。

行が増えなくても、イベントは増えてしまう

保存処理では、関連付けの作成に伴うイベントも扱っていました。データの重複を防ぐだけでは、再送のたびにイベントを余分に残す可能性があります。

修正では、既存の関連付けに対する再送で、新しい関連データもイベントも作らないようにしました。戻り値だけを見て成功と判断せず、その操作で何が増えたかまで確認します。

保存ボタンを一時的に無効にする対応も役立ちますが、それだけに頼りたくはありません。通信の再試行など、画面の操作以外から同じ要求が来ることもあるためです。

二度目をテストに含める

この修正では、繰り返し送った場合を含むテストを追加し、lintと本番向けビルドも確認しました。初回の成功だけを試していたら、見逃していた挙動です。

関連付けのように「ある状態にしたい」操作は、再送を考えやすい題材でした。一方、メール送信や決済のような操作では、どの要求を同じものとして扱うのかを別途決める必要があります。この修正をそのまま何にでも当てはめられるとは考えていません。

小さい不具合でしたが、保存処理を見るときに「もう一度届いたらどうなるか」という確認が一つ増えました。