AWS Lesson 103 of 123

AWS Enterprise Architecture: Data Lakehouse

In a nutshell

Imagine two ways to run a distribution business. The first is to rent a fully-staffed, climate-controlled distribution centre: the building, the shelving, and the crew that moves your goods all come as one always-on bill, whether ten trucks arrive today or none. That is a data warehouse — storage and the compute that works the data are fused, and you pay for both around the clock. The second way is to keep your goods in cheap, near-indestructible storage, hire forklift crews by the hour only when a truck actually arrives, and run a single security desk that checks every crew’s badge the same way no matter which crew shows up. That is a lakehouse: your data sits once on Amazon S3, query engines spin up only when a query runs, and one governance service (Lake Formation) decides who may read which shelf — identically for every engine.

The trick that makes the cheap storage trustworthy is an open table format (Apache Iceberg is the AWS default). Plain files on S3 are like an unlabelled storage unit — you cannot safely update them, two people writing at once corrupt each other, and you cannot ask “what did this look like last Tuesday?” Iceberg adds a thin metadata layer on top of the same files that gives you database-style guarantees — atomic writes, updates and deletes, time travel, and schema changes — without moving the data into a database. That single idea is the whole lesson: the reliability of a warehouse, on the price and openness of a lake.

Everything else here is the supporting cast. The medallion zones (bronze → silver → gold) are just three quality tiers of the same data — raw arrivals, cleaned-and-conformed, and business-ready. The catalog (Glue Data Catalog) is the card index of what tables exist; Lake Formation is the security desk on top of it. And the engines (Athena, Redshift Spectrum, EMR/SageMaker) are interchangeable crews you rent per job, all reading the same shelves.

Level: Advanced · Time: ~55 min

Prerequisites — you’ll get the most from this if you already know:

After this lesson you will be able to:

A lakehouse is not “a data lake with a SQL engine bolted on.” It is a deliberate architecture where open table formats give your S3 data the transactional guarantees of a warehouse, a single governance plane (Lake Formation) controls who sees which rows and columns, and you pick the cheapest engine that fits each workload — Athena for ad-hoc, Redshift Spectrum for BI joins against curated dimensions, EMR for heavy transforms and ML feature engineering. This article builds that architecture on AWS end to end and treats the hard parts — table format choice, governance propagation, small-file economics, and DR for a catalog that lives in three places — as first-class concerns.

The business scenario

Picture a company that has outgrown its warehouse but cannot afford a second one. This is true at 50 people and at 50,000.

The early-stage version: a Series B fintech runs a single Amazon Redshift cluster. It started as the reporting database; now it ingests clickstream, holds 18 months of transaction history, runs the dbt models, and serves the data-science notebooks. Every new use case means a bigger cluster, and the cluster is 24/7 even though batch jobs run for three hours a night. The CFO sees a line item that grows linearly with the company and asks the question every architect dreads: “Why does storing data we rarely read cost the same as the data we read constantly?”

The large-enterprise version: a retailer with 1,200 stores has a Redshift estate, a Hadoop cluster a previous team built, three Glue jobs nobody owns, and an analytics team that exports CSVs to laptops because getting access through the warehouse takes two weeks. Data is duplicated five times. Nobody can answer “who can see PII” with a straight face during the audit.

Both companies have the same underlying problem and it is not a tooling problem — it is a coupling problem:

The lakehouse decouples all four. One copy of data on S3 in open formats; many engines reading the same files; one governance plane (Lake Formation) that every engine honours; and compute you turn on only when a query runs. Storage drops to S3 prices (and tiers down further when cold), each workload runs on the engine sized for it, and “who can see this column” becomes a single Lake Formation policy that Athena, Redshift Spectrum, and EMR all enforce identically.

The target outcome: store once, govern once, query with whatever is cheapest for the job, and pay for compute by the query — not by the calendar.

Lake, warehouse, or lakehouse — get the framing right

Before any architecture, fix the vocabulary, because the three words are thrown around interchangeably and they are not the same thing. The clearest way to tell them apart is to ask “where does the data physically live, and what guarantees does that give me?”

Dimension Data lake (raw S3 files) Data warehouse (Redshift) Lakehouse (S3 + Iceberg + Lake Formation)
Where data lives Object storage, open files Proprietary managed storage (RMS) Object storage, open table format
Storage cost Lowest (S3 + tiering) Highest (warehouse pricing) Lowest (S3 + tiering)
ACID transactions None Full Full (via Iceberg)
Schema discipline Schema-on-read only Schema-on-write, enforced Schema-on-read on bronze, enforced on silver/gold
Update / delete a row Rewrite files by hand UPDATE/DELETE MERGE INTO (Iceberg)
Time travel / rollback No Limited (snapshots/backups) Yes (FOR TIMESTAMP AS OF)
Storage–compute coupling Decoupled Coupled (loosened by RA3/Serverless) Decoupled
Governance S3 bucket/IAM (coarse) SQL GRANT (per-warehouse) Lake Formation — column/row/cell, every engine
Engines that can read it Many, but inconsistent behaviour One (Redshift) Many, consistent behaviour
Best at Cheap raw retention, ML feature stores Fast, curated BI All three over one governed copy
Main weakness No trust, no governance Cost, lock-in, forced copies Larger operational surface (you own file hygiene)

The row that matters most is governance. A warehouse governs data it owns; a lake barely governs at all; a lakehouse governs one shared copy so consistently that Athena, Redshift Spectrum, and EMR all see the exact same masked columns and filtered rows. That is the property you cannot get by “bolting a SQL engine onto a lake,” and it is the reason the lakehouse is not just a rebranded lake.

A worked example — one query’s journey. An analyst runs, in Athena:

SELECT store_id, SUM(net_sales) AS sales
FROM gold.daily_store_sales
WHERE region = 'US' AND sale_date >= DATE '2026-08-01'
GROUP BY store_id;

Here is what actually happens, and why the lakehouse design makes it both cheap and safe:

  1. Athena asks the Glue Data Catalog where gold.daily_store_sales lives and what its schema is. The catalog answers with the S3 location and the fact that it is an Iceberg table — Athena does not yet have permission to read the files.
  2. Because Lake Formation manages that location, Athena calls Lake Formation’s credential-vending API. Lake Formation checks this analyst’s grants: they hold SELECT on the table, but there is a column mask on customer_email (not selected here, so irrelevant) and no row filter for this role. It returns scoped, temporary credentials valid only for this table’s S3 prefixes, for this query.
  3. Athena reads the Iceberg metadata — the current snapshot’s manifest list — and sees the table is partitioned by days(sale_date) and physically clustered so that region and date prune cleanly. It skips every data file that cannot contain US rows from August onward.
  4. It scans only the surviving Parquet files, zstd-compressed and columnar, reading just the store_id, net_sales, region, and sale_date columns — say 12 GB, not the table’s full 3 TB.
  5. The bill is 12 GB ÷ 1024 × $5/TB ≈ $0.06, and it returns in seconds. The same data as raw uncompressed CSV, unpartitioned, would force a full 3 TB scan — roughly $15 and 100× slower — with no way to hide customer_email from someone who shouldn’t see it.

Every design choice in the rest of this lesson exists to make that journey cheap (partitioning, compression, compaction), safe (Lake Formation vending instead of raw S3 access), and identical no matter which engine the analyst happened to use.

Architecture overview

The data path runs left to right through five logical tiers. Imagine the diagram as a wide flow with a governance plane spanning the full width underneath and an observability plane spanning the full width on top — everything in the middle passes through both.

AWS data lakehouse reference architecture: ingestion (DMS, Kinesis/Firehose, Glue/AppFlow) lands raw data in the bronze S3 zone; medallion bronze→silver→gold with Apache Iceberg gives ACID tables on S3; Glue ETL and EMR transform; Athena, Redshift Spectrum and EMR/SageMaker query the same gold tables; a Glue Data Catalog plus Lake Formation governance plane vends scoped credentials to every engine, with a CloudTrail / CloudWatch / Glue Data Quality observability plane on top.

1. Ingestion (left edge). Three classes of source land in a raw landing bucket on S3. Batch files (vendor drops, exports) arrive via AWS DMS for relational CDC or direct S3 uploads. Streaming events (clickstream, app telemetry, IoT) flow through Amazon Kinesis Data Streams into Amazon Data Firehose, which buffers and writes partitioned objects to S3. SaaS and operational data come through AWS Glue connectors or AWS AppFlow. Nothing transforms here — raw is immutable and append-only, the system of record for replay.

2. The lake on S3 (center, the spine). S3 holds three zones following the medallion patternbronze (raw, as-landed), silver (cleaned, conformed, deduplicated), and gold (business-level aggregates and dimensional models). Crucially, silver and gold tables are written as open table formatsApache Iceberg is the default on AWS in 2026 — which means each table directory contains data files plus a metadata layer that gives ACID transactions, snapshot isolation, schema evolution, and time travel directly on S3. This is the line between a “data lake” and a “lakehouse.”

3. Catalog and governance (the plane underneath). The AWS Glue Data Catalog is the single technical metadata store — every table, schema, and partition is registered once and every engine reads it. AWS Lake Formation sits on top of the catalog as the permission layer: it owns the S3 locations, and instead of granting engines raw S3 access, you grant database/table/column/row/cell-level permissions in Lake Formation. Athena, Redshift Spectrum, and EMR all call Lake Formation’s credential-vending API at query time, so a single policy (“analysts cannot see the ssn column; the EU team sees only EU rows”) is enforced identically across every engine.

4. Processing and transformation. AWS Glue (Spark serverless ETL) does the bronze→silver→gold transforms on a schedule or event trigger, writing Iceberg tables and committing atomically. For heavy, long-running, or specialized work — large reprocessing, complex Spark/ML feature pipelines, or jobs needing specific libraries — Amazon EMR (on EC2 with Spot, or EMR Serverless) reads the same S3 + Glue Catalog and writes back the same Iceberg tables. Glue and EMR are not competitors here; they are two compute shapes over one storage and one catalog, chosen per job.

5. Consumption (right edge). Three query engines serve three patterns, all reading the same gold (and silver) tables:

BI tools (Amazon QuickSight, Tableau, Power BI) sit beyond the engines. The key property: the data is never copied into any engine permanently. S3 is the one copy; engines are ephemeral lenses over it, and Lake Formation governs every lens the same way.

Component breakdown

Component Role in the lakehouse Key configuration choices
S3 (3 buckets/zones) Single physical store for bronze/silver/gold; the one copy of all data Separate buckets per zone; partition by event date; S3 Intelligent-Tiering on bronze; lifecycle to Glacier for cold raw; Block Public Access + default SSE-KMS
Apache Iceberg ACID table format over S3 — transactions, time travel, schema evolution, hidden partitioning Default format for silver/gold; copy-on-write for gold (read-heavy), merge-on-read for high-churn silver; schedule OPTIMIZE (compaction) and snapshot expiry
Glue Data Catalog One technical metadata store every engine shares One catalog per environment; databases per domain; Iceberg tables registered natively; crawlers only for bronze schema discovery
Lake Formation The governance plane — fine-grained, engine-agnostic permissions Register S3 locations; hybrid access mode during migration; tag-based access control (LF-Tags) for scale; column masking + row/cell filters; cross-account sharing
AWS Glue ETL Serverless Spark for routine bronze→silver→gold transforms Glue 5.0 (Spark 3.5); job bookmarks for incremental; Auto Scaling workers; Flex execution for non-urgent jobs (~↓ cost)
Amazon EMR Heavy / specialized Spark, big reprocessing, ML feature pipelines EMR on EC2 with Spot for task nodes (60–90% off) or EMR Serverless for spiky jobs; EMRFS + Glue Catalog; runtime role for Lake Formation enforcement
Amazon Athena Serverless ad-hoc SQL, the default exploration engine Athena engine v3 (Trino); workgroups with per-query data-scan limits + cost guardrails; results to a dedicated S3 location; reusable query results cache
Redshift Spectrum Warehouse BI joining hot local dims against S3 gold tables Redshift Serverless or small RA3 cluster; external schema → Glue Catalog; keep only hot dimensions resident; materialized views for hot aggregates
Kinesis + Firehose Streaming ingestion into bronze Firehose dynamic partitioning; buffer to balance freshness vs. small-file count; optional Parquet conversion on write
DMS / AppFlow Relational CDC and SaaS ingestion DMS CDC to bronze, merged into silver Iceberg with MERGE INTO; AppFlow for SaaS sources

Why each is here, briefly:

S3 zones, not one bucket. Three buckets give you blast-radius isolation, distinct lifecycle policies (bronze tiers aggressively, gold rarely), and clean IAM/Lake Formation boundaries. Bronze is immutable and replayable; silver and gold are recomputable from bronze, which is the backbone of your DR story.

Iceberg is the decision that makes it a lakehouse. Plain Parquet on S3 has no transactions: a failed Spark job leaves half-written files and readers see corruption. Iceberg’s metadata layer gives atomic commits (a query sees the table before or after a write, never mid-write), time travel (FOR TIMESTAMP AS OF for audits and “oops” recovery), and schema evolution without rewriting data. On AWS in 2026, Iceberg has first-class support across Glue, Athena (v3), EMR, and Redshift, which is why it’s the default over Hudi/Delta here — one format every engine reads natively.

Glue Catalog is the contract; Lake Formation is the lock. The catalog says what exists (one source of truth for schema). Lake Formation says who may see it — and because every engine vends credentials through Lake Formation rather than reading S3 directly, you cannot accidentally have “Athena can see the SSN column but the BI tool can’t.” The policy lives in one place and travels with the data.

Glue vs. EMR is a workload decision, not a religious one. Glue: serverless, fast to start, ideal for the steady bronze→silver→gold pipeline and teams who don’t want to manage clusters. EMR: when you need Spot economics on long jobs, specific library versions, very large reprocessing, or co-locating ML feature engineering with training. Both write the same Iceberg tables to the same S3 catalog — you can move a job from one to the other without changing the data.

Three query engines because one size never fits. A data scientist running SELECT ... LIMIT 100 ten times an hour should not touch a warehouse cluster — that’s Athena (zero standing cost, pay per scan). A nightly executive dashboard joining a 10-row date dimension against a billion-row fact wants the warehouse’s local-dimension speed — that’s Redshift Spectrum (hot dims resident, facts on S3). A 4-hour model-training run wants EMR/SageMaker reading Parquet directly. Forcing all three onto one engine is exactly the coupling the lakehouse exists to break.

Implementation guidance

Bucket and zone layout. Three buckets with consistent prefixing:

s3://acme-lake-bronze-<acct>/<source>/<table>/ingest_date=YYYY-MM-DD/
s3://acme-lake-silver-<acct>/<domain>/<table>/        # Iceberg-managed layout
s3://acme-lake-gold-<acct>/<domain>/<table>/          # Iceberg-managed layout

Bronze is partitioned by ingest date for cheap pruning and lifecycle. Silver/gold use Iceberg hidden partitioning — you declare PARTITIONED BY (days(event_ts)) and queries filter on event_ts directly without knowing the physical layout, so you can change partitioning later without rewriting queries.

Terraform is the right IaC here (the team standardizes on it). Manage as code:

Iceberg table creation (via Athena/Glue DDL) — note the explicit format and compaction intent:

CREATE TABLE silver.transactions ( ... )
PARTITIONED BY (days(event_ts))
LOCATION 's3://acme-lake-silver-<acct>/payments/transactions/'
TBLPROPERTIES (
  'table_type'='ICEBERG',
  'format'='parquet',
  'write_compression'='zstd',
  'optimize_rewrite_delete_file_threshold'='10'
);

Then schedule OPTIMIZE silver.transactions REWRITE DATA USING BIN_PACK (compaction) and snapshot expiry as a Glue job — this is the small-file fix, covered below.

Incremental upserts from CDC use Iceberg MERGE INTO, which is the lakehouse’s answer to the old “you can’t update a data lake” problem:

MERGE INTO silver.customers t USING bronze_cdc.customers s
ON t.id = s.id
WHEN MATCHED AND s.op='D' THEN DELETE
WHEN MATCHED THEN UPDATE SET ...
WHEN NOT MATCHED THEN INSERT ...;

Networking. Keep all data-plane traffic on the AWS backbone via VPC Gateway Endpoint for S3 (free) and Interface (PrivateLink) endpoints for Glue, Lake Formation, Athena, KMS, and STS. EMR and Redshift run in private subnets, no public IPs; NAT only for OS/package egress. QuickSight reaches Redshift/Athena through a VPC connection. The result: queryable data never traverses the public internet.

Identity wiring. Human access flows from your IdP (the org standardizes on Entra ID) through IAM Identity Center with SAML/SCIM, mapping IdP groups to permission sets that assume data-access roles. Those roles are then granted Lake Formation permissions (not S3 permissions). So “analysts” in Entra → an Identity Center group → a permission set/role → a Lake Formation grant of SELECT on gold with the ssn column masked. Pipeline identities (Glue/EMR job roles) get their own Lake Formation grants. No human or job role gets direct s3:GetObject on the lake buckets — Lake Formation vends scoped, temporary credentials per query. This is the single most important wiring decision in the build.

Enterprise considerations

Security and Zero Trust. The model is “no implicit S3 access; every read is brokered.” Concretely: (1) Block Public Access on all three buckets, account-wide; (2) SSE-KMS with separate CMKs per zone so you can revoke gold independently and get per-zone audit in CloudTrail; (3) Lake Formation as the only path to data — pipeline and human roles hold Lake Formation grants, not bucket policies; (4) column masking on PII (ssn, card_pan) and row/cell filters for tenancy/region (EU analysts see only region='EU'); (5) all access on PrivateLink; (6) CloudTrail data events on the buckets plus Lake Formation’s own audit log give you a complete “who read which column when” trail. Tag-based access control (LF-Tags) keeps this manageable: tag tables confidentiality=pii once and grant on the tag, so new tables inherit policy automatically instead of needing per-table grants.

Cost optimization — this is where the lakehouse earns its keep, and it’s multi-layered:

Scalability. S3 scales to effectively unlimited objects and throughput; partition-prefix design avoids hotspots. Athena and Glue are serverless and absorb concurrency automatically. EMR and Redshift scale horizontally (and Redshift concurrency-scaling adds transient clusters for BI spikes). The catalog scales to millions of partitions. The real scaling discipline is file hygiene, not capacity: streaming ingestion creates millions of tiny files that wreck query performance, so scheduled Iceberg compaction (OPTIMIZE) and snapshot/orphan-file expiry are mandatory operational jobs, not nice-to-haves.

Reliability and DR (RTO/RPO).

Observability. Glue/EMR push Spark metrics and logs to CloudWatch; Athena and Spectrum query history expose bytes scanned and runtime per query (the cost signal). Critically, add data-quality gatesAWS Glue Data Quality (DQDL rules) on the silver→gold boundary so freshness, null-rate, and referential checks fail the pipeline before bad data reaches dashboards. CloudTrail + Lake Formation audit logs cover access observability. Track three SLOs: pipeline freshness (gold lag), query cost (bytes scanned/day), and DQ pass rate.

Governance. Lake Formation LF-Tags are the scalable model — a taxonomy like domain, confidentiality, retention applied to databases/tables, with grants written against tags so policy is inherited, not hand-maintained per object. This is also the foundation for a data-mesh evolution: each domain owns its silver/gold databases and grants access to others via Lake Formation cross-account sharing, with a central team owning only the tag taxonomy and platform. Pair with a business catalog (e.g. Amazon DataZone) for discovery and access requests on top of the technical Glue catalog.

Reference enterprise example

NorthPeak Retail is a fictional 1,400-store home-goods chain, ~$4.2B revenue, with the classic mess: a Redshift cluster at the limit of its node count, an aging self-managed Hadoop cluster, eight unowned Glue jobs, and an analytics team that exports CSVs because warehouse access takes two weeks. Storage and compute are fused; the annual data-platform bill is ~$2.1M and rising, and the last audit flagged that nobody could prove who could read customer PII.

What they built. Over two quarters they moved to the architecture above:

Decisions worth noting. They chose Iceberg over Delta for native Redshift Spectrum + Athena v3 support across every engine. They kept Redshift (Serverless) only for hot dimensions rather than loading the lake into it — Spectrum does the heavy fact joins on S3. They set Athena workgroup per-query scan caps at 2 TB after one analyst’s SELECT * scanned 40 TB in the pilot. They made nightly OPTIMIZE + snapshot expiry a first-class pipeline after streaming ingestion produced 9M tiny files in week one and Athena latency tripled.

The outcome (12 months).

When to use it

Use this lakehouse when:

Trade-offs and anti-patterns:

Alternatives and how to choose:

Situation Better fit
Single team, pure SQL BI, modest/predictable volume, no ML Redshift-only warehouse — simpler ops, fewer moving parts
Want managed lakehouse platform, less AWS plumbing, multi-cloud Databricks on AWS (Unity Catalog + Delta)
Mostly ad-hoc SQL on S3, governance not yet a requirement Athena + Glue, defer Lake Formation until access control matters
Sub-second operational analytics / serving DynamoDB / Aurora / OpenSearch, not a lakehouse
Streaming-first, real-time materialized views the core need Kinesis + Flink / Managed Service for Apache Flink as the spine

The lakehouse on AWS is the right default when one governed copy of data on S3, queried by the cheapest engine that fits each job, beats the cost and access friction of a single all-purpose warehouse — which, for any organization running ad-hoc, BI, and ML over a growing dataset, is most of the time.

Going deeper

The sections above are the architecture an experienced team can ship. This section is the layer under it — what Iceberg actually writes to S3, how Lake Formation hands out credentials, the newer AWS building blocks that are quietly redrawing the picture in 2026, and the performance math you need when someone asks “why is this query slow / expensive.”

What Iceberg actually writes to S3 (and why atomic commits work)

An Iceberg table is not a magic file type — it is a tree of small metadata files sitting next to your Parquet data, plus one pointer that the catalog owns. Understanding the tree explains every Iceberg feature you rely on.

Glue Catalog entry  ──▶  metadata/v37.metadata.json     (current table state, schema, partition spec, snapshot list)
                              │
                              └─▶ snap-<id>.avro          (a "snapshot" = a manifest list, one per commit)
                                       │
                                       ├─▶ manifest-a.avro   (lists data files + per-file column stats: min/max, null counts)
                                       └─▶ manifest-b.avro
                                                │
                                                ├─▶ 00001-data.parquet   (your actual rows)
                                                ├─▶ 00002-data.parquet
                                                └─▶ 00003-delete.parquet  (merge-on-read delete markers, if any)

Every write — an INSERT, a MERGE INTO, a compaction — produces a new metadata.json and a new snapshot; nothing existing is mutated in place. The commit is atomic because it ends with a single compare-and-swap on the catalog pointer: “if the current metadata is still v36, make it v37.” If two jobs race, one wins the swap and the other retries against the new state. That is the whole trick behind “a reader sees the table before or after a write, never mid-write.” It is also why time travel is free: old snapshots still point at their old data files, so FOR TIMESTAMP AS OF just reads an older metadata pointer.

The column min/max stats in the manifest files are what make queries fast — the engine reads the manifest, sees “this data file’s sale_date ranges 2026-01-01 to 2026-01-31,” and skips the whole file for an August query without opening it. This is file pruning, and it is why partitioning and sorted writes matter so much: they tighten those min/max ranges.

Copy-on-write vs merge-on-read — the write-mode decision

Iceberg offers two ways to apply an UPDATE/DELETE/MERGE, set per operation via table properties (write.update.mode, write.delete.mode, write.merge.mode):

Copy-on-write (COW) Merge-on-read (MOR)
On write Rewrites every data file containing a changed row Writes small delete files + new data files; leaves the rest
Write cost High (rewrites whole files) Low (touches only the delta)
Read cost Low (files are already clean) Higher (reader merges deletes at query time)
Best for Gold — read-heavy, infrequent updates Silver — high-churn CDC ingest
Compaction need Lower Mandatory and frequent (delete files pile up)

The rule of thumb the reference architecture uses: MOR on silver (CDC hammers it with upserts all day, and you compact hourly), COW on gold (read constantly by dashboards, updated in controlled batches). Getting this backwards — COW on a high-churn CDC table — is a classic way to make Glue jobs run 10× longer than they should, because every micro-batch rewrites gigabytes of untouched data.

Iceberg vs Hudi vs Delta — why Iceberg is the AWS default

All three are open table formats that add ACID to files on S3. They are more alike than different; the choice on AWS is mostly about native, first-class support across every AWS engine.

Apache Iceberg Apache Hudi Delta Lake
AWS-native reach Athena v3, Glue, EMR, Redshift, S3 Tables, SageMaker Lakehouse — the broadest Glue, EMR, Athena (read) Glue, EMR, Athena (read); best on Databricks
Origin / strongest home Netflix; vendor-neutral Uber; streaming/CDC upserts Databricks
Hidden partitioning Yes Partial No (needs explicit columns)
Sweet spot General analytics, broad interop Record-level streaming upserts, indexes Databricks-centric estates
AWS strategic signal S3 Tables and SageMaker Lakehouse are Iceberg-native Supported, not the default Supported, not the default

The tie-breaker is not a benchmark; it is that AWS is putting its own new managed services — S3 Tables and the SageMaker Lakehouse catalog — on Iceberg. Choosing Iceberg means one format that Athena, Redshift Spectrum, EMR, and the newer managed layers all read natively, with no conversion. Pick Hudi only if you have a genuine record-level streaming-upsert case with its indexing needs; pick Delta mainly if you are also heavily invested in Databricks.

The 2026 direction: S3 Tables, SageMaker Lakehouse, and Zero-ETL

Three newer AWS capabilities are collapsing the seams in the architecture above. They are worth knowing even if you build on the established stack today.

Where do these fit the reference architecture? Treat them as upgrades to specific boxes, not a rewrite: S3 Tables can back your silver/gold Iceberg storage and retire your compaction Glue jobs; SageMaker Lakehouse can become the catalog/governance plane spanning lake + Redshift; Zero-ETL can replace the DMS-to-bronze CDC path for the operational databases where seconds-fresh warehouse copies matter more than a replayable raw landing zone.

Redshift in a lakehouse: RA3, Managed Storage, and data sharing

Redshift is not the enemy of the lakehouse — in this design it is the BI-serving engine that holds hot dimensions, and three of its features are what let it play nicely with S3:

The mental model: RA3 + RMS make Redshift storage cheap and decoupled; Spectrum makes S3 gold joinable; data sharing makes one copy reach many consumers. Redshift becomes a lens, like Athena and EMR — just the lens tuned for concurrent, low-latency BI.

Lake Formation credential vending — the mechanism, precisely

The claim “every engine enforces the same policy” rests on one API. When an integrated engine (Athena, Redshift Spectrum, EMR with the Lake Formation runtime role, or a Glue job) needs to read a governed table, it does not use its own IAM role’s S3 permissions. Instead it calls Lake Formation (GetTemporaryGlueTableCredentials / the credential-vending path), passing the table and the caller’s identity. Lake Formation:

  1. Evaluates the caller’s grants (direct or via LF-Tags), including column masks and row/cell filters.
  2. If the query touches a filtered column or row set, it rewrites the request plan so the engine physically cannot read excluded data — column projection drops masked columns; row filters become predicate pushdowns.
  3. Returns STS credentials scoped by an IAM session policy to exactly the S3 prefixes and objects the caller may read, valid for the query’s lifetime.

Two consequences matter in production. First, this is why you must remove IAMAllowedPrincipals — while that legacy grant exists, engines fall back to their own broad S3 IAM and never call the vending path, so your fine-grained policy is bypassed entirely. Second, it is why no human or job role should carry s3:GetObject on the lake buckets: if they do, they have a back door around the security desk. The whole model is “the only key to the shelves is issued, per-query, by Lake Formation.”

Small-file economics — the math, made concrete

“Compaction is mandatory” is easy to say; here is why, quantified. Suppose a Firehose stream lands one object every 60 seconds per partition, and you have 200 active partitions. That is 200 × 1440 = 288,000 tiny files per day. Each file forces the query engine to: open it, read its footer, and issue at least one S3 GET. Query planning time and request overhead now dominate; a scan that should take 8 seconds takes 4 minutes, and your S3 request bill balloons independently of bytes scanned.

Compaction (OPTIMIZE ... REWRITE DATA USING BIN_PACK) rewrites those 288,000 files into, say, 1,150 files of ~256 MB — the size range engines are tuned for. The scan drops back to seconds, request counts fall by ~250×, and snapshot expiry (VACUUM, or S3 Tables’ automatic maintenance) then deletes the now-orphaned small files so you stop paying to store them. The design levers that prevent the problem in the first place: tune Firehose buffer hints higher (fewer, bigger objects on landing) and prefer MOR + scheduled compaction over per-micro-batch rewrites. The design lever that fixes it: never ship a streaming lakehouse without compaction and expiry as first-class, monitored pipeline jobs.

Concurrency, cost caps, and the failure modes worth pre-empting

Practice challenges

Work these in order — they escalate from “can you read the architecture” to “can you defend a design decision under cost and security pressure.” Try each before opening the solution. Nothing here needs a live AWS account; the point is reasoning, not running.

1. (Beginner) Place the zones. A vendor drops a raw daily CSV of orders; your pipeline cleans and deduplicates it, then a nightly job builds a per-store revenue summary the dashboards read. Which medallion zone (bronze / silver / gold) does each of the three artefacts belong in, and which one must stay immutable?

<details><summary>Solution</summary>

Raw CSV → bronze (as-landed, immutable, append-only, replayable). Cleaned + deduplicated orders → silver (conformed). Per-store revenue summary → gold (business-level aggregate the dashboards read). Bronze is the one that must stay immutable — it is the system of record you replay silver and gold from.

Why: the medallion pattern is quality tiers of the same data; keeping bronze immutable is what makes silver/gold recomputable, which underpins the whole DR story. </details>

2. (Beginner→Intermediate) Pick the engine. Match each workload to Athena, Redshift Spectrum, or EMR/SageMaker: (a) a data scientist runs SELECT ... LIMIT 100 twenty times an hour while exploring; (b) 40 executive QuickSight dashboards join a 10-row date dimension against a billion-row fact every morning; © a monthly 4-hour Spark job reprocesses 14 months of data and builds ML features.

<details><summary>Solution</summary>

(a) Athena — serverless, pay-per-scan, zero standing cost for bursty ad-hoc work. (b) Redshift Spectrum — hot dimensions resident in a small cluster, billion-row facts joined from S3 gold; warehouse-grade BI latency and concurrency. © EMR / SageMaker — long, heavy Spark with Spot economics, reading Parquet directly.

Why: the lakehouse exists precisely so ad-hoc, BI, and ML each run on the engine sized for them over one copy — forcing all three onto one engine is the coupling you are breaking. </details>

3. (Intermediate) The masks are decorative. A colleague has configured Lake Formation column masks on ssn and row filters for the EU team, but an audit shows analysts can still s3:GetObject the raw Parquet and read ssn directly. Name the two things most likely wrong and how you’d fix them.

<details><summary>Solution</summary>

(1) IAMAllowedPrincipals was never removed from the databases/tables, so engines fall back to broad IAM S3 access and never call Lake Formation’s credential-vending path — remove it so Lake Formation actually governs. (2) The analyst/job roles still hold direct s3:GetObject on the lake buckets — a back door around the security desk. Strip all direct bucket access; the only path to data must be Lake Formation vending scoped, temporary credentials per query.

Why: fine-grained masks are enforced only when the engine reads through Lake Formation; any surviving direct S3 grant or the legacy IAM default makes the policy theatre. </details>

4. (Intermediate) Choose the write mode. You have a silver customers table that a CDC stream upserts thousands of times an hour, and a gold daily_sales table refreshed once nightly and read by dashboards all day. Which Iceberg write mode — copy-on-write or merge-on-read — for each, and what operational job does your choice make non-negotiable?

<details><summary>Solution</summary>

Silver customersmerge-on-read (MOR): cheap writes (delete files + new data, no full rewrite) suit the high-churn CDC stream. Gold daily_salescopy-on-write (COW): clean files give the all-day dashboard reads the lowest read cost, and the once-nightly refresh makes the rewrite affordable. The non-negotiable job: frequent compaction (OPTIMIZE ... BIN_PACK) plus snapshot/orphan expiry on the MOR silver table, or delete files pile up and reads slow down.

Why: MOR trades read cost for write cost (right for churn), COW trades write cost for read cost (right for read-heavy) — and MOR without compaction degrades exactly where you chose it for speed. </details>

5. (Advanced) Do the cost math. An analyst’s SELECT * on a 3 TB gold table scans the whole thing at $5/TB. Your teammate proposes three fixes: (a) partition by sale_date and require a date predicate, (b) store zstd-compressed columnar Parquet and select only the 4 needed columns, © set an Athena workgroup per-query scan cap of 2 TB. Which fixes cut the bill, which is a guardrail, and roughly what does a well-partitioned, columnar version of the same query cost?

<details><summary>Solution</summary>

(a) and (b) cut the bill — partition pruning skips files outside the date range, and columnar + column projection + compression read only the needed columns’ bytes; together they typically cut bytes scanned by 90%+. © is a guardrail — it does not make any single well-written query cheaper, it caps the blast radius of a runaway SELECT * (stops it at 2 TB / $10 instead of scanning 40 TB / $200). A pruned, columnar version scanning ~12 GB costs 12 ÷ 1024 × $5 ≈ $0.06 versus 3 × $5 = $15 for the full raw scan — roughly 250× cheaper.

Why: Athena bills bytes scanned, so partitioning and columnar projection attack the bill directly; scan caps are cost insurance, not optimisation — you want both. </details>

6. (Advanced) Design the DR story. Bronze is your replayable system of record; silver and gold are recomputable from it. Sketch a DR plan that gives ~15-minute RPO and a few-hours RTO for a Region failure, and separately explain how you’d recover from a logical corruption (a bad pipeline run poisoned gold) in minutes. What is the one component that S3 durability does not protect, and how do you cover it?

<details><summary>Solution</summary>

Region failure: enable S3 Cross-Region Replication on bronze only (RPO ≈ replication lag ≈ 15 min); silver/gold are recomputable, so you don’t replicate them. In DR, re-point the catalog and re-run pipelines to rebuild silver/gold from replicated bronze (RTO ≈ a few hours). Logical corruption: use Iceberg time travel — roll the poisoned gold table back to the prior snapshot in seconds (FOR VERSION AS OF / snapshot rollback), no rebuild. The unprotected component: the catalog (Glue Data Catalog + Lake Formation grants) lives in the control plane, not S3 — export it via IaC/scripted exports so it is reproducible in the DR Region. Consider S3 Tables to make maintenance managed, but the catalog-DR gap is still yours to close.

Why: replicate only the irreplaceable layer and recompute the rest; time travel is the fast path for logical (not physical) failures; and a lake whose catalog you can’t rebuild is just opaque files. </details>

Common beginner mistakes

These are misconceptions, not typos — the wrong mental model that leads a well-meaning team into an expensive corner. Each pairs the trap with the model that avoids it.

“A lakehouse is just a data lake with Athena on top.” The seductive wrong idea: point Athena at some Parquet in S3 and call it a lakehouse. Without an open table format you have no ACID — a failed write leaves half-written files readers see as corruption — and without Lake Formation you have no consistent governance. The right model: a lakehouse is the combination of open table format (Iceberg, for transactions and time travel) and a single governance plane (Lake Formation, for engine-agnostic fine-grained access). Athena is a lens; Iceberg and Lake Formation are what make the thing a lakehouse.

“Iceberg makes storage slow/expensive, so I’ll skip it on hot data.” The misconception is that the metadata layer is overhead. It is the opposite: Iceberg’s per-file min/max stats are what let engines skip files without opening them, and hidden partitioning is what lets a query prune without knowing the physical layout. The right model: Iceberg on silver/gold usually makes queries faster and cheaper, not slower. Where you legitimately skip it is bronze — raw, throwaway, or intermediate data that no one queries as a table gains nothing from a managed format.

“Compaction is an optimisation I’ll get to later.” Beginners treat file hygiene as a nice-to-have. With streaming ingestion, “later” is week one: hundreds of thousands of tiny files per day turn an 8-second scan into minutes and inflate the S3 request bill independently of bytes scanned. The right model: compaction (OPTIMIZE) and snapshot/orphan expiry are mandatory, monitored pipeline jobs, not chores — or adopt S3 Tables to have AWS run them for you. A streaming lakehouse without compaction will collapse.

“I set up Lake Formation, so my data is governed.” The trap: configure beautiful column masks and row filters, then leave the legacy IAMAllowedPrincipals grant in place and let job roles keep direct s3:GetObject. Both are back doors that bypass every mask. The right model: Lake Formation governs only the reads that flow through its credential-vending path — so removing IAMAllowedPrincipals and stripping all direct bucket access from human and job roles is part of turning governance on, not an optional hardening step later.

“Redshift is the old way; a real lakehouse replaces it.” New teams often frame it as lake versus warehouse. In this architecture Redshift is a first-class engine — the one tuned for concurrent, low-latency BI on hot dimensions, with RA3/RMS decoupling its storage and Spectrum joining S3 gold. The right model: you don’t rip Redshift out, you right-size it to hot dimensions and let Spectrum do the heavy fact joins on S3. Pick the cheapest engine per job; sometimes that engine is Redshift.

“Zero-ETL / S3 Tables / SageMaker Lakehouse means I don’t need the medallion pipeline anymore.” The newest managed features are tempting to read as “the architecture is obsolete.” They are upgrades to specific boxes, not a replacement for curation. The right model: you still want bronze as the replayable system of record and silver/gold as governed, conformed tables. Zero-ETL shortcuts a CDC pipeline; S3 Tables shortcuts compaction; SageMaker Lakehouse unifies the catalog — none of them decide what “clean, conformed, business-ready” means for your data. That is still design work you own.

“Put everything in Iceberg on day one.” Over-eager teams convert every intermediate and throwaway dataset to managed tables. That adds compaction and metadata cost for data no one queries as a table. The right model: bronze stays raw; curate only silver and gold. Managed-table cost is justified by managed-table benefits (ACID, governance, time travel) — spend it where those benefits are actually consumed.

Glossary

AWSArchitectureEnterpriseReference Architecture
Need this built for real?

Vinod is a Senior Cloud Architect (22+ yrs) — available for Azure / AWS / GCP architecture, landing zones, and migrations.

Work with me

Comments