data-eng-lab → Atlas: Expectations & Enablement Hand-off¶
Audience: the engineer(s) working on Atlas (thekaveh/atlas).
Purpose: a single, authoritative statement of everything data-eng-lab expects from Atlas — what is already delivered (so you don't undo it), and the Iceberg/Spark capabilities the scenario catalog relies on. This supersedes the ad-hoc atlas-enablement.md ledger as the hand-off reference; that file remains the terse A1–A9 origin status table.
data-eng-lab consumes Atlas as a pinned submodule at infra/ (currently atlas 85ff46b) and never edits it — enhancements come to you as issues/PRs. Everything below was verified against atlas 72e30d1, and Atlas's consumer-doc clarifications from #281 (85ff46b) resolved our A7/A9 feedback (see atlas-feedback-a7a9.md section F).
0. TL;DR status¶
| Item | Capability | Status | Blocks |
|---|---|---|---|
| A1 | Iceberg REST catalog + buckets | ✅ delivered (#245) | — |
| A2 | Spark 4.1.2 + Iceberg runtime on the cluster | ✅ delivered (#246) | — |
| A3 | Zeppelin Spark interpreter seeding | ✅ delivered | — |
| A4 | JupyterHub lakehouse Python clients | ✅ delivered | — |
| A5 | Jenkins Maven Spark builder | ✅ delivered (#253) | — |
| A6 | Airflow S3A spark-submit path | ✅ delivered (#249) | — |
| A7 | Trino query engine | ✅ delivered (#270) | — |
| A8 | data-eng track membership |
✅ delivered (#250) | — |
| A9 | Redpanda (Kafka API) + Spark Kafka connector | ✅ delivered | — |
Key takeaway: A1–A9 are now live. The full scenario catalog can execute (Spark + Iceberg + Trino + Kafka streaming). See Delivered deviations for the %trino interpreter name and topic-seeding notes.
1. Delivered capabilities — confirmed reality (do not regress)¶
A1 — Iceberg REST catalog + buckets¶
- Service
iceberg-rest: baseapache/iceberg-rest-fixture:1.10.1, locally rebuilt to layer the Postgres JDBC driver (ICEBERG_REST_POSTGRES_JDBC_VERSION, default42.7.12) onto the classpath. In-networkhttp://iceberg-rest:8181; host-published onICEBERG_REST_PORT(default 63020). - Backing store: JDBC catalog → Supabase Postgres (
jdbc:postgresql://supabase-db:5432/iceberg), seeded byiceberg-rest-init(creates theicebergDB + role). Catalog namelakehouse(a client concern; every Atlas client useslakehouse). - Warehouse:
s3://lakehouse/server-side (S3FileIO withs3.endpoint=http://minio:9000, path-style). Spark clients address it ass3a://lakehouse/. - MinIO buckets created by
minio-initunder a scoped account (MINIO_ICEBERG_ACCESS_KEY/_SECRET_KEY):landing,lakehouse,jars,checkpoints(+spark-historyfromspark-init).data-eng-labadds only its ownlakehouse-test. - GOTCHA — no namespaces are seeded.
bronze/silver/golddo NOT exist on a fresh stack.data-eng-labcreates them host-side viascripts/register_iceberg.py; apps also self-create (CREATE NAMESPACE IF NOT EXISTS). Please keep it this way (app-owned namespaces) unless you decide to seed them — if you seed, tell us.
A2 — Spark + Iceberg runtime¶
- Spark
4.1.2, Scala2.13. Baked into/opt/spark/jars(SHA-verified):iceberg-spark-runtime-4.1_2.13:1.11.0,iceberg-aws-bundle:1.11.0,hadoop-aws:3.4.2, AWS SDK v2bundle:2.29.52. - Spark Connect at
sc://spark-connect:15002(backend-only). Standalone masterspark://spark-master:7077, N workers, history server. - The full catalog config (
spark.sql.extensions=…IcebergSparkSessionExtensions,spark.sql.catalog.lakehouse.*= rest/uri/warehouse/S3FileIO/creds/region) is injected into thespark-connectservice. - GOTCHA — standalone master/workers do NOT inherit that catalog config. Cluster-mode
spark-submitjobs (i.e. Airflow) must passspark.sql.catalog.lakehouse.*themselves.data-eng-labdoes this in its DAGs (copied from your referenceservices/airflow/dags/lakehouse_spark_submit_smoke.py) and has a CI guard test enforcing it. If you ever make the master/workers carry the catalog defaults, that would simplify our DAGs — but is not required.
A3 — Zeppelin¶
- The
sparkinterpreter is auto-seeded (init container merges Atlas-owned properties via REST). GOTCHA — Zeppelin uses standalonespark.master=spark://spark-master:7077(client mode), NOT Spark Connect (spark.remoteis explicitly removed; Spark 4 rejects mixing). Our%sparkScala notebooks rely on this seeded interpreter. - We now also need
%trinofor the Trino scenarios — see A7. Today the Trino interpreter is seeded by Atlas (no manual UI step needed per our agreement).
A4 — JupyterHub¶
- Image adds
boto3,s3fs,pyiceberg[s3fs]==0.11.1,duckdb,pyarrow(+pyspark-client==4.1.2). Env:SPARK_REMOTE=sc://spark-connect:15002,ICEBERG_REST_URI, PyIceberg auto-config. - GOTCHA — Jupyter uses the
jupyter-scoped MinIO account, not the iceberg one. Direct PyIceberg writes tolakehousemay be denied; the Spark-Connect path (correctly scoped) is the write path our notebooks use. If you can grant the jupyter account read onlakehouse, our Jupyter read demos get simpler.
A5 — Jenkins¶
jenkins/jenkins:lts-jdk21+ Maven + MinIOmc. JCasC only; no seed jobs, nomcalias shipped (by design — "downstream provides jobs"). Host portJENKINS_PORT(default 63080); adminJENKINS_ADMIN_USER/_PASSWORD.- Publish contract (documented in your
services/jenkins/README.md):mc alias set atlas "$MINIO_ENDPOINT" "$MINIO_ICEBERG_ACCESS_KEY" "$MINIO_ICEBERG_SECRET_KEY"thenmc cp target/*.jar "atlas/$MINIO_BUCKET_ICEBERG_JARS/<app>/<version>/app.jar".data-eng-labsupplies its own job viajenkins/seed-job.sh(REST create-item) +jenkins/nyc-taxi-etl-job.xml. No change requested — this split works; just don't start shipping conflicting jobs.
A6 — Airflow¶
apache/airflow:3.2.2withhadoop-aws+ the Iceberg runtime baked (client or cluster submit). Providers: apache-spark, amazon. DefaultATLAS_LAKEHOUSE_SPARK_DEPLOY_MODE=cluster.- Connections seeded when the sources are
container:spark_default(spark://spark-master:7077, extradeploy-mode: cluster) andminio_default(aws,http://minio:9000). Reference DAGdags/lakehouse_spark_submit_smoke.py— our DAGs mirror itsconf.
A7 — Trino query engine¶
- Trino
trinodb/trino:482with Iceberg REST connector pointed at the existing lakehouse catalog. - Catalog reachable as
lakehouse.<namespace>.<table>(namelakehouse). - Coordinator in-net
trino:8080, host-published onTRINO_PORT(default 63029). - Iceberg connector config:
iceberg.catalog.type=rest,iceberg.rest-catalog.uri=http://iceberg-rest:8181, warehouses3://lakehouse/,s3.endpoint=http://minio:9000(path-style), iceberg-scoped MinIO creds. - Supports CTAS (
CREATE TABLE lakehouse.<schema>.<table> AS SELECT …) for ourfederated_query/bi_queryscenarios. - Zeppelin
%trinointerpreter (groupjdbc, JDBC URLjdbc:trino://trino:8080, cataloglakehouse, useratlas, no auth) auto-seeded — no manual UI step. (Note: interpreter name istrino, notjdbc(trino)— Zeppelin 0.12.1 uses the interpreter name as paragraph prefix.) - Python/Jupyter client reaches
trino:8080in-net /localhost:$TRINO_PORTfrom host. --trino-sourceflag; member ofdata-eng; defaultdisabled.
A8 — Track & launch¶
data-engtrack =[spark, airflow, jupyterhub, zeppelin, minio, iceberg-rest, trino, redpanda, supavisor, weaviate, neo4j]. New launch flags--iceberg-rest-source,--jenkins-source,--trino-source,--redpanda-source(all defaultdisabled).supavisoris an optional Postgres pooler (not a lakehouse dependency).
A9 — Redpanda (Kafka API) + Spark Kafka connector¶
redpandadata/redpanda:v26.1.12broker with Kafka API.- Broker in-net
redpanda:9092(Kafka API port), host-published onREDPANDA_KAFKA_PORT(default 63010). - Optional console on
REDPANDA_CONSOLE_PORT(default 63011). - Topic-creation seam:
REDPANDA_DEMO_TOPICS(defaultatlas_stream_events) defines topics pre-created at bootstrap viarpk topic create(idempotent). Downstream can override in.envto seed their own topics; topics not in that list rely onauto_create_topics_enabled(enabled indev-containermode). - Spark Kafka connector (
org.apache.spark:spark-sql-kafka-0-10_2.13:4.1.2) baked into the single Spark image (used by spark-master, spark-worker, spark-connect, spark-history) withspark-token-provider-kafka-0-10_2.13:4.1.2,kafka-clients:3.9.1,commons-pool2:2.12.1(all SHA-512-verified) —readStream.format("kafka")works in both Spark Connect and standalone with no--packages. - Streaming checkpoints use the existing
s3a://checkpoints/bucket (MinIO). --redpanda-sourceflag; member ofdata-eng; defaultdisabled.- data-eng-lab consumers:
scenarios/streaming_ingest-events-spark-iceberg/(built, live-gated) +producer.py;tests/scenarios/test_streaming_live.py; roadmap CDC/stateful streaming.
2. Delivered deviations from our A7/A9 asks¶
Divergence 1: Zeppelin interpreter is %trino, not %jdbc(trino)¶
We requested %jdbc(trino) per older Zeppelin semantics. Atlas correctly documented that Zeppelin 0.12.1 uses the interpreter name as the paragraph prefix — so the named trino interpreter surfaces as %trino. This is the intended Zeppelin 0.12.1 happy path. Our notebooks now use %trino. No Atlas change required; this is better UX.
Divergence 2: Trino user convention is atlas (no auth)¶
The Trino coordinator (catalog/lakehouse.properties) has no authenticator configured; any user string is accepted. Atlas conventions + our examples use user='atlas' (the convention worker name). Our queries pass this; no auth challenge. Fine — just documenting the convention.
Divergence 3: Redpanda default only seeds atlas_stream_events topic¶
We asked for a topic-creation seam; Atlas delivered it via REDPANDA_DEMO_TOPICS (default atlas_stream_events). Our scenario topics (events, online_retail_cdc) rely on REDPANDA_DEMO_TOPICS override in .env or Redpanda auto_create_topics_enabled (which is on). We've adapted by documenting "run producer.py first to auto-create" in scenario READMEs.
3. Iceberg and Spark capabilities the scenario catalog relies on¶
These should already be satisfied by A2 (Spark 4.1.2 + Iceberg 1.11.0 runtime + IcebergSparkSessionExtensions). Listed so you can verify them when the stack comes up — each maps to a scenario. None of these need new Atlas services (they need only the delivered Spark/Iceberg):
| Capability | Needed by | Provided by | Verify |
|---|---|---|---|
MERGE INTO (upserts/CDC) |
incremental_upsert, scd2 (online_retail) |
Iceberg SparkSQL extension | MERGE INTO lakehouse.silver.x … runs |
Snapshots / VERSION AS OF / rollback |
time_travel (nyc_taxi) |
Iceberg + system.rollback_to_snapshot |
SELECT … VERSION AS OF <id>; CALL lakehouse.system.rollback_to_snapshot(...) |
| Branch/tag (WAP) | time_travel |
Iceberg 1.11 branches; spark.wap.branch |
ALTER TABLE … CREATE BRANCH; write to branch |
| Maintenance procedures | table_maintenance (nyc_taxi) |
Iceberg system.* procedures |
CALL lakehouse.system.rewrite_data_files / expire_snapshots / remove_orphan_files |
| Schema evolution (add/rename/reorder) | schema_evolution (gh_archive) |
Iceberg | ALTER TABLE … ADD/RENAME/ALTER COLUMN; read old+new snapshots |
Nested JSON → columns, explode |
json_flatten (gh_archive) |
Spark SQL | from_json/explode on GH Archive events |
| Structured Streaming (file source) | streaming_ingest-gh_archive |
Spark (no Kafka) | readStream.format("json").load("s3a://landing/…") → Iceberg + checkpoint |
| Dimensional / star schema | star_schema (tpch) |
Spark SQL | fact/dim gold marts |
| Multi-engine read | federated_query, bi_query |
A7 (Trino) | see section 2 |
Action for the worker: confirm the
system.*Iceberg procedures (rewrite_data_files,expire_snapshots,remove_orphan_files,rollback_to_snapshot) and branch/tag / WAP are usable through the seeded Zeppelin interpreter and Spark Connect. If any require theSparkSessionCatalog(session-catalog) wiring rather than theSparkCatalogyou configured, please note it — a couple of maintenance/time-travel scenarios depend on it.
4. Gotchas and Deviations Recap¶
- Namespaces are not seeded — apps/bootstrap must
CREATE NAMESPACE. (section A1) - Cluster-mode jobs don't inherit the catalog config — only
spark-connecthas it; DAGs must carryspark.sql.catalog.lakehouse.*. (section A2) - Zeppelin is standalone Spark, not Spark Connect. (section A3)
- Warehouse scheme differs —
s3://lakehouse/server-side vss3a://lakehouse/for Spark clients. Harmless with S3FileIO, but a footgun; consider documenting. - Jupyter uses the
jupyter-scoped MinIO account, which can't writelakehousevia PyIceberg. (section A4) - Jenkins ships no jobs/alias — downstream provides. (section A5)
- New services default
disabled— must pass--trino-source container/--redpanda-source containerto enable them. Same for--iceberg-rest-source/--jenkins-source. - Zeppelin Trino interpreter is
%trino, not%jdbc(trino)— Zeppelin 0.12.1 uses the interpreter name as the paragraph prefix. (section 2, Divergence 1) - Trino user is
atlas(no auth). Convention; any user accepted. (section 2, Divergence 2) - Redpanda demo-topic seed only includes
atlas_stream_events. Downstream projects overrideREDPANDA_DEMO_TOPICSin.envor rely onauto_create_topics_enabled. (section 2, Divergence 3)
5. Verification¶
data-eng-lab ships an executable form of this contract:
scripts/register_iceberg.py— createsbronze/silver/gold(must run first; you seed none).tests/infra/preflight — Layer 1 (service existence/health) + Layer 2 (integration matrix: Spark↔MinIO, Spark↔iceberg-rest, Airflow↔MinIO, etc.) viadocker execprobes.RUN_INFRA=1 uv run pytest -m infra— runs the live probes + scenario execution/parity + the Trino/streaming live tests (tests/scenarios/test_*_live.py) once A7/A9 land.go-live.md— the runbook that brings the stack up and drives all of the above.
When all A1–A9 are delivered, we flip --trino-source/--redpanda-source on and these gated tests validate the contract end-to-end. If a delivered shape differs from sections 1–3, the live tests will point at exactly which expectation broke.
A7/A9 delivery feedback: See [atlas-feedback-a7a9.md] for a detailed feedback report on the delivered Trino + Redpanda services (atlas 72e30d1), including what matched the contract, the intentional deviations we've adapted to, and optional documentation polish suggestions.
Go-live infra findings: [atlas-feedback-go-live.md] lists the Atlas-side issues surfaced during the full live go-live validation (atlas 85ff46b): Spark REST submission server, Spark Connect core monopoly, spark-connect healthcheck, and Airflow-3 connection context.
Maintained by data-eng-lab. Questions → open an issue on thekaveh/data-eng-lab or comment on atlas#268 / atlas#269.