pub struct RangeCache { /* private fields */ }Expand description
Range-aware read cache over an origin object store.
§Cache invalidation
This cache assumes object keys are write-once and content-addressed: a given key always maps to the same bytes for the lifetime of the object. The size and block caches therefore carry no TTL, etag, or generation in their keys and are never invalidated. Overwriting an existing key with different content would cause stale size/block data to be served indefinitely. This is safe for micromegas lake objects (blocks, parquet) which are never overwritten; do not point this cache at a mutable namespace.
§In-flight map and priority
Concurrent fetches of the same block or size are collapsed via an
in-flight map (FetchScheduler): the first caller to ask for a key
becomes its owner and issues the origin request (spawned as a detached
task, so a cancelled caller never strands the others waiting on it);
every other concurrent caller joins and observes the same result.
Contiguous missing blocks the owner controls are coalesced into one
origin.get_range per run. Every origin GET is either Demand or
Prefetch priority; a demand caller joining a prefetch-priority fetch
promotes it (see own_or_join), so a late demand read is never stuck
behind unrelated prefetch traffic.
Implementations§
Source§impl RangeCache
impl RangeCache
pub fn new( origin: Arc<dyn ObjectStore>, backend: Arc<dyn RangeCacheBackend>, block_size: u64, ns: String, total_fetch_permits: usize, demand_reserved_fetch_permits: usize, max_coalesced_get_bytes: u64, promote_whole_batch: bool, ) -> Self
Sourcepub fn with_prefix_labels(self, labels: Arc<[&'static str]>) -> Self
pub fn with_prefix_labels(self, labels: Arc<[&'static str]>) -> Self
Attach a prefix classifier for the dimensioned hit-rate/request
metrics: labels are the server’s configured allowed_prefixes,
leaked to 'static once at startup by the caller (bounded,
low-cardinality, set once). Every request key is then
longest-prefix-matched against labels (see classify); a key
matching none classifies as metric_tags::PREFIX_OTHER.
RangeCache::new itself leaves this empty (every key classifies as
"other"), so its existing callers/tests compile unmodified; only
object_cache_srv.rs opts in.
Sourcepub fn classify(&self, key: &str) -> &'static str
pub fn classify(&self, key: &str) -> &'static str
The prefix label key falls under (e.g. "blobs", "views", per
the server’s configured allowed_prefixes), or
metric_tags::PREFIX_OTHER if it matches none. See
with_prefix_labels.
Sourcepub fn fetch_budget_stats(&self) -> (usize, usize, usize, usize)
pub fn fetch_budget_stats(&self) -> (usize, usize, usize, usize)
(shared_available, shared_total, prefetch_available, prefetch_total)
– the fetch-permit budget’s current occupancy, for the saturation
sampler.
Sourcepub fn inflight_len(&self) -> usize
pub fn inflight_len(&self) -> usize
Number of keys (blocks or size() heads) currently in flight to
origin, for the saturation sampler.
Sourcepub fn backend_disk_stats(&self) -> Option<BackendDiskStats>
pub fn backend_disk_stats(&self) -> Option<BackendDiskStats>
Backend disk write-path counters (None for a backend with no disk
tier), for the saturation sampler’s per-second foyer disk gauges.
Sourcepub fn block_size(&self) -> u64
pub fn block_size(&self) -> u64
Size in bytes of one cache block. Every distinct block a request touches is fetched and held whole, so callers gating memory (e.g. the server’s cross-request budget) need this to account for amplification from small scattered ranges.
pub fn size(&self, key: &str) -> impl Future<Output = Result<u64>>
Sourcepub fn stream_ranges(
&self,
key: &str,
ranges: Vec<Range<u64>>,
caller: StreamRangesCaller,
) -> impl Future<Output = Result<impl Stream<Item = Result<Bytes>> + Send + 'static>>
pub fn stream_ranges( &self, key: &str, ranges: Vec<Range<u64>>, caller: StreamRangesCaller, ) -> impl Future<Output = Result<impl Stream<Item = Result<Bytes>> + Send + 'static>>
Stream the bytes for ranges (each half-open [start, end)) without
materializing more than a couple of DEMAND_WINDOW_BLOCKS windows of
blocks at a time, regardless of how large the ranges are in total.
Upfront validation — the size() lookup and every range’s
out-of-bounds check — runs before the stream is constructed and
returned, so 404/416-shaped errors surface synchronously to the
caller with proper status codes; only a failure after that point
(a mid-stream fetch_blocks error, e.g. the origin going down) is
yielded as the stream’s terminal Err item. A degenerate
start >= end range is validated like any other (its end is still
checked against file_size) but yields no bytes.
Yields a flat, ordered sequence of chunks: ranges are processed in
the order given and each range’s bytes are emitted contiguously
(possibly split into several Bytes chunks at window boundaries)
before the next range’s bytes begin. There is no cross-range block
dedup — a block shared by two ranges is fetched once per range it
appears in, though a repeat is always a backend hit or an in-flight
join, never a second origin GET (see own_or_join).
caller selects which of the two distinct error counters
(range_cache_get_range_error / range_cache_get_ranges_error) this
call emits on validation failure or a mid-stream fetch error, so
get_range/get_ranges (and the two HTTP handlers, which call this
directly) keep emitting the metric they always have.
Sourcepub fn stream_ranges_with_size(
&self,
key: &str,
ranges: Vec<Range<u64>>,
file_size: u64,
caller: StreamRangesCaller,
) -> impl Future<Output = Result<impl Stream<Item = Result<Bytes>> + Send + 'static>>
pub fn stream_ranges_with_size( &self, key: &str, ranges: Vec<Range<u64>>, file_size: u64, caller: StreamRangesCaller, ) -> impl Future<Output = Result<impl Stream<Item = Result<Bytes>> + Send + 'static>>
Like stream_ranges, but for a caller that already resolved
file_size itself (e.g. get_range_handler, which needs it up front
for range validation): skips the redundant self.size() call, so a
cache hit doesn’t fire range_cache_size_backend_hit a second time
per call.
pub fn get_range( &self, key: &str, range: Range<u64>, ) -> impl Future<Output = Result<Bytes>>
Sourcepub fn get_range_with_size(
&self,
key: &str,
file_size: u64,
range: Range<u64>,
) -> impl Future<Output = Result<Bytes>>
pub fn get_range_with_size( &self, key: &str, file_size: u64, range: Range<u64>, ) -> impl Future<Output = Result<Bytes>>
Like get_range, but for a caller that already resolved file_size
itself, skipping the redundant self.size() call inside
stream_ranges (see stream_ranges_with_size).
pub fn get_ranges( &self, key: &str, ranges: &[Range<u64>], ) -> impl Future<Output = Result<Vec<Bytes>>>
Sourcepub fn get_ranges_with_size(
&self,
key: &str,
file_size: u64,
ranges: &[Range<u64>],
) -> impl Future<Output = Result<Vec<Bytes>>>
pub fn get_ranges_with_size( &self, key: &str, file_size: u64, ranges: &[Range<u64>], ) -> impl Future<Output = Result<Vec<Bytes>>>
Like get_ranges, but for a caller that already resolved file_size
itself (see stream_ranges_with_size).
Sourcepub async fn prefetch_ranges(
&self,
key: &str,
ranges: &[Range<u64>],
) -> Result<()>
pub async fn prefetch_ranges( &self, key: &str, ranges: &[Range<u64>], ) -> Result<()>
Warm the cache for ranges at Prefetch priority without returning
any bytes. The HTTP surface for this (endpoint + client method) is
#1198; this is the priority-carrying core it builds on. Public (rather
than crate-private) so integration tests under tests/ — which
compile as a separate crate — can exercise the promotion behavior
described in the fetch-rework plan.
Trait Implementations§
Source§impl Clone for RangeCache
impl Clone for RangeCache
Source§fn clone(&self) -> RangeCache
fn clone(&self) -> RangeCache
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreAuto Trait Implementations§
impl !RefUnwindSafe for RangeCache
impl !UnwindSafe for RangeCache
impl Freeze for RangeCache
impl Send for RangeCache
impl Sync for RangeCache
impl Unpin for RangeCache
impl UnsafeUnpin for RangeCache
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more