APIが止まったとき、DBまで昨日の状態へ戻す必要はあるでしょうか。もし現在のDBに正常な登録が残っていれば、その復元で新しいデータを失うおそれがあります。
2026年10月26日、架空のタスク管理APIを使い、APIプロセスを止めて再起動した後も、SQLiteに確定済みのデータを取得できるか確認しました。この記事は「AI業務システムの設計演習・全12回」の第9回です。ローカルの個人学習で得た結果から、復旧時にどこまで操作するかを整理します。
最初に分けるのは、止まった処理と残っている記録
今回の構成は、1台のPCで動くFastAPIと、ファイルに保存するSQLiteです。APIが要求を受け付け、SQLiteへ保存し、GETで読み出します。プロセス内の変数だけにタスクを持つ構成ではありません。
API・スキーマ・テストの準備と事前の再起動確認はCodexが担当しました。その後、私自身がSwaggerで停止前のデータを取得し、ターミナルで停止・再起動して、取得結果を提示・確認しました。紙上の配置・監視・費用案も主にCodexが作成したもので、クラウド構築の実績を示す演習ではありません。
ここで確かめたいのは、APIプロセスの再起動後、現在のDBから確定済みデータを読み直せることです。バックアップから過去のDBを戻す操作は、別の判断になります。
図の停止・再起動と、確定済みデータの読み直しが今回の実行範囲です。後述する結果不明の要求への対応とバックアップ復元は、今回の結果を基に整理した運用案です。
「3件ある」だけでは、保持の確認にならない
停止前にGET /tasksの実際の応答を保存しました。SwaggerのExample Valueではなく、HTTP 200とResponse bodyを比較元にしています。
一覧には3件あり、各タスクは次の6項目です。
id、department、title、owner、status、done_when
同じ件数でも、別のIDに置き換わったり、担当者や状態が変わったりしている可能性があります。そこで、件数に加え、IDの集合と、各IDに対応する全6項目を比較しました。
| 比較するもの | 停止前 | 再起動後 |
|---|---|---|
| 一覧のHTTP応答 | 200 | 200 |
| 総件数 | 3件 | 3件 |
| IDの集合 | 3つのIDを記録 | 同じ集合 |
| 各IDの全6項目 | 応答本文を記録 | 全行・全項目が一致 |
| 確認対象のID |
ARCH9-RESTART-001が1件 |
同じIDが1件 |
停止はControl+Cと正常終了ログ、再起動はApplication startup completeと、その後のGETで確認しました。再起動後にPOSTで登録し直さなかったことは、私自身の操作についても確認しています。
ただし、提示した途中のログには、別タスクへのPATCH 200も含まれています。PATCHの入力と中間の値は記録できていません。最終的な前後比較ではその行も一致しましたが、これを「途中に更新操作はなかった」「PATCHは何も変えなかった」という証拠にはしません。
確認できた結果は、停止前の3件を、再起動後も同じID・同じ全6項目で取得できたことです。操作全体を、変更のない単一のGETだけの連続試験とは扱いません。
なぜ残ったかは、HTTP応答より前の確定処理を見る
実装では、登録処理がSQLite接続のwith connection:内でINSERTを行い、そのブロックを抜けた後に応答用の値を返します。この接続設定では、正常終了時にトランザクションを確定する構造です。Python公式資料も、接続のコンテキストマネージャーが未確定のトランザクションを正常時にcommitし、例外時などにrollbackすることを説明しています。Python 3.14 sqlite3:接続のコンテキストマネージャー
起動時のスキーマもCREATE TABLE IF NOT EXISTSで、既存行を消して作り直す処理にはなっていません。同じ保存先のDBを再び開くため、プロセス内の一時状態とは別に残った記録を読めます。
今回の結果とこの実装から、停止前にCOMMIT済みのデータがDBに残っていたため取得できた、と考えています。一方、停止の瞬間に実行中だったトランザクションを回復させた実験ではありません。SQLiteのCOMMITとROLLBACKの仕様を確認することと、特定の障害条件を実測することは分ける必要があります。SQLite:Transaction
応答が返らなかった要求を、まとめて再登録しない
次は今回未実験の、復旧手順を考えるためのケースです。DBへのCOMMITが成功した直後、応答を返す前にAPIが止まったとします。呼び出し側には成功応答が届かなくても、登録そのものは済んでいるかもしれません。
その状態を「未登録」と決めて全件再送すると、別IDでの二重登録などを起こし得ます。再起動後は、停止前に確定を確認できたデータと、結果が分からない要求を分けて扱う案にしました。
- 現在のDBを上書きせず、障害原因と接続する保存先を確認する。
- 同じ保存先でAPIを起動し、GETで既知のIDと保存内容を照合する。
- 結果不明の要求は、要求IDや業務上の識別子で個別に調べる。
- 不一致や未解決の要求を保留し、確認できた範囲から再開する。
同じIDが存在するだけでは、今回の要求が成功したとは判断できません。保存内容まで合うかを確認し、判断材料が足りなければ再送を止めます。また、既存CRUD APIだけでは、要求単位の照合台帳や承認処理まで実装できたことにはなりません。
昨日のバックアップを無条件で現在のDBへ戻す案は選びませんでした。DB自体の破損・消失などが疑われるなら、まず現状を保全し、復元対象の時点と失われる更新、確認手順を別に決める必要があります。
バックアップの取得も単純なファイルコピーと同一視しません。稼働中のDBにはSQLiteのOnline Backup APIなどの選択肢があり、整合したコピーを得る方法と復元後の確認を組み合わせて検討します。今回は取得・復元とも未実行です。SQLite:Online Backup API
負荷や費用の紙上案は、次の測定項目として残す
第9回では、停止・再起動以外に負荷・費用・監視も整理しました。ただし、以下は設計上の仮定です。
| 紙上の案 | 実測・実装が必要なこと |
|---|---|
| 利用者30人、同時API処理5件 | 操作別の応答時間、書込み競合、待ちの内訳 |
| 月1,000依頼、LLM呼出し1,100回 | 実際の利用量、トークン数、料金、再試行分 |
| 保存エラー・不一致・結果不明件数を監視 | 記録方法、閾値、通知先、受付を止める条件 |
| 整合したバックアップを毎日1回、7世代保持 | 保存容量、取得成功、別の場所での復元確認 |
利用者が10倍になったとき、依頼数や同時書込みも10倍になるとは限りません。APIの待ちと人の確認待ちを分けて測ってから、構成を変える理由を決めたいと考えています。
今回は、ローカルAPIの正常停止・再起動と、SQLiteへ確定済みの3件の保持を確認しました。クラウド障害、電源断、処理途中の再開、バックアップ復元、負荷・費用の実測、実LLM、認証認可を含む業務フローは未確認です。次は結果不明の要求を意図的に作り、何を照合できれば再送してよいかを、復旧手順とともに検証する必要があります。
この記事を書いた人✏️@YushiYamamoto
個人事業ITPRODX.comの山本勇志です。PHPサイトの改修・保守、Web・CMS開発の担当経験を基に、既存システムの切り分けや設計判断を発信しています。
古いホームページやPHPサイトの調査・限定改修は、対応できる範囲を確認のうえご相談を承ります。
