Based on the DEA-C01 exam guideReport an error
OPTIMIZE) and storage cost with snapshot expiration (VACUUM). The data of deleted rows stays in S3 until the snapshots expire.A traditional data lake (Parquet + Hive metastore) is "directory = table." The engine lists the partition paths and treats every file found there as the contents of the table. This simplicity creates limits.
Open table formats flip the idea. They do not list paths; only the files the metadata points to count as the table. If you change the metadata pointer all at once (commit) after finishing writing new files, everything becomes visible at that moment, and if it fails, nothing becomes visible.
The ACID of open table formats is snapshot-based. A write creates new data files and new metadata, then atomically swaps the "current snapshot" pointer. A read sees only the snapshot as of its start, so reads and writes do not block each other (snapshot isolation). If you leave previous snapshots instead of deleting them, that is time travel.
Looking at the layers based on Iceberg, which AWS supports most broadly, gives the following.
Glue Data Catalog table ── metadata_location ──► metadata file (schema, partition spec, snapshot list)
│
▼
manifest list (1 snapshot = list of manifests)
│
▼
manifest file (data file list + per-file column statistics)
│
▼
data files (Parquet/ORC/Avro), delete files| Aspect | Apache Iceberg | Apache Hudi | Delta Lake |
|---|---|---|---|
| Metadata | Metadata file → manifest list → manifest | Timeline (.hoodie) + index | Transaction log (JSON + Parquet checkpoints in _delta_log) |
| Strengths | Schema and partition evolution, hidden partitioning, engine neutrality | Record-key-based upserts, incremental queries | Integration with Spark |
| Write method | CoW / MoR (delete files) | CoW / MoR table types | Mainly CoW, deletion vectors |
| Athena | Read, write, DDL, time travel | Read-only (snapshot and read-optimized queries) | Read-only, time travel not supported |
| Glue ETL (Spark) | Read and write | Read and write | Read and write |
| EMR | Used in Spark, Flink, Trino, etc. | Used | Used |
| Clues for choosing | "Modify and delete with Athena SQL", "must change the partitioning scheme" | "Load CDC as upserts", "read only what changed incrementally" | "Existing Databricks/Delta assets", "Spark-centric" |
PARTITIONED BY (month(dt)). Queries only need to use WHERE dt >= ..., and the engine computes partition values and prunes. Unlike Hive style, users do not need to know about year= and month= columns.| Aspect | Copy-on-Write (CoW) | Merge-on-Read (MoR) |
|---|---|---|
| On change | Rewrites the whole data file containing that row | Records changes and deletions in separate files (delete files, logs) |
| Write cost | High | Low |
| Read cost | Low (just read the files) | High (merges data files and changes at read time) |
| Suitable workload | Read-heavy, rare changes | Write-heavy and change-heavy, streaming upserts |
| Maintenance | Less needed | Compaction is required |
UPDATE, DELETE, and MERGE INTO are always Merge-on-Read. They use position delete files, and ignore a CoW setting even if you specify it in table properties. So a table you modify often with Athena accumulates delete files and needs periodic OPTIMIZE.⚠️ Pitfall: The answer to "reads on an MoR table are getting slower and slower" is compaction. Snapshot expiration (
VACUUM) only reduces storage and does not remove the delete files that must be merged at read time. Conversely, the answer to "storage cost keeps growing" is snapshot expiration. Do not mix the two up.
-- Athena (Iceberg): row-level UPDATE / DELETE
UPDATE sales.orders SET status = 'cancelled' WHERE order_id = 1001;
DELETE FROM sales.orders WHERE customer_id = 'GDPR-REQ-42';
-- MERGE (upsert): apply source changes to the target
MERGE INTO sales.orders AS t
USING staging.order_changes AS s
ON t.order_id = s.order_id
WHEN MATCHED THEN UPDATE SET status = s.status
WHEN NOT MATCHED THEN INSERT (order_id, status) VALUES (s.order_id, s.status);-- Time travel: query as of a past timestamp / snapshot ID (Iceberg on Athena)
SELECT * FROM sales.orders FOR TIMESTAMP AS OF TIMESTAMP '2026-06-20 00:00:00';
SELECT * FROM sales.orders FOR VERSION AS OF 8623491172120013573;
-- Check the snapshot list (Iceberg metadata table)
SELECT snapshot_id, committed_at, operation FROM "sales"."orders$snapshots";⚠️ Pitfall: Running
DELETEdoes not make the data disappear from S3. It is just not visible in the new snapshot; the data files that previous snapshots point to are still there and can be read again with time travel. To handle a GDPR deletion request completely, you must go all the way throughDELETE→ compaction (rewrite files to reflect deletions) → snapshot expiration (physically delete old files). If the bucket has S3 versioning enabled, you also need a rule to clean up noncurrent versions.
CREATE TABLE sales.orders (
order_id bigint, customer_id string, status string, amount double, dt date
)
PARTITIONED BY (month(dt)) -- Iceberg hidden partitioning
LOCATION 's3://acme-datalake-curated/iceberg/orders/'
TBLPROPERTIES ('table_type' = 'ICEBERG');CREATE TABLE, not CREATE EXTERNAL TABLE. Adding EXTERNAL causes an error. LOCATION and 'table_type'='ICEBERG' are required.year, month, day, hour, bucket(N, col), and truncate(L, col).INSERT INTO, and MSCK REPAIR TABLE are not supported on Hudi tables.VACUUM, MERGE, UPDATE, and OPTIMIZE with Lake Formation.iceberg, hudi, or delta in the job parameter --datalake-formats.# Upsert into an Iceberg table in Glue Spark (job parameter: --datalake-formats iceberg)
spark.sql("""
MERGE INTO glue_catalog.sales.orders t
USING updates s ON t.order_id = s.order_id
WHEN MATCHED THEN UPDATE SET *
WHEN NOT MATCHED THEN INSERT *
""")The more you write to a table format, the more three things pile up.
| What piles up | Cause | Result | How to clean up |
|---|---|---|---|
| Small data files | Frequent small commits, streaming ingestion | Slower reads | Compaction |
| Delete files | MoR-style UPDATE/DELETE | Merge cost at read time | Compaction |
| Old snapshots and their files, orphan files | Every commit, failed jobs | Rising storage cost | Snapshot expiration, orphan file removal |
-- Compaction: merge delete files into data files and rewrite closer to the target size
OPTIMIZE sales.orders REWRITE DATA USING BIN_PACK
WHERE dt >= DATE '2026-06-01';
-- Shorten snapshot retention to 3 days, then expire and remove orphan files
ALTER TABLE sales.orders SET TBLPROPERTIES ('vacuum_max_snapshot_age_seconds' = '259200');
VACUUM sales.orders;| Athena Iceberg table property | Default | Meaning |
|---|---|---|
vacuum_max_snapshot_age_seconds | 432000 (5 days) | Snapshots older than this are treated as expiration targets |
vacuum_min_snapshots_to_keep | 1 | The minimum number of snapshots to keep. Takes priority over the retention period |
optimize_rewrite_data_file_threshold | 5 | If fewer data files need optimizing than this, they are not rewritten |
optimize_rewrite_delete_file_threshold | 2 | If fewer delete files are attached to a data file than this, it is not rewritten |
OPTIMIZE and VACUUM are queries you must run yourself. If you do not set a schedule, nobody runs them.You must not expire the data prefix of an Iceberg table with an S3 lifecycle rule. The metadata believes those files exist, so if the files vanish, queries fail. Manage the file lifetime of a table format with the table format's tools (snapshot expiration, orphan file removal).
| Symptom | Cause | Action |
|---|---|---|
| Queries on a frequently modified Iceberg table get slower and slower | Accumulated delete files and small files | OPTIMIZE or Glue automatic compaction |
| Row count unchanged but storage keeps growing | Old snapshots hold on to old files | VACUUM (snapshot expiration), adjust the retention period |
| Cannot time travel to last week | That snapshot has already expired | Extend the retention period to fit requirements (a preventive step) |
| Deleted customer data remains in S3 | Previous snapshots still reference it | Compaction, then snapshot expiration |
| INSERT into a Hudi table from Athena fails | Athena is read-only for Hudi | Write with Glue/EMR Spark |
| One of the concurrent write jobs fails with a commit conflict | Optimistic concurrency conflict | Retry, separate jobs that write the same partition concurrently |
CREATE EXTERNAL TABLE ... ICEBERG error | Iceberg does not support the EXTERNAL keyword | Use CREATE TABLE |
| Clue in the question | Answer |
|---|---|
| "ACID transactions and row-level updates and deletes in an S3 data lake" | Open table format (Iceberg if done with Athena) |
| "Delete a specific customer's records (GDPR)" | Table format DELETE + compaction and snapshot expiration |
| "Upsert CDC changes, query only what changed incrementally" | Hudi (or Iceberg MERGE INTO) |
| "Query the table state as of yesterday" | Time travel (FOR TIMESTAMP AS OF) |
| "Change the partitioning scheme without rewriting existing data" | Iceberg partition evolution |
| "Pruning even if query authors do not know the partition columns" | Iceberg hidden partitioning |
| "Write-heavy, a bit of read latency is acceptable" | Merge-on-Read |
| "Mostly reads, rare changes" | Copy-on-Write |
| "Slow because of small files and delete files" | Compaction |
| "Iceberg maintenance without operational burden" | Glue Data Catalog table optimization |
Tomorrow we cover how to reduce the storage cost of the lake built up this way with storage classes and lifecycle rules.
MERGE INTO.Click a choice to reveal the answer and explanation.
Question 1
Which of the following is NOT a core capability that open table formats (Iceberg/Hudi/Delta) provide compared with traditional Hive-style Parquet tables?
Question 2
You must efficiently delete only a specific customer's rows in response to a GDPR deletion request. What is the most suitable approach?
Question 3
Which table format specializes in upserts and incremental (CDC) queries and provides Copy-on-Write/Merge-on-Read modes?
Question 4
What is the correct syntax in Athena engine version 3 to query an Iceberg table as of a specific past timestamp?
Question 5
Which option correctly states the main reason the read performance of a Merge-on-Read (MoR) table degrades over time, and its solution?