How it works
- Server-side loading — each server decodes the Iceberg Puffin deletion files relevant to its segments and applies them when answering a query, so deleted/updated rows are excluded from results.
- Atomic snapshot readiness — before a new Iceberg snapshot becomes visible to queries, the controller polls every server until all of them confirm they’ve loaded the deletion vectors for that snapshot. This avoids a window where some servers see the new snapshot’s deletes and others don’t.
- Broker-side pinning and consistency — the broker pins every query to a single snapshot per table so all servers involved in a query agree on which snapshot (and therefore which deletes) to use. By default it pins the newest active snapshot automatically and injects the
snapshotVersionByTableoption for the servers; a query can also pin an explicit snapshot (see Query-time snapshot pinning). An invalid explicitly-pinned snapshot fails the query rather than silently mixing snapshot state.
Enabling deletion vectors
Set on the table’sExternalTableSyncTask config:
Changes to
iceberg.dv.* keys (and to the table’s catalog or storage credentials) take effect on the servers without a restart — the deletion-vector readers are rebuilt when the table config changes.
The number of snapshots kept queryable (and therefore pinnable — see Query-time snapshot pinning) is bounded by snapshot retention, iceberg.snapshotProcessing.retention.maxActiveVersions (default 5).
Controller: snapshot readiness
Each server exposes a readiness endpoint, which the controller polls:
{"ready": true|false} plus additive load-progress fields the controller’s poller uses for stall detection — loaded, total, inFlight, consecutiveLoadFailures, and lastLoadError (present only when set). Older pollers ignore the extra fields; a server that omits them (older build) is treated as deadline-only, never assumed to be stalled. The check is non-blocking: it answers from the server’s in-memory state and, if the deletion vectors aren’t loaded yet, kicks off a background load and returns ready: false — so the first poll for a new snapshot normally reports not-ready. Returns 412 when the table is not configured for deletion vectors (enableDeletionVectors=false) or the deletion-vector index for the requested snapshot is missing or unreadable.
Controller: IS/EV convergence
Before polling for snapshot readiness, the controller waits for every affected segment’s Helix ideal-state and external-view to converge:Broker: pruning and pinning
Broker-side pruning also requires
pinot.broker.data.dir to point at the cluster’s deep-store root (StarTree Cloud sets this); without it the broker cannot read snapshot state and affected queries fail.
Broker-side snapshot pruning activates for OFFLINE External Tables when any of these is true on the table: enableDeletionVectors=true, enableSnapshotConsistency=true (a flag the onboarding/sync flow sets automatically for snapshotting catalogs — pins queries to the last completed snapshot so they never see a half-ingested one), or segment groups enabled. It is a zero-cost no-op otherwise.
Query-time snapshot pinning
You do not need to pin a snapshot yourself: for a pruning-enabled table the broker automatically resolves each query to the newest active snapshot and injects the server-requiredsnapshotVersionByTable option. Set the option explicitly only to pin a query to a specific snapshot (time travel / reproducing an earlier state):
QUERY_EXECUTION error rather than returning results computed from a mix of snapshot states. A query also fails when the table has no active snapshots at all yet (nothing fully synced), or when the broker hasn’t finished loading the pinned snapshot’s segment state locally (retried in the background; see pinot.broker.iceberg.dv.cacheRetryIntervalMs).
Only snapshots in the active set can be pinned, and the set’s size is bounded by snapshot retention (iceberg.snapshotProcessing.retention.maxActiveVersions, default 5) — so time travel reaches back at most that many snapshots.
Storage format
The deletion-vector index itself is stored as a dictionary-encoded Parquet file (dv-index.parquet), with rows sorted by segment name within each row group and per-row-group bloom filters, which keeps heap usage low even for large delete sets and supports lazy point lookups. On the servers, Puffin files fetched in cache mode live under the instance data directory (dv-puffin-cache/); the cache survives restarts and is cleaned up when the table is deleted.
Observability
The run-status endpoint’s
failurePhase field (see Observability) can also report IS_EV_CONVERGENCE or SNAPSHOT_READINESS_POLL when a sync run fails during deletion-vector convergence.
FAQs
Do I need to set snapshotVersionByTable on every query once deletion vectors are enabled?
No — the broker pins each query to the newest active snapshot automatically and injects the option for the servers. Set it explicitly only for time travel to a specific snapshot; an explicit pin that isn’t in the active set fails the query rather than returning potentially inconsistent results.
Does this work for raw S3 Parquet (non-Iceberg) External Tables?
No — deletion vectors are an Iceberg-specific capability (deletionVectorSource=iceberg), tied to Iceberg’s Puffin delete-file format.
