learn-data-engineering-with-phoebe / Builder session 3 of 10
Learn Data Engineering with Phoebe · Builder track · Session 3 of 10

Ingestion patterns

Once you can read a source, the next question is how much to pull and how often. This session walks the ingestion continuum - full reload versus incremental - the watermark pattern that powers "only new rows", ETL versus ELT, and idempotent batch windows. You will run an incremental extract live against Daybreak and watch the row counts shrink.

🟠 Builder track Practitioners: DE · analysts · DS · PMs Runs in your browser · DuckDB 45 min
0-3 · Recap 3-20 · Full vs incremental & watermarks 20-42 · Build-along: incremental extract 42-45 · Q&A
Part 0

Where we are in the pipeline

In b2 you connected to Daybreak's sources and landed a copy of each. But a real pipeline runs every day, not once - and re-copying the entire source every single day is slow and expensive. This session is about the smart part of ingestion: pulling only what changed since last time, reliably, without double-loading. Still the same lifecycle seam - generation into ingestion - now done efficiently.

Live - presented in session Self-study - read after class ▶ Live pipeline - editable & runnable Official sources covered
★ What you walk out with today A clear feel for the full-reload versus incremental trade-off, the watermark pattern written in real SQL, why modern stacks default to ELT, and a live incremental extract you can point at any date - proving that "only new rows" is just a WHERE clause plus a saved high-water mark.
Part 1 · covers DLAI Source Systems M2

Ingestion on a continuum 6 min live

Ingestion is not one choice, it is a slider. At one end, full reload: copy the entire source every run - dead simple, always correct, but expensive and slow as data grows. At the other, incremental: pull only rows that are new or changed - cheap and fast, but you have to track what you have already seen. Most pipelines sit somewhere on this line, chosen table by table.

Full reload copies everything, every run simple + correct · slow + costly Incremental only new / changed rows (dark = new) cheap + fast · needs a watermark The slider: small dimension table? Full reload is fine. Huge, ever-growing fact table? Incremental, or you pay for the whole history every night.
🔍 Click to zoom - full reload copies all; incremental copies only what changed, at the cost of tracking
LiveETL vs ELT - where the transform runs3 min

The other big ingestion decision is when you transform. The letters are the order of operations:

  • ETL (extract, transform, load): reshape the data before it lands in the destination. The old default, from when storage was expensive and you cleaned data to save space.
  • ELT (extract, load, transform): land the raw data first, transform it later, inside the destination. The modern default - cloud storage is cheap, so you keep the raw copy and reshape on demand.

This whole track is ELT-shaped: b2-b4 land raw data, b5 and b8 transform it after it lands. Keeping the raw copy means you can re-transform any time without re-extracting from the source.

✗ ETL - transform first reshape before it lands assumes storage is expensive the old default ✓ ELT - load raw first land raw, transform later storage is cheap now this whole track is ELT-shaped b2 to b4 land raw data; b5 and b8 transform it after it lands - re-transform anytime, no re-extract.
🔍 Click to zoom - land raw first, transform later, re-transform anytime
Real world

Why modern stacks went ELT. When a warehouse charged by the gigabyte, teams transformed first to store less (ETL). Once cloud storage dropped to cents, the calculus flipped: land everything raw, transform later, and you get replay, auditability, and the freedom to fix a transform bug without touching the source. That flip is why "load then transform" is now the default.

Self-studyThe cost of full reload as data grows2 min read

Full reload feels harmless when a table has a thousand rows. The problem is that its cost grows with the total size of the source, not the amount of new data. A table that gains 1% new rows a day still gets copied 100% every night under full reload. After a year that is 365 full copies of an ever-larger table to capture a trickle of change. Incremental flips the cost to scale with change, not history - which is why every high-volume fact table eventually moves to it.

Part 2 · the watermark pattern

The watermark pattern 6 min live

Incremental ingestion needs one piece of memory: the high-water mark - the largest timestamp or id you pulled last run. Next run, you pull only rows past it. That is the whole trick. The watermark is a single saved value, and the extract is just WHERE column > watermark.

LiveSimulate an incremental pull3 min

Pretend your last run stopped at a watermark date. This run pulls only orders after it. Change the date and re-run to see the "new rows" count move. Press ▶ Run (first run caches the ~8 MB engine).

-- watermark = the max order_date we saw last run
SELECT count(*) AS new_rows_since_watermark
FROM orders
WHERE order_date > '2026-04-01';
Self-studyLate-arriving data & the safety window3 min read

A naive watermark assumes rows always arrive in order. Reality disagrees. A mobile app buffers events offline and syncs a two-day-old order tomorrow; a batch job backfills yesterday's records at noon. If your watermark jumps straight to "now", those late rows arrive behind the mark and never get pulled - silent data loss.

The safety window Do not set the watermark to the newest row you saw. Set it a little behind - say, back up 1-3 days - and accept re-reading a small overlap each run. You trade a few duplicate reads (handled by idempotency, Part 3) for never missing a late row. Overlap is cheap; missing data is not.
Part 3 · batch windows & idempotency

Batch windows and idempotency 5 min live

Incremental runs on a schedule, in windows - one file per day, per hour, or per month. The rule that keeps windows safe is idempotency: re-running the same window must not double-load. The clean pattern is one file per window, named by its window, so a re-run overwrites rather than appends.

LiveOne dated file per window3 min

Extract one month of orders to a file named for that window. Re-running writes the same file, so the window is idempotent - run it once or ten times, the landed result is identical.

-- the March 2026 window, landed as its own dated file
COPY (SELECT * FROM orders
      WHERE order_date >= '2026-03-01'
        AND order_date < '2026-04-01')
TO 'windows/orders_2026-03.parquet' (FORMAT PARQUET);

SELECT count(*) AS rows_in_march_window
FROM 'windows/orders_2026-03.parquet';
Demo 1 of 2

Incremental extract, live ★ 6 min · everyone builds

Watch full versus incremental side by side. First count the whole source, then land only the rows past a watermark, then count the incremental file. The gap between the two counts is exactly the bytes you did not move - the whole point of incremental ingestion.

Full size: count every row in the source - what a full reload would copy.

Incremental extract: land only orders after the watermark to a Parquet file.

Compare: one result shows full count next to incremental count. The difference is the saving.

-- extract only rows past the watermark
COPY (SELECT * FROM orders WHERE order_date > '2026-04-01')
TO 'inc/orders_new.parquet' (FORMAT PARQUET);

-- full reload size vs incremental size, side by side
SELECT
  (SELECT count(*) FROM orders)                    AS full_reload_rows,
  (SELECT count(*) FROM 'inc/orders_new.parquet')  AS incremental_rows;
Real world

This gap is money at scale. On a fact table with a billion rows gaining a million a day, full reload moves a billion rows every night; incremental moves a million. Same freshness, one-thousandth of the transfer, compute, and cloud bill. The two-line difference you just ran is the single highest-leverage decision in most ingestion pipelines.

Same freshness, very different cost Full reload, nightly 1,000,000,000 rows Incremental, nightly 1,000,000 rows Same freshness, one-thousandth of the transfer, compute and cloud bill versus full reload.
🔍 Click to zoom - same freshness, one-thousandth of the rows moved
Demo 2 of 2

Your turn: tune the ingestion ★ 8 min · build your own

Each editor starts fresh from Daybreak's raw source. Change the watermark, size a batch window, then reason about the trade-off. All three run against the source tables.

LiveQ1 · Write the incremental filter for a new watermark3 min

Pick a different watermark date and count only the orders after it. Try a few dates and watch the "new rows" number respond.

SELECT count(*) AS new_since_watermark
FROM orders
WHERE order_date > '2026-05-01';
LiveQ2 · Daily vs monthly batch window4 min

How many orders would a single daily window carry, versus a full month? One query shows both, so you can feel the window-size trade-off.

SELECT
  count(*) FILTER (WHERE order_date = '2026-03-15')            AS one_day,
  count(*) FILTER (WHERE order_date >= '2026-03-01'
                     AND order_date < '2026-04-01')            AS one_month
FROM orders;
Self-studyQ3 · Full reload or incremental?3 min

A thought exercise, no SQL. Given a table, how do you decide? The rule of thumb:

  • Small dimension table (customers, products): full reload. A few hundred or thousand rows are cheap to re-copy, and you dodge all the watermark bookkeeping. Simpler is better when the table is small.
  • Huge, append-heavy fact table (orders, events, clicks): incremental. The table only grows, so full reload's cost climbs forever while incremental stays flat at "today's new rows".
  • The tie-breaker: if the source has a reliable, indexed timestamp or id to watermark on, incremental is easy. If it does not, full reload may be the only correct option until the source adds one.
Homework

Try it yourself - this week ◐ 20-30 min total

Source material

Official sources covered

This session teaches the working core of the DeepLearning.AI Data Engineering Professional Certificate (Joe Reis) and Reis & Housley's Fundamentals of Data Engineering, run on a live engine instead of slides. This page covers:

DLAI Source Systems, Data Ingestion & Pipelines - M2: IngestionParts 1-3 · the continuum, ETL vs ELT, watermarks, batch windows
Fundamentals of Data Engineering (Reis & Housley) - ingestion chapterParts 1-2 · full vs incremental, the ELT shift, idempotency
DLAI Source Systems - streaming ingestionPart 1 · touched; continuous/CDC ingestion is session b6
Check yourself

Three questions before you go 🎯 ◐ 90 seconds

1 · What is the difference between full reload and incremental ingestion?

Full reload re-copies everything each run - simple but costly as data grows. Incremental pulls only what changed since last run, so its cost scales with change, not history.

2 · What does a watermark do in incremental ingestion?

The watermark is a single saved high-water value. The next extract is just WHERE column > watermark - that one memory turns a full scan into "only new rows".

3 · Why do modern data stacks default to ELT over ETL?

When storage got cheap, the calculus flipped: land raw (load), transform later. You keep the raw copy for replay, auditing, and fixing transforms without touching the source.

Builder session 3 cheat sheet · pin this

The continuumFull reload (copy all) ↔ incremental (copy only new/changed). Chosen table by table.
Full reloadSimple and always correct, but cost scales with total size. Good for small dimension tables.
IncrementalCheap and fast, cost scales with change. Good for huge, append-heavy fact tables. Needs a watermark.
WatermarkThe max timestamp/id from last run. Next run pulls WHERE column > watermark. One saved value.
Safety windowSet the watermark a few days behind, not at 'now', so late-arriving rows are not missed.
ETL vs ELTETL transforms before load; ELT lands raw then transforms later. Modern default is ELT.
IdempotencyRe-running a window must not double-load. One dated file per window - a re-run overwrites.
Running projectIngesting Daybreak incrementally. Next: b4, file and table formats - CSV vs JSON vs Parquet.