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
Sourcepub fn new(
origin: Arc<dyn ObjectStore>,
backend: Arc<dyn RangeCacheBackend>,
block_size: u64,
ns: String,
total_fetch_permits: usize,
demand_reserved_fetch_permits: usize,
fetch_memory_budget_bytes: u64,
max_coalesced_get_bytes: u64,
promote_whole_batch: bool,
) -> 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, fetch_memory_budget_bytes: u64, max_coalesced_get_bytes: u64, promote_whole_batch: bool, ) -> RangeCache
fetch_memory_budget_bytes is the total transient origin-GET buffer
memory allowed across all in-flight coalesced runs; the slice reserved
for demand is derived, not a separate parameter – each of
demand_reserved_fetch_permits’s reserved slots reserves one full-size
run’s worth of bytes (max_run_bytes(block_size, max_coalesced_get_bytes)),
so it keeps the same “N full-size demand runs always fit” meaning the
count reservation already has.
Sourcepub fn with_prefix_labels(self, labels: Arc<[&'static str]>) -> RangeCache
pub fn with_prefix_labels(self, labels: Arc<[&'static str]>) -> RangeCache
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), resolved by longest-prefix
match against prefix_labels, or metric_tags::PREFIX_OTHER if it
matches none. See with_prefix_labels.
Sourcepub fn fetch_budget_stats(&self) -> FetchBudgetStats
pub fn fetch_budget_stats(&self) -> FetchBudgetStats
Both fetch-permit budgets’ (count and bytes) 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 outstanding_fetch_tasks(&self) -> usize
pub fn outstanding_fetch_tasks(&self) -> usize
Number of detached fetch tasks (coalesced block runs + size() HEADs)
currently in flight to origin, for the shutdown path’s
abandoned-fetch-count warning.
Sourcepub async fn wait_for_fetch_tasks_drain(&self)
pub async fn wait_for_fetch_tasks_drain(&self)
Resolves once every detached fetch task has finished. Used by graceful shutdown to wait for in-flight origin GETs instead of letting the tokio runtime drop them.
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 backend_ram_usage(&self) -> Option<usize>
pub fn backend_ram_usage(&self) -> Option<usize>
Accounted RAM-tier usage (None for a backend with no RAM tier), for
the saturation sampler’s residency gauge.
Sourcepub fn backend_ram_entries(&self) -> Option<usize>
pub fn backend_ram_entries(&self) -> Option<usize>
Accounted RAM-tier entry count (None for a backend with no RAM
tier), for the saturation sampler’s entry-count gauge.
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, Error>>
Sourcepub fn stream_ranges(
&self,
key: &str,
ranges: Vec<Range<u64>>,
caller: StreamRangesCaller,
) -> impl Future<Output = Result<impl Stream<Item = Result<Bytes, Error>> + Send + 'static, Error>>
pub fn stream_ranges( &self, key: &str, ranges: Vec<Range<u64>>, caller: StreamRangesCaller, ) -> impl Future<Output = Result<impl Stream<Item = Result<Bytes, Error>> + Send + 'static, Error>>
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, Error>> + Send + 'static, Error>>
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, Error>> + Send + 'static, Error>>
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, Error>>
Sourcepub fn get_range_with_size(
&self,
key: &str,
file_size: u64,
range: Range<u64>,
) -> impl Future<Output = Result<Bytes, Error>>
pub fn get_range_with_size( &self, key: &str, file_size: u64, range: Range<u64>, ) -> impl Future<Output = Result<Bytes, Error>>
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>, Error>>
Sourcepub fn get_ranges_with_size(
&self,
key: &str,
file_size: u64,
ranges: &[Range<u64>],
) -> impl Future<Output = Result<Vec<Bytes>, Error>>
pub fn get_ranges_with_size( &self, key: &str, file_size: u64, ranges: &[Range<u64>], ) -> impl Future<Output = Result<Vec<Bytes>, Error>>
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<(), Error>
pub async fn prefetch_ranges( &self, key: &str, ranges: &[Range<u64>], ) -> Result<(), Error>
Warm the cache for ranges at Prefetch priority without returning
any bytes – the priority-carrying core that the HTTP prefetch
endpoint and client method build on. Public (rather than
crate-private) so integration tests under tests/ — which compile as
a separate crate — can exercise the promotion behavior.
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,
§impl<T> FutureExt for T
impl<T> FutureExt for T
§fn with_context(self, otel_cx: Context) -> WithContext<Self> ⓘ
fn with_context(self, otel_cx: Context) -> WithContext<Self> ⓘ
§fn with_current_context(self) -> WithContext<Self> ⓘ
fn with_current_context(self) -> WithContext<Self> ⓘ
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> 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§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request§impl<L> LayerExt<L> for L
impl<L> LayerExt<L> for L
§fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>where
L: Layer<S>,
fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>where
L: Layer<S>,
Layered].