はじめに
ポイントを2つのシステム(POSとEC)で運用していると、いつかは必ず残高がズレます。通信が切れる、片方だけ処理が失敗する、再送が二重に走る。原因はさまざまですが、起きるものは起きるんです。
大事なのは「ズレないようにする」ことよりも、「ズレたときに正しい状態へ戻せる」こと。この記事では、監査ログを使って残高の突合と復旧をどう行うかを、実際の設計をもとに解説します。ログの持ち方ひとつで、障害対応の楽さがまるで変わってきます。
なぜ残高はズレるのか
システムをまたぐと「正解」が2つになる
POSにもECにもそれぞれ残高があります。両者が常に一致していれば問題ないのですが、片方の更新が成功し、もう片方が失敗すると、その瞬間に「正解」が2つ生まれます。
店頭で500ポイント利用の処理は成功
ECへの反映だけ通信エラーで失敗
どちらの数字も「そのシステムの中では正しい」ので、システム同士を見比べても正解は決まりません。これが突合を難しくしている根っこの部分です。
再送と順序の入れ替わり
もう一つの原因が、処理の再送と順序の乱れです。ネットワークが不安定だと、同じ指示が二重に届いたり、後から出した指示が先に着いたりします。
残高という「今の値」だけを持っていると、こうした乱れは吸収できません。一方、増減を1つずつ記録したイベントログがあれば、順序を時刻で並べ直し、重複を弾いて、正しい積み上げをやり直せます。ログは、乱れた現実を整え直すための台本のようなものなんです。
ログから正しい状態を再構築する
残高はイベントの合計として導く
復旧の基本方針はシンプルです。壊れた残高を人間が推測して直すのではなく、イベントログを最初から合計し直して、あるべき残高を計算する。これに尽きます。
その顧客の付与・消費・変換をすべて取り出す
occurred_at で正しい順序に整える
同じ一意キーのイベントは1つにまとめる
増減を足し合わせた値が、あるべき残高
こうして出した「あるべき残高」を正とし、ズレていたシステムの数字を上書きします。人の勘ではなく履歴という事実から答えを出せるので、対応に自信が持てます。
balance_after で突合を高速化する
イベントごとに「操作後の残高(balance_after)」を記録しておくと、突合が一気に速くなります。全イベントを合計しなくても、「最後のイベントの balance_after が、いまの残高と合っているか」を見るだけで異常を検知できるからです。
チェックポイントとしての残高スナップショット
毎回すべてを合計するのは、イベントが増えると重くなります。各イベントに残高を刻んでおけば、それが道中のチェックポイントになり、「どのイベントからズレ始めたか」まで素早く特定できます。
差分が出た地点が分かれば、原因調査の範囲もそこに絞れます。「どこかでズレた」ではなく「この付与から狂った」と言えるのは、対応スピードに直結します。
冪等性が復旧を安全にする
復旧処理そのものも、失敗して再実行されることがあります。このとき復旧処理に冪等性がないと、直したはずの残高をまた二重に動かしかねません。
だからこそ、イベント記録の段階で一意キーによる重複防止を効かせておくことが、復旧の安全性にもつながります。「同じ操作は何度流しても結果は1回分」という性質が、障害対応の再実行を怖くなくしてくれるんです。
突合を運用に組み込む
定期的な自動突合
障害はいつ起きるか分かりません。そこで、夜間などに定期的にPOSとECの残高を突き合わせ、ズレがあれば検知する仕組みを用意しました。人が気づく前にシステムが気づく状態を作るわけです。
復旧はログを正とする一択に
運用ルールとして、「迷ったらログを正とする」を徹底しました。POSが正か、ECが正か、と議論を始めると復旧は進みません。すべての増減が残る監査ログこそが唯一の事実の源だと決めておく。
この一貫した方針があるおかげで、障害時に慌てず、機械的に正しい状態へ戻せます。ログを信じられる設計にしておくことが、結局は現場をいちばん助けてくれます。
まとめ
残高がズレることは避けられません。だからこそ、監査ログを「正しい状態を復元するための元帳」として持っておくことが効いてきます。
- 残高はイベントの合計として再計算する — 勘ではなく事実から直す
- balance_after で突合を高速化する — ズレ始めた地点を素早く特定
- 冪等性で復旧の再実行を安全にする — 何度流しても結果は1回分
このログの前提となるイベントスキーマは「付与・消費・変換のイベント記録設計」で、全体像はハブ記事「ポイント監査ログ」で解説しています。