Skip to content
PHP2Shell: The PHP Bug
Akshay Thakare

Incremental Loads and Watermarks

Load only what changed since the last run: high-water marks, choosing the right column, overlap windows and the rows a naive watermark misses.

ETL / ELTIntermediate2 min readUpdated 11 Oct 2026

Full vs incremental

Full loadIncremental load
ReadsEvery row, every runOnly new or changed rows
Simple?YesNeeds state
Scales?PoorlyWell

The high-water mark

SQL.sql
-- stored after each successful run: last_watermark = '2026-01-05 00:00:00'
SELECT * FROM orders
WHERE updated_at >= :last_watermark AND updated_at < :run_upper_bound;

Then advance the watermark in the same transaction as the load, so a failed run never moves it.

Choosing the column

ColumnCatchesMisses
Auto-increment idInsertsUpdates
updated_atInserts and updatesHard deletes; rows whose timestamp is set late
Ingestion / arrival timeEverything that reached the sourceNothing, if the source sets it on write
CDC log positionInserts, updates, deletes—

Traps

  • Late timestamps: a transaction commits at 10:05 with updated_at = 09:58, after you read up to 10:00. Use a fixed upper bound slightly in the past, or overlap windows and dedupe.
  • Equal timestamps at the boundary: use >= with deduplication, or a strictly increasing column.
  • Clock differences between servers: prefer a database-generated timestamp.

Finished this note?

Share