普段は自動で動くので何もしなくてよい。手を出すのは、戻したいときと、動いていないと気づいたときだけ。なぜこの形にしたかはバックアップ設計にある。
管理者権限は要らない。ログオンしているユーザーのタスクとして動く。
tools\80_ops\migrate-backup-layout.cmd
tools\80_ops\register-backup-tasks.cmd
| やること | 中身 |
|---|---|
| 置き場を整える | 区分を分ける前のものを hourly へ移し、最新の 1 本を残り 3 区分にも配る。配らないと monthly が空のまま翌月 1 日まで待つことになる |
| タスクを登録する | タスクスケジューラの \ai-chat-lite\ フォルダに 4 つ登録する。フォルダは自動で作られる。既に同じ名前があれば作り直す |
登録されるのはこの 4 つ。名前に時刻が入っているので、一覧の並び順がそのまま実行順になる。
| タスク名 | 区分 |
|---|---|
ai-chat-lite バックアップ 00時00分 (毎時) | hourly |
ai-chat-lite バックアップ 00時01分 (毎日) | daily |
ai-chat-lite バックアップ 00時02分 (毎週) | weekly |
ai-chat-lite バックアップ 00時03分 (毎月) | monthly |
| 区分 | 取る時刻 | 残る数 | 遡れる範囲 |
|---|---|---|---|
hourly | 毎時 0 分 | 8 | 8 時間 |
daily | 毎日 0 時 1 分 | 7 | 1 週間 |
weekly | 月曜 0 時 2 分 | 4 | 1 か月 |
monthly | 1 日 0 時 3 分 | 6 | 半年 |
PC が寝ていて時刻を過ぎたときは、起きたあとに取り返す。サービスは止めなくてよい。読み取り専用で複製するので、動いたまま取れる。
DB を触る前など、手で 1 本取りたいときは区分を指定して呼ぶ。
tools\80_ops\backup.ps1 -Kind hourly
置き場は _backup\<区分>\chat-yyyymmdd-hhmmss.db.zip。
tools\80_ops\restore.cmd
いちばん新しいものが選ばれる。区分をまたいで新しい順に並べるので、どこにあるかを気にしなくてよい。確認を求められたら yes と入力する。
tools\80_ops\restore.ps1 -Path _backup\daily\chat-20260828-000100.db.zip
| 順 | すること |
|---|---|
| 0 | これから止めることを public に流し、10 秒待つ。印を置く前に投稿する(置いてからでは受け付けてもらえない) |
| 1 | _data\MAINTENANCE を置く。理由と 見込み: N 分 を書く |
| 2 | サーバーを落とす。印があるので、待ち受けだけ始めて DB は開かない |
| 3 | zip を展開し、中身の件数を数えて確かめる |
| 4 | いまの DB を _data\prev-yyyymmdd-hhmmss\ へ移し、入れ替える |
| 5 | 印を消す。受け口が通常に切り替わり、再開したことを public に流す |
数秒で起動する。http://localhost:8787/ を開いて確かめる。
止めている間もポートは開いている。繋いだ側には 503 と理由が返り、画面には帯が出る。「繋がらない」で落ちるのではないので、メンテナンス中だと分かる。
待受けを張っている相手は 10 分まで繋ぎ直すので、その間に終われば何ごともなかったように繋がり直す。
既定は 5 分。長くかかると分かっているときは渡す。案内の本文と Retry-After の両方に効く。
tools\80_ops\restore.ps1 -EstimatedMinutes 20
いまの DB は消していない。_data\prev-yyyymmdd-hhmmss\ に 3 つのファイルが入っている。元に戻すには、印を置いてサーバーを落とし、3 つとも _data へ戻す。
echo 戻し直しています > _data\MAINTENANCE
node src\client\chat.mjs restart
# _data\prev-… の中身 3 つを _data\ へ移す(chat.db / chat.db-wal / chat.db-shm)
Remove-Item _data\MAINTENANCE
3 つとも戻すこと。chat.db だけを戻すと、直近の発言がまとめて消える。実測では 64 件のすべてが chat.db-wal 側にあり、本体は空同然だった。
Get-ChildItem _backup -Recurse -Filter 'chat-*.db.zip' |
Group-Object { $_.Directory.Name } |
Select-Object Name, Count
数が増えていない区分があれば、タスクが動いていない。
Get-ScheduledTask -TaskPath '\ai-chat-lite\' |
Get-ScheduledTaskInfo |
Select-Object TaskName, LastRunTime, LastTaskResult
-TaskPath を省くと見つからない。タスクは \ai-chat-lite\ フォルダに置いてある。名前だけで Get-ScheduledTask を呼んでも 0 件になる。
tools\80_ops\unregister-backup-tasks.cmd
4 つとも解除し、空になったフォルダも消す。ほかのタスクが同じフォルダに残っていれば、フォルダは消さずにそのままにする。
控え(_backup)は消さない。要らなくなったら手で消す。
LastTaskResult | 意味 |
|---|---|
| 0 | 取れた。印を待った場合も、待った末に取れていれば 0 |
| 0 以外 | その回の控えが無い。DB が読めない、印が 5 分待っても消えなかった、など。調べる必要がある |
zip を展開して、そのコピーを開く。控えそのものを直接開かない。
Expand-Archive _backup\daily\chat-20260828-000100.db.zip -DestinationPath tmp\peek
node -e "const{DatabaseSync}=require('node:sqlite');const d=new DatabaseSync('tmp/peek/chat.db');console.log(d.prepare('SELECT count(*) c FROM messages').get().c + ' 件');d.close();"
どの区分も取れていないなら、印が残っている可能性がある。
Get-Content _data\BACKUP-RUNNING
Get-Content _data\MAINTENANCE
| 印 | 対処 |
|---|---|
BACKUP-RUNNING |
中身に開始時刻と pid が書いてある。30 分より古ければ次の実行が自分で奪うので、待てばよい。急ぐなら Get-Process -Id <pid> で本当に動いていないことを確かめてから消す |
MAINTENANCE |
人が置いたもの。勝手に消さない。DB を触っている最中かもしれない。置いた人に確かめる |
メンテナンスの印を消し忘れると、控えが 1 本も取れなくなる。作業が終わったら必ず消すこと。消し忘れている間は毎時 ERROR が出て、タスクの履歴も赤くなる。赤が並んでいたら、まず印を疑う。
chat.db-wal と chat.db-shm を消さない。例外はない。-wal には本体にまだ統合されていない書き込みが入っている。消せばその分が失われ、本体だけが残る。本体の大きさから中身の量は判断できない。実測では 40 KB の本体に 0 件、2.6 MB の -wal に 64 件すべてが入っていた。
捨てるときは 3 つまとめて捨て、残すときは 3 つまとめて残す。
| やってはいけない | なぜ | 代わりに |
|---|---|---|
chat.db だけをコピーする |
中身が入っていないことがある | backup.ps1 を使う。VACUUM INTO が 3 つを織り込んだ 1 ファイルを作る |
| 控えを直接開く | 読むだけでも -wal と -shm が作られ、閉じても残る |
展開してコピーを開く |
MAINTENANCE を消す |
人が DB を触っている最中かもしれない | 置いた人に確かめる |
_backup を Git に入れる |
発言の中身が履歴に残り、消すには履歴の書き換えが要る | 先頭が _ なので既に管理外。そのままにする |
戻すのに stop を使う |
終了コード 0 でサービスごと止まる。印を消しても起動せず、管理者権限での再開が要る | restore.ps1 を使う。中で restart を呼んでいる |