Skip to content

Pre-touch the file tail on a footer cache hit - #141

Open
Vedin wants to merge 2 commits into
rustice/inexact-string-boundsfrom
rustice/footer-hit-pre-touch
Open

Vedin wants to merge 2 commits into
rustice/inexact-string-boundsfrom
rustice/footer-hit-pre-touch

Conversation

@Vedin

@Vedin Vedin commented Oct 28, 2026

Copy link
Copy Markdown

Why

On S3, a data-range GET of an object that was requested less than about a second earlier gets its response headers roughly twice as fast as one to an object left untouched for ten seconds or more. Measured on the deployed SPCS service (TPC-H SF100, one run, one image):

  • with the footer cache off, every data GET follows a footer GET of the same file, and the data GET header p50 is 28–42 ms (Q4, Q6, Q12, Q14) against 85–87 ms with the cache on;
  • with the cache on, data GETs to objects touched < 100 ms earlier have p50 42–46 ms, against 79–89 ms when the last touch was 1–300 s earlier.

A footer cache hit removes exactly the footer read that made the data read fast, so warm scans pay the slow first touch on every data file.

What

With ICEBERG_PARQUET_PRE_TOUCH_MAX_IN_FLIGHT above 0 (read once per process; default 0, off), a footer cache hit issues one 8-byte ranged GET of the file's tail. Under file_stream_open_ahead the hit happens while the previous file is still being read, so the touch lands before that file's data reads start.

  • The touch never delays the hit: it is a SpawnedTask owned by the reader (aborted when the reader drops; no detached task).
  • It takes a slot of a process-wide semaphore without waiting and is skipped when every slot is taken.
  • Its outcome is ignored: the data reads that follow report any real error.
  • New scan metrics parquet_pre_touches and parquet_pre_touch_skipped; the cache init log gains pre_touch_max_in_flight.
  • Cost: at most one extra GET per file whose footer was served from cache.

MetadataCache::load becomes load_reporting_hit, which also returns whether the footer came from the cache.

Tests

cargo test -p datafusion_iceberg --lib parquet_metadata_cache: 27 passed (3 ignored benches), including four new tests:

  • the env value parses off by default and is capped;
  • a hit issues exactly one GET, of size-8..size, and counts it;
  • with no free slot the touch is skipped and counted;
  • dropping the reader aborts a slow touch and frees its slot.

cargo clippy -p datafusion_iceberg --all-targets: no warnings in the changed file.

Stacked on #140. The engine measurement on SPCS follows with the knob on in a separate arm.

🤖 Generated with Claude Code

Vedin and others added 2 commits October 29, 2026 00:17
On S3, a data GET to an object requested less than about a second
earlier returns its headers roughly twice as fast as one to an object
left untouched for ten seconds or more. On the deployed service the
data GET header p50 is about 30-45 ms right after a footer read of the
same file and 85-90 ms otherwise. A footer cache hit removes exactly
that footer read, so warm scans pay the slow first touch on every file.

With ICEBERG_PARQUET_PRE_TOUCH_MAX_IN_FLIGHT above 0 (default 0, off),
a cache hit issues one 8-byte ranged GET of the file tail. Under
file_stream_open_ahead the hit happens while the previous file is still
being read, so the touch lands before that file's data reads. The touch
never delays the hit: it is a task owned by the reader (aborted on
drop), takes a slot of a process-wide semaphore without waiting, and is
skipped when no slot is free. parquet_pre_touches and
parquet_pre_touch_skipped count both cases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants