レイクハウスのテーブル形式 — Apache Iceberg をメタデータで理解する
誤って消した地域を元に戻し、翌日のデータはブランチで確認してから公開する
目標
3日分のコミットのあとに、1つの地域を丸ごと消す事故を起こし、タイムトラベルで事故前の状態を読み、タグでその時点に名前を付け、ロールバックで元に戻します。続けて、翌日のデータをmainではなくブランチに先に書いて確認したあと、mainを早送りして公開します。
なぜ重要なのか
Icebergのすべてのコミットは、古いスナップショットを消さずに新しいスナップショットを追加します。そのため、「昨日の状態」はバックアップから取り出すものではなく、そのスナップショットのファイル一覧を読むことであり、元に戻すことは、ファイルをコピーすることではなく、mainというポインターを古いスナップショットに移すことです。数秒で終わります。 ただし、古いスナップショットは永遠ではありません。スナップショットの期限切れ処理が動くと、基準より古いものは消え、IDだけを書き留めた時点は失われます。タグは、スナップショットに名前を付け、保持期間を別に与えて、期限切れから守ります。 ブランチは、反対方向の道具です。新しいデータをmainではない場所に先にコミットしておき、検査に通ったら、mainをそのスナップショットまで早送りします。読み取る人は、検査前のデータを1度も見ません(write-audit-publish)。
ステップ
- /root/ice/tt/days.py(アプリ
ice-tt-days)でlake.tt.orders(format-version 2)を作成し、2026-03-01・02・03を1日に1コミットずつ入れてください。 - /root/ice/tt/accident.py(アプリ
ice-tt-accident)で、DELETE FROM lake.tt.orders WHERE region = 'busan'を実行してください(わざと起こす事故です)。 - /root/ice/tt/travel.py(アプリ
ice-tt-travel)で、3番目のコミット(事故直前)のスナップショットを探し、そのときの行数・釜山の行数と、現在の釜山の行数を、/root/ice/tt/out/travel.jsonに書いてください。 - /root/ice/tt/tag.py(アプリ
ice-tt-tag)で、そのスナップショットにタグbefore_accidentをRETAIN 7 DAYSで付けてください。 - /root/ice/tt/rollback.py(アプリ
ice-tt-rollback)でrollback_to_snapshotを呼び、mainをそのスナップショットに戻してください。 - /root/ice/tt/branch.py(アプリ
ice-tt-branch)でブランチfixを作成し、2026-03-04をそのブランチにだけ書いてください。 - /root/ice/tt/publish.py(アプリ
ice-tt-publish)でfast_forwardを呼び、mainをfixまで早送りしてください。 - /root/ice/tt/report.mdに、
## 타임트래블、## 롤백과 태그、## 브랜치の3つの節を書いてください(3つの見出しは順に、韓国語で「タイムトラベル」「ロールバックとタグ」「ブランチ」を意味する語句です)。
参考
- スナップショットは
SELECT * FROM lake.tt.orders.snapshotsで、mainが通ってきた道はlake.tt.orders.historyで、ラベル(ref)はlake.tt.orders.refsで見ます。 - タイムトラベル:
SELECT … FROM 표 VERSION AS OF <스냅샷 ID 또는 태그·브랜치 이름>、TIMESTAMP AS OF '<시각>'(プレースホルダーは順に、テーブル名、スナップショットIDまたはタグ・ブランチの名前、時刻です)。 - ブランチへの書き込み:
df.writeTo("lake.tt.orders.branch_fix").append()、またはSQLのINSERT INTO lake.tt.orders.branch_fix …。 - よくある間違いは、ロールバックが事故のスナップショットを消すと思い込むこと(消しません。消すのは期限切れ処理です)、タグを付ける前にスナップショットの期限切れ処理を動かしてしまうことです。
- 公式ドキュメント: Branching and Tagging・Spark Queries — Time travel・Spark Procedures — rollback_to_snapshot · fast_forward・Spark DDL — Branching and Tagging DDL
3日、コミット3つ
/root/ice/tt/days.pyをアプリ名ice-tt-daysで作成し、lake.tt.orders(列は6つ、'format-version' = '2')を作って、2026-03-01・03-02・03-03を、1日にappend()1回ずつで入れてください。
3つのスナップショットが、親でつながります。採点ツールは、最初の3つのスナップショットが、順にその日の行数を加えたappendであるかを確認します。何度も実行してスナップショットが増えた場合は、DROP TABLE lake.tt.orders PURGEを実行してからやり直してください。
事故: 1つの地域が丸ごと消える
/root/ice/tt/accident.pyをアプリ名ice-tt-accidentで作成し、DELETE FROM lake.tt.orders WHERE region = 'busan'を実行してください。
DELETEもコミットなので、4番目のスナップショットができます。釜山の行を含むファイルは、釜山を除いた新しいファイルに変わって一覧に入り、古いファイルは一覧から外れるだけで、ディスクにはそのまま残っています。採点ツールは、4番目のスナップショットが3番目を親に持ち、釜山の行がないかを確認します。
タイムトラベル: 事故直前を読む
/root/ice/tt/travel.pyをアプリ名ice-tt-travelで作成し、lake.tt.orders.snapshotsから3番目のコミットのスナップショットIDを探し、VERSION AS OFでそのときの全体の行数と釜山の行数を、現在のテーブルで釜山の行数を数えたうえで、/root/ice/tt/out/travel.jsonに{"snapshot_id", "rows_before", "busan_before", "busan_after"}の形で書いてください。
VERSION AS OFは、そのスナップショットのマニフェストリストを読むだけです。事故前のファイルがまだディスクにあるので、そのまま読めます。スナップショットIDは19桁の整数なので、手で写さず、スクリプトで読んで書いてください。
タグ: 時点に名前と保持期間を
/root/ice/tt/tag.pyをアプリ名ice-tt-tagで作成し、ステップ3のスナップショットに、ALTER TABLE lake.tt.orders CREATE TAG before_accident AS OF VERSION <ID> RETAIN 7 DAYSでタグを付けてください。
タグは、metadataのrefsに、名前・スナップショットID・max-ref-age-msとして入ります。RETAIN 7 DAYSは604,800,000msです。タグが指すスナップショットは、期限切れの対象から外れます。採点ツールは、refsの3つの値を確認します。
ロールバック: ポインターだけを移す
/root/ice/tt/rollback.pyをアプリ名ice-tt-rollbackで作成し、タグbefore_accidentが指すスナップショットに対して、CALL lake.system.rollback_to_snapshot('lake.tt.orders', <ID>)を呼んでください。
ロールバックは、新しいスナップショットを作らず、mainが指すスナップショットだけを変えます。事故のスナップショットはsnapshotsの一覧にそのまま残り、mainが通ってきた道(snapshot-log)に「事故 → 事故の直前」が記録されます。採点ツールは、その記録と、事故のスナップショットが残っているかを確認します。
まずブランチに書く
/root/ice/tt/branch.pyをアプリ名ice-tt-branchで作成し、ALTER TABLE lake.tt.orders CREATE BRANCH fixのあと、2026-03-04のファイルをwriteTo("lake.tt.orders.branch_fix").append()でfixにだけ書いてください。
ブランチは、作成したときのmain(元に戻したスナップショット)から分岐します。fixにコミットすると、fixだけが先に進み、mainはそのままなので、読み取る人たちは、3月4日のデータをまだ見ません。採点ツールは、fixの先頭が、元に戻したスナップショットを親に持ち、3月4日の行数が加わっているかを確認します。
公開: mainを早送りする
/root/ice/tt/publish.pyをアプリ名ice-tt-publishで作成し、CALL lake.system.fast_forward('lake.tt.orders', 'main', 'fix')を呼んでください。
fast_forwardは、mainがfixの祖先であるときだけ実行できます(その間にmainに別のコミットが入っていたら拒否されます)。新しいスナップショットは作らず、mainポインターをfixの先頭に移します。採点ツールは、mainとfixが同じスナップショットを指しているか、mainに釜山と3月4日の両方があるかを確認します。
元に戻す手順を1枚にまとめる
/root/ice/tt/report.mdに、## 타임트래블、## 롤백과 태그、## 브랜치の3つの節を書いてください(3つの見出しは順に、韓国語で「タイムトラベル」「ロールバックとタグ」「ブランチ」を意味する語句です)。最初の節には、ステップ3のbusan_beforeを、数字で入れてください。
明け方に同じ事故が起きたとき、オンコール担当者が従う順序で書いてみてください。期限切れ処理が動いているテーブルなら、何を先にすべきかも書いてください。