再試行と冪等性:SQLiteの中断位置を変えて、欠落と二重処理を確かめる

「処理済みの印を付ければ十分か」「DBの外への処理も守れるか」を、同じ入力を使った7ケースで調べました。 同じDB内の確定と、外部への効果を分けて読むための学習資料です。 SAA・SAPで再試行を考える手掛かりにできますが、AWSサービスの動作を実測した結果ではありません。

コードと実行記録で確かめる

再試行で二重処理しない?SQLiteで停止位置を変えた7つの実験

同じ処理をやり直しても意図した結果が増えない性質を、ここでは冪等性として考えます。「処理済み」の印を付けるだけでは、欠落や二重反映を防げません。印とDBへの反映を別々に確定すると、途中で止まった位置によって結果が変わります。ローカルのSQLite実ファイルを別プロセスから更新し、停止後に同じイベントを再処理して確かめました。

実行日:2026年9月26日。架空の固定入力を使った実測です。AWSサービスの実機検証、実利用者の学習成果、人による専門家監修ではありません。採点付き問題ではなく、収録問題数には含めません。

条件を固定して、止める位置を変える

  • 入力は毎回同じイベントID demo-event-001と「1を加える」という内容。各ケースは空のDBから開始します。
  • DB内の processed に処理済みID、effectsに反映を記録します。DB反映数は effects の合計です。
  • B〜EとGは決めた位置で子プロセスを強制終了し、新しい子プロセスで同じ入力を再処理します。Fだけは2つの処理を重ね、ロック競合後に明示的に再処理します。
  • D〜Gは処理済みIDの一意制約と BEGIN IMMEDIATEを使います。Gはこれに加え、DBトランザクションの外のファイルへ1行追記します。

観測環境:Python 3.12.0・SQLite 3.42.0・macOS(Darwin 25.6.0)。SQLiteはDELETEジャーナル・synchronous=FULL・ロック待ち0秒。入力と仮説は実行前に固定し、各段階でDBを読み戻しました。

生成AIを用いた別工程でも、新しい出力先へ再実行し、同じ7ケースの結果と保存したDBを照合しました。同じ環境での再現確認です。

保存したDBとファイルで観測した結果数字は「1回目の後 → 再処理の後」。Fは「1番目が未commit(別接続の読戻し) → 1番目のcommit後 → 再処理後」の3段階です。外部行は別ファイルへの追記数で、メール送信や決済の実行数ではありません。
方式と中断条件DB反映数外部行
A:重複を判定しない1 → 20 → 0
B:処理済み記録を先に確定0 → 00 → 0
C:DB反映を先に確定1 → 20 → 0
D:同じトランザクション・commit前に中断0 → 10 → 0
E:同じトランザクション・commit後に中断1 → 10 → 0
F:書込み競合後に明示的に再処理0 → 1 → 10 → 0 → 0
G:DBの外にも書き込み、commit前に中断0 → 11 → 2

A:同じ処理を2回呼ぶと、加算も繰り返す?

この実装では、同じ入力でも加算が重なりました。再処理を受け付けるだけでは、結果を1回分に保てません。

条件:ローカルSQLiteに合成入力(同じIDで1を加算)を2回渡す。処理済みの判定は行わない。

観測:1回目の後 → 再処理の後
DB反映数:1 → 2

確認の限界:通信障害やメッセージ配信回数を測った結果ではありません。

元の観測:結果JSONの case: A-no-dedup。observations の読戻し値を表示しています。

B:処理済みの印を先に付ければ安全?

印だけが残り、再処理が加算をスキップしました。重複を避けても、本来必要な更新を失う場合があります。

条件:ローカルSQLiteの合成入力(同じIDで1を加算)。処理済みIDだけを確定し、加算前に中断後、再処理する。

観測:1回目の後 → 再処理の後
DB反映数:0 → 0

確認の限界:この停止点での欠落です。すべての障害を再現したわけではありません。

元の観測:結果JSONの case: B-marker-first。observations の読戻し値を表示しています。

C:加算してから処理済みにすれば安全?

印がないため、確定済みの加算をもう一度行いました。Bと順番を逆にするだけでは、欠落を重複に変えることになります。

条件:ローカルSQLiteの合成入力(同じIDで1を加算)。加算だけを確定し、処理済みIDを残す前に中断後、再処理する。

観測:1回目の後 → 再処理の後
DB反映数:1 → 2

確認の限界:印と効果を別々に確定する、この実装と停止点での反例です。

元の観測:結果JSONの case: C-effect-first。observations の読戻し値を表示しています。

D:まとめて書いた後、commit前に止まると?

中断した変更は残らず、再処理で印と加算が一緒に確定しました。今回の入力と停止点では、欠落も二重反映も残りませんでした。

条件:ローカルSQLiteの合成入力(同じIDで1を加算)。印と加算を同じトランザクションに書き、commit前に中断後、再処理する。

観測:1回目の後 → 再処理の後
DB反映数:0 → 1

確認の限界:確認したのはプロセス中断です。電源断・ディスク故障は未検証です。

元の観測:結果JSONの case: D-atomic-before-commit。observations の読戻し値を表示しています。

E:commit済みでも、完了を返す前に止まると?

確定済みの印を見つけて加算をスキップし、DBの結果を維持しました。処理の完了が伝わらなくても、今回の再処理は二重反映しませんでした。

条件:ローカルSQLiteの合成入力(同じIDで1を加算)。印と加算をまとめてcommitし、完了を返す前に中断後、再処理する。

観測:1回目の後 → 再処理の後
DB反映数:1 → 1

確認の限界:外部APIへの副作用や、以前の応答結果を返す仕組みは未検証です。

元の観測:結果JSONの case: E-atomic-after-commit。observations の読戻し値を表示しています。

F:同時に来た2番目の処理も成功する?

別接続からは1番目の未確定の更新が見えません。2番目は競合で失敗し、commit後に呼び直すと処理済みの印を見つけました。

条件:ローカルSQLiteの合成入力(同じIDで1を加算)。1番目が未commit中に2番目はSQLITE_BUSY。commit後、明示的に再処理する。

観測:1番目が未commit(別接続の読戻し) → 1番目のcommit後 → 明示的な再処理後
DB反映数:0 → 1 → 1

確認の限界:自動再試行や、あらゆる並行実行順の安全性を確かめた結果ではありません。

元の観測:結果JSONの case: F-overlapping-workers。observations の読戻し値を表示しています。

G:DBをまとめて確定すれば、外部の効果も1回?

DB外の追記はrollbackで消えず、再処理で増えました。このDBのrollbackは、別ファイルへの追記を取り消しません。

条件:ローカルSQLiteの合成入力(同じIDで1を加算)。DB内の印と加算に加え別ファイルへ追記し、commit前に中断後、再処理する。

観測:1回目の後 → 再処理の後
DB反映数:0 → 1
DB外ファイル行数:1 → 2

確認の限界:外部行はローカルファイルの行数です。メール・決済・AWSでの実行数ではありません。

元の観測:結果JSONの case: G-side-effect-outside-db。observations の読戻し値を表示しています。

AWSの学習では、どこへつなげる?

AWS公式は、SQS標準キューで同じメッセージを複数回受け取る場合に備え、アプリを冪等に設計するよう案内しています。根拠:標準キューの少なくとも1回の配信と冪等性。

SQSでは受信だけではメッセージは削除されず、処理・削除が完了しないまま可視性タイムアウトが切れると再び受信できます。標準キューでは、そのタイムアウト中にも重複配信が絶対にないとは保証されません。根拠:可視性タイムアウトと処理後の削除。

SAAで疎結合や再試行、SAPで複数処理の失敗を考えるときは、受信・業務DBの確定・メッセージ削除を別々の操作として並べてみてください。次に、どこで止まると重複または欠落するかを書きます。上のSQLiteの値を、SQSの配信回数やAWSの保証として読み替えないことが大切です。

上のAWS仕様2点の資料確認日:2026年9月26日。ローカル実験の観測とは別の根拠です。

自分でも再現する

公開したソースはPython 3.12以降の標準ライブラリを使います。ソースを保存したフォルダーから、新しい出力先を指定して実行します。ネットワーク通信、AWSアカウント、認証情報は使いません。今回確認した実行環境は上記の1環境で、他のOSや版すべての互換性は未確認です。

python3 -I retry-boundaries.py --output evidence/run-1

既存の出力先は上書きしません。結果の countsは「処理済み行数・DB反映合計・外部ファイル行数」の順です。実行時刻やDBファイルのハッシュまで同じになることは要求せず、各段階の行と件数、子プロセスの終了・競合記録を比べてください。

配布するのはコードと観測記録です。実DBファイルは含みません。READMEのSQL確認は、自分で再現実行して生成したDBに対して行います。

検証したのは、この固定入力と7つの処理順、プロセス中断からの復帰です。電源断・ディスク故障、異なる入力内容に同じIDを使う場合、分散トランザクション、性能、AWS各サービスの配信保証は検証していません。学習時は、利用するAWSサービスの公式仕様と、アプリが責任を持つ範囲を別に確認してください。

制作・実行:PASS QUEST。説明と検証コードの作成には生成AIを使用しています。制作・確認範囲と訂正窓口から訂正を依頼できます。