pub struct DiskCache {
pub(super) cache_dir: PathBuf,
pub(super) write_gc_lock: Arc<RwLock<()>>,
pub(crate) index_walk: Arc<Semaphore>,
}Expand description
Content-addressable disk cache.
Fields§
§cache_dir: PathBuf§write_gc_lock: Arc<RwLock<()>>§index_walk: Arc<Semaphore>One remove_by_key_prefix at a time, process-wide.
The removal walks the whole index twice, and the second walk holds
write_gc_lock.write_owned() for its duration. tokio’s RwLock is
write-preferring, so overlapping removals starve every cache write in
the process, and a walk costs exactly the same for a key nothing was
ever stored under. Lives on the cache, not on Admission, so every
caller gets the bound without learning it exists, and the permit is
carried INTO both blocking closures the same way the GC guard is: a
permit held by the async fn would be released when a client drops the
request mid-walk, while the walk itself runs on.
Implementations§
Source§impl DiskCache
impl DiskCache
Sourcepub fn generate_key(data: &[u8]) -> String
pub fn generate_key(data: &[u8]) -> String
Generate a cache key from file content (SHA256 hash).
Sourcepub async fn get<T: DeserializeOwned>(
&self,
key: &str,
) -> Result<Option<T>, ApiError>
pub async fn get<T: DeserializeOwned>( &self, key: &str, ) -> Result<Option<T>, ApiError>
Get a cached value by key.
Sourcepub async fn set<T: Serialize>(
&self,
key: &str,
value: &T,
) -> Result<(), ApiError>
pub async fn set<T: Serialize>( &self, key: &str, value: &T, ) -> Result<(), ApiError>
Set a cached value.
Serializes and then delegates to Self::set_bytes, so there is
exactly one body that takes the write_gc_lock read guard around a
cacache::write – the invariant cannot drift between the two entry
points.
Sourcepub async fn has(&self, key: &str) -> Result<bool, ApiError>
pub async fn has(&self, key: &str) -> Result<bool, ApiError>
Whether the index holds an entry for key. An index lookup only: the
content is not read, so this is the cheap pre-filter, never the
decision a reader acts on.
Ok(false) is “absent”; a lookup that FAILS is an error, not an
absence, so a caller answering 404 on false still answers a broken
cache store with 500 CACHE_ERROR (#5750 review). cacache::metadata
answers Ok(None) for a key it has no entry for.
Sourcepub async fn get_bytes(&self, key: &str) -> Result<Option<Vec<u8>>, ApiError>
pub async fn get_bytes(&self, key: &str) -> Result<Option<Vec<u8>>, ApiError>
Get raw bytes from cache (for Parquet responses).
Sourcepub async fn set_bytes(&self, key: &str, data: &[u8]) -> Result<(), ApiError>
pub async fn set_bytes(&self, key: &str, data: &[u8]) -> Result<(), ApiError>
Set raw bytes in cache.
Sourcepub async fn stats(&self) -> Result<CacheStats, ApiError>
pub async fn stats(&self) -> Result<CacheStats, ApiError>
Cache-wide entry count and total content size in bytes, for the
/api/v1/metrics gauges (issue #3636). Sums Metadata::size across
every index entry; entries sharing a deduplicated content blob are
each counted once (as stored), matching what remove_by_key_prefix
treats as “still referenced”.
Source§impl DiskCache
impl DiskCache
Sourcepub async fn remove_by_key_prefix(
&self,
key_prefix: &str,
) -> Result<usize, ApiError>
pub async fn remove_by_key_prefix( &self, key_prefix: &str, ) -> Result<usize, ApiError>
Remove every index entry whose key is key_prefix itself or starts with
"{key_prefix}-" (issue #3636). One source file fans out into several
entries under the same hash prefix – the plain request key, the
-json-v2, -parquet-vN, -parquet-metadata-v4 and -symbolic-v1
variants, each combination of opening-filter and quality suffix – and
DELETE /api/v1/cache/{key} (handed the file hash its cache_key
resolves to) is meant to drop all of them for that
file in one call.
The underlying store is content-addressable: two different source
files can produce byte-identical output (the issue’s example is two
different IFCs that both emit an empty symbolic-data payload) and then
share one content blob across two index entries. So this removes
INDEX ENTRIES first – an index-only removal (cacache::remove) is
exactly the operation the issue’s own hand-pruning experiment found
safe: leaving a blob temporarily orphaned makes it unreachable but
never makes a surviving entry read as corrupt, whereas removing a
blob a live index entry still points at does (their write-up
reproduced the resulting 500s). Only once every matching index entry
is gone does this walk the remaining index and drop content blobs
that nothing references any more; a blob still referenced by an
unrelated entry is left alone.
Returns the number of index entries removed. Zero is a normal,
successful result for a prefix nothing was ever cached under, or that
already had its entries removed – deleting an absent entry is a
no-op, not an error, so a retried or duplicate DELETE stays safe.
Trait Implementations§
Auto Trait Implementations§
impl Freeze for DiskCache
impl !RefUnwindSafe for DiskCache
impl Send for DiskCache
impl Sync for DiskCache
impl Unpin for DiskCache
impl !UnwindSafe for DiskCache
Blanket Implementations§
§impl<U> As for U
impl<U> As for U
§fn as_<T>(self) -> Twhere
T: CastFrom<U>,
fn as_<T>(self) -> Twhere
T: CastFrom<U>,
self to type T. The semantics of numeric casting with the as operator are followed, so <T as As>::as_::<U> can be used in the same way as T as U for numeric conversions. Read moreSource§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
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§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§impl<T> Pointable for T
impl<T> Pointable for T
§impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
§fn to_subset(&self) -> Option<SS>
fn to_subset(&self) -> Option<SS>
self from the equivalent element of its
superset. Read more§fn is_in_subset(&self) -> bool
fn is_in_subset(&self) -> bool
self is actually part of its subset T (and can be converted to it).§fn to_subset_unchecked(&self) -> SS
fn to_subset_unchecked(&self) -> SS
self.to_subset but without any property checks. Always succeeds.§fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
self to the equivalent element of its superset.