Auto Loader Backlog, Schema Drift, and a Duplicate-Producing Checkpoint Reset
A pipeline ingests roughly 120,000 small JSON files per day (about 400 GB) from an ADLS Gen2 landing zone into a bronze Delta table. Auto Loader runs as a triggered stream every 15 minutes; each run takes 11-14 minutes, most of it spent listing the landing prefix, and the backlog grows during peak hours. About 0.3% of files contain a promo_code field that appears only after a marketing release date, and several runs failed on those files with a schema mismatch. To pick up the new column, an engineer restarted the stream with a fresh checkpoint and a fresh schema location. Three days of files were reprocessed and the silver tables now hold duplicate events. Storage cost is not a concern; compute cost and data freshness are.
1. What configuration changes address the listing latency and the schema failures, and why was the checkpoint reset so damaging?
Read the worked answer
Listing latency. The 11-14 minute runs are dominated by directory listing of a prefix with hundreds of thousands of objects. Switch to file notification mode (cloudFiles.useNotifications = true), which registers file events with a cloud queue and removes per-run listing entirely. It requires a one-time permission grant so Auto Loader can create the notification resources, and it must be enabled with the stream stopped and the same checkpoint retained. Directory listing remains the right choice only when the file volume is low or permissions cannot be granted.
Schema drift. cloudFiles.schemaEvolutionMode = addNewColumns (the default) lets new fields like promo_code land as nullable columns on the next inference, and unparseable records land in _rescued_data rather than failing the run. Adding cloudFiles.schemaHints for fields that would otherwise be inferred as strings avoids later type churn.
The reset. The checkpoint is what records which files Auto Loader has already consumed. Deleting it discards that state, so every file still sitting in the landing zone is re-read. The schema location can be re-inferred far more cheaply than the checkpoint can be rebuilt, so a schema change should never be the reason to throw the checkpoint away.
2. How should the duplicates be removed, and what would prevent a recurrence?
Read the worked answer
Treat bronze as append-only raw history and deduplicate on the way to silver, not by deleting bronze rows.
- Identify the key. If events carry a stable event id, dedupe with
row_number()over a window partitioned by event id ordered by ingestion time, or a streamingdropDuplicatesWithinWatermarkif an event-time watermark exists. If they do not,_metadata.file_pathplus an offset distinguishes physically distinct rows, but it will not catch the same logical event arriving in two different files. - Apply it as a
MERGEinto silver so the operation is idempotent and re-running it changes nothing. - Prevent recurrence structurally: keep the checkpoint directory stable and out of reach of ad-hoc cleanups; use
cloudFiles.cleanSource(move or delete after processing) so consumed files are not available to be re-read; and make every downstream write aMERGErather than an append, so a reprocessing event is absorbed instead of duplicated.
A one-off repartition of the landing zone during the fix also works, since anything removed cannot be re-consumed.
3. Bronze is now accumulating thousands of small files per hour. How do you fix that without slowing ingestion further?
Read the worked answer
Use write-time compaction on the bronze table rather than a separate scheduled job that competes with the stream:
delta.optimizeWrite = truecoalesces the shuffle output of each micro-batch, reducing the number of files written per trigger.delta.autoCompact = trueruns a small compaction at the end of a write once the table crosses the threshold, merging small files up to the target size.cloudFiles.maxFilesPerTriggercaps how much each trigger consumes, which keeps trigger latency predictable and stops a backlog from producing oversized bursts.
These are table properties, so they survive restarts and apply to every writer, which matters if more than one stream feeds bronze. A periodic OPTIMIZE is still useful for older data that predates the settings, and predictive optimization can handle it on managed tables, but compaction should not be the primary mechanism during active ingestion.
Note the trade-off: optimizeWrite adds a shuffle step, so validate trigger duration after enabling it on the busiest stream.