Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

はじめに

Sekai は Minecraft Java 版のリージョンファイル(.mca)のチャンクレベルでの重複排除スナップショットを取得し、任意のスナップショットからワールドを迅速に再構築します。 長期的なアーカイブ目的ではなく、荒らし行為や不適切なアップデートの後に稼働中のサーバーを巻き戻すといった、頻繁に行われるホットリカバリのために設計されています。

同一のチャンクペイロードは1つだけ保存され、コンテンツ指向ストレージ(CAS)を介して複数のスナップショット間で共有されるため、変更のないバックアップのコストはワールド全体のコピーではなく、およそメタデータ1行分で済みます。 ロールバックは、スナップショット取得時に保存された正確なバイト列を、アトミックかつそのまま復元します。

Sekai には主に2つの機能が含まれています:

  • 本ガイドで解説する sekai CLI
  • サーバー管理ソフトウェア向けの再利用可能な Rust ライブラリ(sekai-core 上の sekai-app)。ライブラリ API は rustdoc(cargo doc)でドキュメント化されています。本ドキュメントでは CLI の操作のみを扱います。

設計の背景については ARCHITECTURE.md を参照してください(レイヤリング、2層ハッシュモデル、MVCC/GC データモデル)。 マシン可読な出力規格については docs/json.md に記載されています。

インストール

各 GitHub Release にプリビルトバイナリ(sekai-<target triple>.tar.gz)を添付しています。 Rust ツールチェーンがある場合(固定バージョンは rust-toolchain.toml)は以下でも導入できます:

cargo install sekai-cli

インストールされるバイナリ名は sekai です。 現在のリポジトリのソースから自分でビルドする場合は以下です:

cargo install --path ./crates/cli

セットアップの確認:

sekai --help

クイックスタート

# 現在のワールド状態を記録する(事前にサーバーへの書き込みを停止してください。「サーバー連携」を参照)
sekai --store ./sekai-store backup ./world

# 現在のワールドでバックアップを取ると何が記録されるかをプレビュー
sekai --store ./sekai-store status ./world

# スナップショットの一覧表示
sekai --store ./sekai-store list

# スナップショット1からワールドを再構築(リージョンファイルを上書きします)
sekai --store ./sekai-store rollback ./world 1

# スナップショット1と2の間でチャンクNBTを比較
sekai --store ./sekai-store diff 1 2 --in overworld:0,0

# スナップショット2に後から参照できる名前を付ける
sekai --store ./sekai-store tag stable 2

# 参照されていないBlobをプレビューしてから回収
sekai --store ./sekai-store gc --dry-run
sekai --store ./sekai-store gc

# 古いスナップショットを削除(最新10件を残す)し、blob を回収
sekai --store ./sekai-store prune --keep-last 10
sekai --store ./sekai-store gc

# スナップショット1を新しいディレクトリに再構築(稼働中のワールドには影響しません)
sekai --store ./sekai-store export 1 ./restored

# 変更を加えずにリージョンファイルを検証
sekai debug scan ./world

--store はバックアップストアのディレクトリ名を指定します(存在しない場合は作成されます)。 すべてのコマンドはマシン可読な出力のための --json を受け付け、listtag を除き、フェーズごとの内訳を表示する --timing を受け付けます。 backupstatusrollbackdiffexportgcprune は標準エラー出力に進捗バーを表示する --progress を受け付けます(--json との併用は拒否されます)。 backupstatusrollbackdiffexport は対象範囲(--in, --region, --kind)を受け付けます。 何も指定しない場合はワールド全体が対象となります。

サーバー連携

sekai がサーバープロセスを直接操作することはありません。 バックアップ前後の書き込み一時停止は呼び出し元または管理者の役割であり、ツール側の役割ではありません:

  1. save-off (サーバーによるリージョンファイルへの書き込みを停止)
  2. save-all (保留中の書き込みをディスクにフラッシュ)
  3. sekai backup の実行
  4. save-on (再開)

同時に書き込みが行われているワールドをバックアップすると、途切れたセクターが記録される可能性があります。 読み取り処理はバニラと同様に末尾の不完全なセクターを許容しますが、真に破損した sector run は黙ってバックアップされることなく明確にエラーとなります。 ロールバックとエクスポートはストアのみから読み取るため、プレイヤーを再入室させる前にサーバーを停止すること(ロールバックは稼働中のリージョンファイルを上書きするため)以外にサーバーとの特別な調整は不要です。

ワールドのレイアウト

3つのサーバーファミリーは .mca フォーマットを共有していますが、ディレクトリ構造(レイアウト)は共有していません。 毎回の実行時に同じパスを指定してください:

  • バニラ (Vanilla): 1つのワールドフォルダ(region/, DIM-1/, DIM1/、および 26.1 以降は dimensions/minecraft/<name>/)。
  • Bukkit ファミリー (Bukkit/Spigot/Paper/Purpur、26.1 以前のレイアウト): サーバーのルートディレクトリ。<base>/, <base>_nether/DIM-1/, <base>_the_end/DIM1/ を保持します(ここで baselevel-name、デフォルトは world)。Paper 26.1+ はバニラレイアウトへ移行します。
  • プラグインワールド (Multiverse 等): その内容によって検出される任意のフォルダ。

完全なネームスペース規則は ARCHITECTURE.md (“World Layouts”) に記載されています。 実用上の2つの注意点:カスタムディメンションのフォルダはコンテンツハッシュ化されているため、名前を変更するとその履歴が孤立します。 また、ロールバックは誤った場所に書き込むのではなく、明確にエラー(UnknownRegionPath)を出力する前に、可能な限り検出された同階層のフォルダを通じて移動されたフォルダを復元します。

概念

  • スナップショット (Snapshot): 記録された1つのワールド状態であり、作成順に番号付けされます(list では古い順に表示されます)。スナップショットは新しい行のみを保存します。読み取り時にはフォールバックを通じて実効状態を解決するため、メタデータはワールド全体のサイズではなく、変更内容に応じて増加します。
  • Blob (CAS key): 解凍前の生チャンクペイロードであり、フレーミングを含めてハッシュ化されます。同一のペイロードはすべてのスナップショット間で1つの保存コピーを共有します。ロールバックはこのバイト列を忠実に復元し、LastUpdate などの揮発性タグを取得時刻に巻き戻します。
  • Diff ハッシュ (Diff hash): 必須ではないタグを除外した、解凍済み NBT に対するオプトインの揮発性ビューです。変更検知を高速化するためだけに使用され、保存された Blob を変更することはありません。
  • 墓標 (Tombstone): 明示的な「チャンク非存在」行です。バックアップ間で削除されたチャンクは、記録なし(サイレント)ではなく墓標として記録されます。これにより、ロールバック時に正しく再削除できます。
  • 導出状態 (Derived state): region_state のフィンガープリントにより、次回のバックアップが高速化されます。これらを消去しても、高々1回のフルインジェストのコストがかかるのみで、間違ったデータが作成されることはありません。

正式な定義は ARCHITECTURE.md (“Data & Hashing Model” および “History, State, and GC Model”)を参照してください。

対象範囲の選択

backuprollbackdiffexport は対象範囲を受け付けます。 デフォルトではワールド全体、またはディメンションごとの領域を指定できます。 指定項目は和集合として合成されます。 --kind(複数指定可、空の場合はすべて)はすべての領域に適用されます。

sekai --store ./sekai-store backup ./world --in overworld --kind region --kind entities
sekai --store ./sekai-store rollback ./world 3 --in overworld:0,0..31,31
sekai --store ./sekai-store diff 1 2 --in overworld:0,0
sekai --store ./sekai-store export 3 ./restored --region overworld:0,0
  • --in DIM はディメンション全体を選択します。--in DIM:x,z は1つのチャンク、--in DIM:x0,z0..x1,z1 は境界を含むチャンクの矩形範囲を選択します。
  • --region DIM:RX,RZ は1つのリージョンファイルに含まれるすべてのチャンクを選択します(矩形範囲の簡略表記)。
  • --kind はリージョンの種類を選択します(region, entities, poi)。

対象範囲はフィルターであり、パーティション(分割)ではありません。 対象範囲付きのバックアップはその対象範囲内のみの事実として読み取られ(対象範囲外の座標は何も記録されず、フォールバックを通じて解決されます)、対象範囲付きのロールバックやエクスポートが対象範囲外のファイルに触れることはありません。 ADR-0003 を参照してください。

バックアップ

sekai --store ./sekai-store backup ./world

現在のワールドの状態を新しいスナップショットとして記録し、スナップショットID、チャンク/Blobの数、墓標(tombstone)、スキップされたリージョン、引き継がれたチャンクを表示します。

  • --with-diff は、Blobに加えて揮発性のdiffビューを導出します(取り込みが遅くなります。これを使用しない場合、ホットパスはデコードフリーのままです)。
  • --jobs N はインジェスト(取り込み)ワーカーの数を設定します(0 はCPUコアあたり1つを意味します)。
  • --progress は標準エラー出力に進捗バーを表示します(--json 指定時は拒否されます)。
  • 対象範囲フラグ(--in, --region, --kind)は記録対象を制限します。対象範囲選択を参照してください。
  • 上記の dry-run プレビューは検証・検査status を参照してください。
  • バックアップによってストアディレクトリが新規作成された場合、その旨を標準エラー出力に表示します(human出力のみ)。

変更のないリージョンはフィンガープリントによってスキップされます。 変更されたリージョン内の変更のないチャンクはフォールバックを通じて解決され、新しいBlobのコストはかかりません。 変更がないバックアップであっても、スナップショットの行が1つ書き込まれます。

ロールバック

sekai --store ./sekai-store rollback ./world 1
sekai --store ./sekai-store rollback ./world @stable --in overworld

スナップショットからワールドを再構築し、リージョンファイルをアトミックに上書きします(対象ディレクトリ内のテンポラリファイル + fsync + rename;インプレースの直接変更は一切行われません)。 スナップショット引数は <id> または @tag を受け付けます(タグ参照)。 ファイルにはスナップショットの時刻が刻印され、揮発性タグは取得時の値に巻き戻されます。

デフォルトのポリシーは厳格です:

  • スナップショットに存在しない(後に作成された)リージョンファイルは削除されます。
  • スナップショットに存在しないチャンクは再構築時に消失します。
  • 墓標化されたチャンクは削除されます(完全に墓標化されたリージョンはファイル自体が削除され、ヘッダーだけのシェルが残ることはありません)。
  • CASから欠損している Blob がある場合は明確に中断されます(破損 — 中途半端なワールドになるよりは何も行わない方が良いため)。

各挙動はデフォルトを変更することなく設定可能です:

フラグ効果
--keep-post-snapshot-filesスナップショット作成後に生成されたファイルを削除せず保持する
--keep-post-snapshot-chunks再構築されるリージョン内のスナップショット作成後に生成されたチャンクの生データを保持する
--keep-tombstoned-chunks墓標化されたチャンクの生データを保持する。完全に墓標化されたファイルは変更されない
--on-missing-blob skip-chunkBlob が欠損しているチャンクをスキップする(デフォルトは abort
--on-missing-file derived-only|error場所を推測する代わりに、同階層のフォルダを無視するか、エラーにする(デフォルトは sibling-first
  • 対象範囲フラグ(--in--region--kind)は対象範囲内のみを再構築および削除します。対象範囲選択を参照してください。 通常通り --timing--json と組み合わせることができます。
  • 再構築の前に、CLIは対象スナップショット・対象範囲・ポリシーを標準エラー出力に表示します(human出力のみ)。

エクスポート

sekai --store ./sekai-store export 1 ./restored

スナップショットを新しいディレクトリに再構築します。 ロールバックとは異なり、稼働中のワールドが変更されることはありません。 エクスポートはロールバックの復元セットを共有しますが、すべてのファイルをレイアウトから導出されたパスの出力ディレクトリ配下に書き出します。 スナップショット引数は <id> または @tag を受け付けます(タグ参照)。

  • 出力ディレクトリが存在しない場合は作成されますが、存在する場合は空である必要があります。データの上書きを防ぐため、それ以外の場合は明確にエラーとなります。
  • --flavor legacy|new|bukkit は出力レイアウトを選択します(legacy: region/, DIM-1/, DIM1/new: dimensions/minecraft/<name>/bukkit: フォルダ分割)。--base はbukkitフレーバーのオーバーワールドフォルダ名を指定します(level-name、デフォルトは world)。
  • 対象範囲フラグ(--in--region--kind)はエクスポート対象を制限します。墓標化(tombstoned)されたリージョンはファイルを生成しません。対象範囲選択を参照してください。
  • --on-missing-blob skip-chunk は Blob が欠損しているチャンクをスキップします(デフォルトはロールバックと同様に abort)。
  • 導出可能なのはバニラのネームスペースのみです。カスタムディメンションは間違った場所に保存されるのを防ぐため、明確にエラー(UnknownRegionPath)となります。

タグ

sekai --store ./sekai-store tag stable 1
sekai --store ./sekai-store tag stable @prev --force
sekai --store ./sekai-store tag -d stable
sekai --store ./sekai-store tag

タグはスナップショットに別名を与え、ロールバック対象を読みやすく保ちます(ID ではなく rollback ./world @stable)。 名前は [A-Za-z0-9._-]、1–64 バイト、全て数字の名前は弾かれるため、@123 がスナップショット 123 と混同されることはありません。

  • rollbackexportdiff のスナップショット引数は <id> または @tag を受け付けます。レポートは常に解決後の数値 ID を運びます。
  • 既存名への作成は --force なしでは失敗し、タグを移動させます。存在しないタグの削除は loud に失敗します。
  • 引数なしの tag は全タグを名前順に一覧し、list は各スナップショットのタグを併記します。
  • タグはメタデータのみで何も拘束しません。タグ付きスナップショットを prune するとタグも一緒に消えます。

メンテナンス

sekai --store ./sekai-store gc --dry-run
sekai --store ./sekai-store gc

gc はどのスナップショットからも参照されていない CAS Blob を回収します。 計画(プランニング)フェーズは読み取り専用です。 適用フェーズでは削除(unlink)前に最新のメタデータと照らし合わせて候補を再検証するため、CLIが1回の実行で両方を行う場合でも、2フェーズ構成(gc plan の後に適用)が保持されます。 gc --dry-run は計画のみを表示し、--timing と組み合わせることができます(計画フェーズが計測され、apply_ms0 になります)が、報告すべき適用フェーズが存在しないため --progress との組み合わせはできません。

GCは孤立した Blob のみを削除し、メタデータを削除することはありません。 スナップショットの削除は prune が担います(次保持への fold 後に gc で回収)。

スナップショットの削減

sekai --store ./sekai-store prune --keep-last 10 --dry-run
sekai --store ./sekai-store prune --keep-last 10
sekai --store ./sekai-store prune --before @stable

prune は古いスナップショットを削除します。 保持対象は最新の --keep-last N 件と --before 以降の積集合です(少なくとも一方の指定が必須)。何も残さない選択はストア全消去ではなく loud に失敗します。

削除は古い順の fold です。削除される各スナップショットは、有効な行を次の保持スナップショットへ移し、既に置換済みの行を捨てるため、保持スナップショットの復元結果は不変です。 削除スナップショットのタグは一緒に消えます。 prune は blob を unlink しません。外れた blob の回収は後続の gc が担います。 --dry-run は削除予定の一覧のみ表示します。

スナップショット、メタデータ、Blob など、すべてを含む完全な二次バックアップを作成するには、ストアディレクトリ全体を外部の場所にコピーまたはアーカイブしてください。 コマンドごとのファイル書き込みはクラッシュに強い順序(メタデータのコミット前に Blob、その後にアンリンク)で行われるため、コピーされたストアは一貫性が保たれます。

検証・検査

これらのコマンドがワールドやストアに書き込みを行うことはありません。

sekai --store ./sekai-store list
sekai --store ./sekai-store status ./world
sekai --store ./sekai-store diff 1 2 --in overworld:0,0
sekai debug scan ./world
  • list はスナップショットを古い順に、生(raw)のUnixミリ秒タイムスタンプで表示します(RFC 3339 形式は human 出力でのみ使われます)。各スナップショットを指すタグを併記します。タグ参照。--stat でスナップショットごとの変化統計(fresh/tombstone/new-blob/effective 件数)を付加します。空のストアでは標準エラー出力に注記を表示して終了コード0で終了します。
  • status はバックアップが何を記録するかをプレビューします。基準スナップショット(latest、ストア空時は null)、変化 region(新規・削除ファイルを分記)、新規記録されるチャンク、tombstone、CAS 不在の blob を報告します。clean はスコープ内が最新スナップショットと無差分である意味で、件数はワールド不変なら直後の backup と一致します。意図的に backup --dry-run はありません。--jobs N でプレビューのワーカー数(0 は CPU 数)を指定できます。
  • diff は2つのスナップショット間、または稼働中のワールド(--world)とスナップショット間のチャンクNBTを比較します。1つの --in DIM:x,z 選択では単一チャンクの出力が維持されます。複数の選択を行うとグループ化された出力に切り替わり、差分のないチャンクは省略されます。対象範囲フラグ(--in--region--kind)は比較対象チャンクを選択します。対象範囲選択を参照してください。片側に存在しない、または墓標化されているチャンクは空のコンパウンド(compound)として比較されます。存在しない座標がエラーになることはありません(ADR-0006 を参照)。破損したペイロードや Blob の欠損のみが明確にエラーとなります。--show-values は人間向け出力において具体的な新旧の値を表示します。
  • debug scan はリージョンファイルのサイズ、mtime(更新日時)、チャンク数、ヘッダーハッシュを表示します。--timing を付けるとフェーズごとの所要時間も表示されます。

自動化

すべてのコマンドは --json を受け付けます。 標準出力 (stdout) には正確に1つのJSONドキュメントが出力され、成功時には標準エラー出力 (stderr) は何も出力せず、カラー表示も適用されません。 listtag を除くすべてのコマンドは --timing を受け付け、通常(人間用)モードではフェーズごとのテーブルを表示し、--json モードでは同一のブロックをJSONドキュメント内にマージします。 --progress は標準エラー出力に進捗バーを描画するもので、--json との併用は拒否されます。

終了コード:0 成功、1 実行時エラー(標準出力にエラーのエンベロープが含まれます)、2 使用方法のエラー(clap が標準エラー出力に出力します)。

コマンドごとの dry-run:

プレビューdry-run の意味
statusコマンド全体(backup--dry-run がないのは設計通り)
gc --dry-runprune --dry-runplan のみ、削除なし
rollbackexportなし(rollback は定義上破壊的、export は空ディレクトリ前提で可逆のため)

フラグマトリクス、エンベロープ、コマンドごとのペイロード、互換性の約束などの完全な規約については docs/json.md を参照してください。 そのページがリファレンスであり、本ドキュメントでは重複記載を行いません。

トラブルシューティング

  • unknown snapshot: N — 指定された ID が存在しません。list を確認してください。ID が再利用されることはありません。
  • blob missing from CAS: <hex> — ストアの破損(または不完全にコピーされたストア)。ロールバックは不完全なワールドを書き込むくらいなら処理を中断します。--on-missing-blob skip-chunk(rollback/export)を指定すると、影響を受けるチャンクを除外して続行します。二次コピーからストアを復元し、原因を調査してください。
  • cannot derive region path for dim ... (UnknownRegionPath) — フォルダが不明なカスタムディメンションのリージョンであるか、--on-missing-file error が推測を拒否しています。バックアップ時と同じフォルダ構成のワールドに向けて実行するか、フォルダ構成を確認してください。
  • unsupported schema version — ストアがより新しい(またはより古い)バイナリによって書き込まれました。このプロジェクトはプレリリース段階です。移行を行わず、ストアを再作成してください。
  • リージョンの解析失敗時にはファイル名が示されます(failed to process region file ...)。中断された保存による torn tail は参照されていない場合は許容されますが、真に破損した sector run は設計上明確に失敗します。
  • 処理が遅いコマンド:--timing を付けて再実行し(リリースビルド)、各フェーズを比較してください。バックアップ経路の低速化については、リージョンごとの詳細が含まれる --timing --json の使用を推奨します。