Skip to main content

lance_core/cache/
backend.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright The Lance Authors
3
4//! Backend interface for cache implementors.
5//!
6//! This module defines the trait that custom cache backends must implement,
7//! along with the entry type they operate on. Most callers should
8//! use [`LanceCache`](super::LanceCache) instead of interacting with
9//! backends directly.
10//!
11//! # Migrating custom backends
12//!
13//! Cache keys are opaque 16-byte values. Store
14//! [`InternalCacheKey::as_bytes`] directly instead of decomposing a logical
15//! prefix, key string, and Rust type name. The physical namespace must also
16//! include [`CACHE_KEY_FORMAT`](super::CACHE_KEY_FORMAT), so a future key
17//! protocol produces cold misses instead of aliases. Persistent or tiered
18//! backends can route serializable values with [`CacheCodec::type_id`].
19//!
20//! Prefix invalidation and key inventory are intentionally not part of this
21//! interface: one-way digests cannot support either operation without
22//! retaining the logical strings that fixed-size keys are designed to remove.
23//! Existing callers should migrate removed symbols as follows:
24//! - replace `with_backend_and_prefix(backend, prefix)` with
25//!   [`LanceCache::with_backend`](super::LanceCache::with_backend) followed by
26//!   [`LanceCache::with_key_prefix`](super::LanceCache::with_key_prefix);
27//! - replace `invalidate_prefix` with [`LanceCache::clear`](super::LanceCache::clear)
28//!   when clearing the shared backend is acceptable, or rotate a versioned
29//!   namespace to leave older entries to age out;
30//! - remove uses of `prefix`, `keys`, and session key-inventory methods; opaque
31//!   keys have no readable or enumerable equivalent.
32
33use std::any::Any;
34use std::pin::Pin;
35use std::sync::Arc;
36
37use async_trait::async_trait;
38use futures::Future;
39
40use crate::Result;
41
42use super::{CacheCodec, InternalCacheKey};
43
44/// A type-erased cache entry.
45pub type CacheEntry = Arc<dyn Any + Send + Sync>;
46
47/// Low-level pluggable cache backend.
48///
49/// Implementations store entries keyed by [`InternalCacheKey`] and return
50/// type-erased [`CacheEntry`] values.
51/// [`LanceCache`](super::LanceCache) handles key construction and type safety;
52/// backend authors only need to implement storage and eviction.
53#[async_trait]
54pub trait CacheBackend: Send + Sync + std::fmt::Debug {
55    /// Look up an entry by its key.
56    ///
57    /// `codec` is provided so that persistent backends can deserialize the
58    /// entry from storage. In-memory backends can ignore it. When `codec`
59    /// is `None`, the entry type does not support serialization yet and
60    /// must be stored in-memory.
61    ///
62    /// The goal is for all cache entry types to eventually have codecs,
63    /// at which point the `Option` will be removed.
64    async fn get(&self, key: &InternalCacheKey, codec: Option<CacheCodec>) -> Option<CacheEntry>;
65
66    /// Store an entry. `size_bytes` is used for eviction accounting.
67    ///
68    /// See [`get`](Self::get) for codec semantics.
69    async fn insert(
70        &self,
71        key: &InternalCacheKey,
72        entry: CacheEntry,
73        size_bytes: usize,
74        codec: Option<CacheCodec>,
75    );
76
77    /// Get an existing entry or compute it from `loader`.
78    ///
79    /// Implementations should deduplicate concurrent loads for the same key
80    /// so the loader runs at most once.
81    ///
82    /// Returns `(entry, was_cached)` where `was_cached` is `true` if the entry
83    /// was already present in the cache (the loader was not invoked).
84    ///
85    /// See [`get`](Self::get) for codec semantics.
86    async fn get_or_insert<'a>(
87        &self,
88        key: &InternalCacheKey,
89        loader: Pin<Box<dyn Future<Output = Result<(CacheEntry, usize)>> + Send + 'a>>,
90        codec: Option<CacheCodec>,
91    ) -> Result<(CacheEntry, bool)>;
92
93    /// Remove all entries.
94    async fn clear(&self);
95
96    /// Number of entries currently stored (may flush pending operations).
97    async fn num_entries(&self) -> usize;
98
99    /// Total weighted size in bytes of all stored entries (may flush pending operations).
100    async fn size_bytes(&self) -> usize;
101
102    /// Approximate number of entries, callable from synchronous contexts.
103    /// Backends that cannot provide this cheaply should return 0.
104    fn approx_num_entries(&self) -> usize {
105        0
106    }
107
108    /// Approximate weighted size in bytes, callable from synchronous contexts.
109    /// Used by `DeepSizeOf` to report cache memory usage.
110    /// Backends that cannot provide this cheaply should return 0.
111    ///
112    /// Assumes entries do not share underlying buffers; if they do, the
113    /// returned total may overcount.
114    fn approx_size_bytes(&self) -> usize {
115        0
116    }
117}