🛸 Hitchhiker's Guide — Phase 09: Lakehouse Table Formats
Read this if: you want Iceberg/Delta/Hudi in your head fast — why they exist, how commits and time travel work, and how to choose. Skim, read WARMUP, build the table.
0. The 30-second mental model
A table format turns the Parquet files of P08 into a real database: ACID, snapshots, time travel. The trick — since S3 has no atomic rename — is an atomically-swapped metadata pointer: the table is "whatever the current metadata says," and a commit writes new metadata
- swaps the pointer (a new immutable snapshot). One sentence: the lakehouse = atomic metadata swap over immutable file snapshots → ACID + MVCC + time travel on a data lake.
1. Why table formats exist
S3: no atomic rename, "directory" = prefix → can't safely say "the table is these files now"
fix: explicit metadata pointer, atomically swapped on commit → ACID
everything else (time travel, concurrent writers, evolution) follows from immutable snapshots
2. Iceberg hierarchy
catalog → metadata.json (current snapshot, schema, partition spec)
└── snapshot → manifest list → manifests → data files (+ per-file stats)
manifests carry partition values + min/max → prune files WITHOUT listing S3 (vs Hive)
3. Concurrency = optimistic, not locked
writer: read base snapshot → stage files → commit IF current == base (atomic swap)
else CONFLICT → re-read new snapshot, RETRY
readers: pin a snapshot (MVCC), never see half-written commits
→ Spark AND Flink AND compaction write one table, no global lock
4. Time travel + rollback
snapshots are immutable → query AS OF snapshot/timestamp (reproducibility, audit)
bad write? roll back = swap pointer to previous metadata (instant, no restore)
cost: snapshots accumulate → must EXPIRE them (bounds time travel, enables real delete)
5. Partitioning that doesn't betray you
HIDDEN partitioning: partition by transform days(ts)/bucket(id); query filters the COLUMN,
Iceberg derives the prune → no "forgot the partition filter" footgun
partition EVOLUTION: change spec for NEW data, no rewrite of old
schema evolution by FIELD ID → safe rename/reorder
6. Maintenance is mandatory
compaction (rewrite_data_files / OPTIMIZE) → fix small files (P08), same data, new snapshot
clustering / Z-ORDER → tight per-file stats → pushdown actually skips (P08)
expire_snapshots / VACUUM → reclaim storage, bound time travel, enable real deletes
remove_orphan_files → clean failed commits
a table nobody compacts/expires becomes a swamp
7. Iceberg vs Delta vs Hudi
Iceberg → open, multi-engine (Spark/Flink/Trino/Athena), append-heavy analytics, evolution → default
Delta → Spark/Databricks-centric; mature MERGE / OPTIMIZE / Z-ORDER
Hudi → upsert/CDC-heavy; CoW (read-fast) vs MoR (write-fast, merge at read); record indexes
all three: ACID + time travel + compaction. Differences = ecosystem + upsert model.
8. Deletion vs time travel (the privacy trap)
DELETE removes a row from CURRENT snapshot, but old snapshots still reference its files
→ time travel can RESURRECT it → not GDPR-compliant until you EXPIRE those snapshots + vacuum
compliant erase = delete current → expire referencing snapshots → remove orphan files
9. Beginner mistakes that mark you
- Treating a lakehouse table as set-and-forget (no compaction/expiry schedule).
- Thinking DELETE = erased (time travel resurrects until expiry).
- Using a global lock instead of optimistic concurrency for concurrent writers.
- Hive-style "all files under the path" thinking (no atomic commit).
- Picking a format by hype instead of ecosystem + upsert pattern.
- Streaming tiny commits with no compaction → small-file swamp.
10. How this phase pays off later
- 2PC → snapshot commit ← P04 exactly-once sink.
- Partition pruning + clustering → P10 Athena/Trino cost.
- Deletion vs time travel → P14 privacy/GDPR.
- Compaction/expiry schedule → P13 automated maintenance.
- Storage core of capstone Problems 1 & 3 (P16).
Read WARMUP, build the table, make the conflict-retry test pass, then P10: the query engines (Trino/Athena) that read all this — and how to optimize them.