あるとき、こんなログが出ていました。
User message saved successfully
User message saved successfully
同じ行が、同じ時刻に2回。「これ、処理重複してる?」と確認を頼まれたのが始まりでした。
ログの見え方に引きずられない
ログだけ見ると「保存処理が2回呼ばれている」と思います。でもコードを追うと、呼び出し自体は1回でした。関数の中にログ出力が2箇所書いてあっただけ。このときの書き込みは1回しか走っていなかった。つまり、このログの重複自体は単なる出力の重複で、データは2件入っていません。
ただ、ここで「じゃあ問題なし」と片付けるのは早い。ログ出力を整理すれば見た目はきれいになりますが、肝心の「同じ通知が2回来たときに本当に守れているか」は別の話です。
実際このシステムは、連続して投稿すると保存が取りこぼされる、という別の症状も出ていました。ログの重複とは逆向きの問題ですが、根っこは同じで、「同じ処理がほぼ同時に走ったとき、保存をどう確定させるか」を決めていないこと。ログの見え方に気を取られず、保存の仕組み自体を見直す必要がありました。
なぜ重複や取りこぼしが起きるのか
このシステムは外部サービスからの通知(Webhook)を受けて処理を動かしていました。Webhookというのは「何か起きたら外から叩きに来る仕組み」のことです。
問題は、同じ通知が短時間に2回届くことがあること。外部サービス側でリトライがかかったり、ネットワークの都合でほぼ同時に2つのリクエストが来たりします。受け取る側が何も気にせず処理すると、同じ保存が2回走ったり、逆に書き込みがぶつかって片方が取りこぼされたりする。
フォームの申請フローも同じです。ユーザーが「送信」ボタンを2回押した、Googleフォームの送信がタイムアウトして再試行された、スプレッドシートへの書き込みトリガーが競合した。どれも同じ構造の問題です。
AIにどう頼んで直したか
コードを渡してAIに頼みました。
同じメッセージが2回保存される問題がある。ユニークなIDを発行して、「すでにそのIDで保存済みなら何もしない」という処理を追加してほしい。保存と確認は不可分にして、割り込みが入っても大丈夫なようにしたい。
返ってきた方針はシンプルでした。書き込む前に「そのIDのデータが既に存在するか」を確認する。存在すれば何もせず終わる。存在しなければ保存する。この確認と保存を、割り込みが起きないひとまとまりの操作として実行する。
言葉にすると当たり前に聞こえます。ただ、これを「割り込みが起きないひとまとまり」として実装するところが肝で、普通に書くと確認と保存の間に別のリクエストが入り込めます。そこをデータベースのトランザクション機能で塞ぐ。
ユニークIDの設計が先
もう一つ気づいたのは、「何を重複とみなすか」をちゃんと決めないと検知できないことです。
タイムスタンプだけでIDを作ると、ほぼ同時に来た2件が同じIDになりません。全く別のレコードとして扱われてしまう。今回のケースでは、外から来るリクエスト自体が持っているID(メッセージIDなど)をそのままキーにすることで解決しました。
GASのフォームフローなら、フォームの回答IDやタイムスタンプ+送信者IDの組み合わせをキーにする、という発想です。「同じ申請を2件とみなすルール」を先に決める。ルールがないと重複チェックは作れません。
必要な3点セット
この問題を直すときに必要なのは、たいてい3つです。
- 申請・リクエストに一意のIDを振る(外部サービスが持っているIDがあればそれを使う)
- 保存処理の前に「このIDで既に処理済みか」を確認する
- 確認と保存をひとまとまりの操作にする(GASならLockServiceやトランザクション)
GASには「スクリプトロック」という仕組みがあって、同時に同じ処理が走るのを防げます。「ロックを取れなければ即座に終了する」と書いておくと、2件目が来ても処理に入れずスキップされます。
どこで詰まるか
一番引っかかりやすいのは、「そもそも重複が起きているか分からない」段階です。今回も最初はログの見え方の話だと思い込んでいました。ログの重複は、必ずしも保存の重複を意味しません。
疑わしいと思ったら、ログより先に実際のデータ件数を確認するほうが早い。本当に重複していれば、タイムスタンプが数秒以内で内容が同じレコードが並んでいます。逆に、件数が足りなければ取りこぼしの側を疑う。どちらにせよ、現物のデータを数えるのが一番確実です。
修正後の確認も省略しないほうがいいです。同じ状況を意図的に再現して、同時に2件送って保存が1件だけになっていれば直っている。「たぶん直った」で本番に出すと、次の相談は「データが2件入ってるんですが」になります。
「処理が2回走る気がする」という相談、わりと頻繁にあります。ほとんどの場合、仕組みで防げます。
