Skip to content

#466 を実装する(スプリント m8c) - #478

Merged
takemi-ohama merged 3 commits into
sprint/m8cfrom
feat/issue-466-m8c
Oct 12, 2026
Merged

takemi-ohama merged 3 commits into
sprint/m8cfrom
feat/issue-466-m8c

Conversation

@takemi-ohama

Copy link
Copy Markdown
Contributor

差分の作成が途中で失敗しても、snapshot.snar と差分の列を作成を始める前の状態のまま保つようにしました(#466)。あわせて、差分の作成の手順と失敗したときの扱いを確定仕様と CHANGELOG に書きました。

利用者向けの変化

  • 差分の作成が失敗しても、snapshot.snar と差分の列は作成を始める前の状態のまま残ります。
  • 差分の作成では仮の snar を backup_command に渡します。除外の仕様にもこのことを書いています。

移行の手順

  • 無し

変更の要点

  • lib/devbase/snapshot/manager.py
    • 差分の作成で、作成が成功したときだけ snapshot.snar と差分の列を更新するようにしました。
    • 作成中は仮の snar を使います。
  • 世代と snapshot.yml の形は変えていません。
    • この変更の前の devbase へ戻しても、この変更の後に作った世代を読めます。
    • 仮の名前のファイルが残っていても、前の devbase の glob には当たりません。
  • docs/specifications/snapshot-series.md に、差分の作成の手順と失敗したときの扱いを書きました。
  • docs/specifications/snapshot-exclude.md に、差分の作成でも仮の snar を backup_command に渡すことを書きました。
  • CHANGELOG.md に今回の変更を追記しました。
  • テストを追加・更新しました。
    • tests/snapshot/test_incremental_failure.py を新しく追加しました。
    • 既存の test_archive_layout.py と test_rebuild_generation.py を手直ししました。

テスト

ステップ 結果
test-limited ok(範囲テスト 1 本、134.2 秒、戦略 local-full)
design-tests ok(設計文書 issues/issue-466-design.md に名前の出るテスト 3 件が、すべて HEAD にある)
design-specs ok(変えたパス 6・シンボル 46 を含む既存の確定仕様と変更履歴 14 件を確認)

設計と違う点

  • 無し

概要

既存の世代へ差分を積む経路(devbase snapshot create --name <名前>・devbase up の自動スナップショット)で、控えが失敗しても
snapshot.snar が進まず、作りかけの incr-NNN.tar.zst も残らないようにした(#466)。設計は issues/issue-466-design.md のとおり。

  • _create_incremental: 検査の後に前の中断の仮のファイルを消し、ホストで snapshot.snar を snapshot.snar.new へ写して
    checked_backup_command(backup_command('incr.tar.zst.new', …, snar='snapshot.snar.new')) を打つ。成功したら差分 → snar の順に改名で置く。
    写し・控え・改名のどこで失敗しても仮のファイルを消して SnapshotError(改名と写しの OSError は 世代 '{名前}' に差分を作れませんでした: {理由} に包む)
  • 追加: _install_incremental・_discard_incremental_temps・定数 INCR_ARCHIVE_TMP。snapshot.snar.bak は作らなくなった(前からあるものには触らない)
  • 確定仕様 docs/specifications/snapshot-series.md(構成要素・### 差分の作成・データ・エラー処理・運用・テスト観点・作り直しの常に成り立つ条件の絞り込み)と CHANGELOG.md の ### Fixed

設計と違えたところ

  • 受け入れ条件 9 は「作り直しの既存のテストは変えずに通る(test_create_and_restore_go_through_the_helpers だけは書き換えてよい)」だが、
    tests/snapshot/test_rebuild_generation.py の test_incremental_after_rebuild_starts_from_one の 1 行も書き換えた。
    この行は作り直しの後の 差分 の控えのコマンドが --listed-incremental=/backup/snapshot.snar を渡すことを縛っており、決定 1 で差分が
    snapshot.snar.new を渡す形になるため、そのままでは通らない。SNAR_FILE_TMP を見る形へ変えた(作り直しの経路のコマンドは変えていない)。
    復元前バックアップが同じ秒の既存の世代と重なると、その世代を作り直す(復元元が pre-restore-* なら展開の前に置き換わる) #473 が触る test_pre_restore_backup_rebuilds_an_existing_generation には触れていない
  • backup_command の docstring(「差分は archive に incr-NNN.tar.zst を渡す」)は、両方が触らない関数の表に従って直していない

確かめたこと

  • env -u DEVBASE_ROOT uv run --locked pytest tests/ -q: 4324 passed, 31 skipped。uvx ruff check --select=E9,F63,F7,F82 lib: 通過。
    固有の語の検査は、手元に語の一覧が無く飛んだ
  • 実の GNU tar を使うテスト(手元の macOS は bsdtar で飛ぶ)は、python:3.12-slim の docker run --rm に devbase-snapshot:latest の zstd 1.5.7 を
    持ち込んで、test_incremental_failure.py・test_backup_real_tar.py・test_rebuild_generation.py を流した: 54 passed(飛ばしは 0)
  • 受け入れ条件 11: _create_incremental だけを今の形(cp … .bak && backup_command(incr-NNN)、失敗で何も戻さない)に戻すと、
    test_incremental_failure.py の受け入れ条件 1〜6 を縛るテスト 19 件が落ちる(通るのは tar 1 の成功・仮の名前の判定・復元の列・.bak の 5 件)
  • 受け入れ条件 12(2026-10-13、devbase-snapshot:latest): 実装の SnapshotManager をそのまま使い、volume_mount_args だけを差し替えて
    作業用のディレクトリを /source/ai:ro・/source/group:ro に付けた(利用者のボリュームと devbase up は使っていない)。失敗は控えのコマンドの前に
    ulimit -f 100;(R1)・touch /backup/incr.tar.zst.new;(R3)を足すか、無いメンバーを足して(R5)起こした
段 結果
full → incr-001(ai/b) 世代は full.tar.zst incr-001.tar.zst meta.yml snapshot.snar、snar 119 バイト・sha の先頭 3761d1060df4
3MB の ai/c を足し R1(zstd が書く途中に止まる) SnapshotError(File size limit exceeded)。一覧と各ファイルのバイト列が打つ前と同じ(snar は 3761d1060df4 のまま、作りかけの差分なし)
ai/e を足し R3(zstd がすぐ失敗) SnapshotError(zstd: /backup/incr.tar.zst.new already exists)。世代は打つ前とバイト列が同じ
R5(tar が 2、zstd は 0) SnapshotError。世代は打つ前とバイト列が同じ
次の差分 incr-002.tar.zst ができ、中身は ai/ group/ ai/c ai/e(失敗の前の c と後の e の両方)。snar は 125 バイトへ進んだ
full → incr-001 → incr-002 の展開 終了コード 0、diff -r で元のツリーと同じ
小さい ai/k だけを足し R3(tar が仮の snar を進め終えてから zstd が失敗する形) SnapshotError。世代は打つ前とバイト列が同じ
ai/m を足して次の差分 incr-003.tar.zst の中身は ai/ group/ ai/k ai/m

残した事項

  • Linux の実機で、コンテナが root で書いた incr.tar.zst.new・snapshot.snar.new をホストの利用者が改名・削除できること、root が書いた
    snapshot.snar をホストで写せることは確かめていない(macOS の Docker Desktop だけ。作り直しの経路と同じ)
  • 要求の未決 3(利用者のボリュームで tar が 2 以上を返す常の場面があるか): 作業用のディレクトリでは、失敗を起こさない控えで tar が 2 以上を返すことは無かった。
    利用者のボリュームでは確かめていない(触らない決まりのため)

モード: standard / 通した工程: 実装 → 完了判定 → Pull Request

🤖 Generated with Claude Code

takemi-ohama and others added 3 commits October 13, 2026 02:34
世代の snar を写した仮の snar と仮の名前の差分 (incr.tar.zst.new) へ控え、
tar の 2 以上と zstd の失敗を検査してから、差分 → snar の順に改名で置く。
写し・控え・改名のどこで失敗しても仮のファイルを消して SnapshotError を返す。
差分の作成は snapshot.snar.bak を作らない。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@takemi-ohama
takemi-ohama marked this pull request as ready for review October 12, 2026 17:43
@takemi-ohama
takemi-ohama merged commit 6743fa6 into sprint/m8c Oct 12, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant