#10628·qdrant

fix(uio): async disk-cache open panics on Populate::Partial for zero-length objects and empty ranges

Author: Harsh23KashyapCreated Sep 13, 2026Updated Sep 13, 2026

Summary

Follow-up to #10625, same class one arm over: the PreferBackground/Blocking arm of the disk cache's open_async was fixed to skip fetching zero-length objects, but the Populate::Partial arm right below still does

rust
let (block_range, fetch_range) =
    block_aligned_fetch(byte_range, file_len).expect("range should not be empty");

(lib/common/common/src/universal_io/simple_disk_cache/async_io.rs:73)

block_aligned_fetch returns None - and the async open therefore panics - in three cases the sync open handles gracefully (fs.rs:219-255):

  1. Zero-length object + Partial(0..HEADER): file_len = 0 makes the block-aligned range empty. This is the same premise as #10625 ("a single empty object anywhere under a segment prefix"), and Partial is how the cold/default placement prefetches headers: id tracker (disk_id_tracker/reader/lifecycle.rs:29), compact flags (read_only_compact_flags.rs:68), graph links, prefix index, etc. preopen/schedule_open route through parked open_async futures (cached_fs/mod.rs:286), so a zero-length flags or id-tracker object panics the segment open instead of failing cleanly.
  2. Empty Partial range (e.g. or_partial(10..10)): sync treats this as a legal lazy open - there is an explicit arm for it (fs.rs:219) and a test (partial_populate_empty_range_is_lazy). Async panics.
  3. Partial range entirely past EOF: same - sync test partial_populate_range_past_eof_is_lazy documents lazy open; async panics.

A panic in a segment-open task is also worse than the clean error the sync path produces: depending on the caller it can abort the whole open instead of degrading to "segment skipped".

Repro

Standalone harness with a path dependency on common, using a mock async-only remote (pattern copied from simple_disk_cache/tests.rs). Ran twice, identical output:

zero-length object, Partial(0..64):
  sync  open: Ok
  async open: PANIC(range should not be empty)
non-empty object, empty Partial(0..0):
  sync  open: Ok
  async open: PANIC(range should not be empty)
non-empty object, Partial past EOF:
  sync  open: Ok
  async open: PANIC(range should not be empty)

Proposed fix

Mirror the sync path: create the local mirror first and skip the fetch when block_aligned_fetch returns None, instead of panicking. PR with regression tests follows.