pub trait StorageBackend: Send + Sync {
// Required methods
fn get<'life0, 'life1, 'async_trait>(
&'life0 self,
path: &'life1 str,
) -> Pin<Box<dyn Future<Output = StorageResult<Option<StorageEntry>>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait;
fn put<'life0, 'life1, 'async_trait>(
&'life0 self,
path: &'life1 str,
entry: StorageEntry,
) -> Pin<Box<dyn Future<Output = StorageResult<()>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait;
fn delete<'life0, 'life1, 'async_trait>(
&'life0 self,
path: &'life1 str,
) -> Pin<Box<dyn Future<Output = StorageResult<()>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait;
fn list<'life0, 'life1, 'async_trait>(
&'life0 self,
prefix: &'life1 str,
) -> Pin<Box<dyn Future<Output = StorageResult<Vec<String>>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait;
// Provided methods
fn list_expired<'life0, 'life1, 'async_trait>(
&'life0 self,
prefix: &'life1 str,
now: DateTime<Utc>,
) -> Pin<Box<dyn Future<Output = StorageResult<Vec<String>>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait { ... }
fn replace_if_unchanged<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
path: &'life1 str,
expected: &'life2 [u8],
entry: StorageEntry,
) -> Pin<Box<dyn Future<Output = StorageResult<bool>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait { ... }
fn ping<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = StorageResult<()>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait { ... }
fn try_acquire_lock<'life0, 'life1, 'async_trait>(
&'life0 self,
_key: &'life1 str,
) -> Pin<Box<dyn Future<Output = StorageResult<bool>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait { ... }
}Required Methods§
fn get<'life0, 'life1, 'async_trait>(
&'life0 self,
path: &'life1 str,
) -> Pin<Box<dyn Future<Output = StorageResult<Option<StorageEntry>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn put<'life0, 'life1, 'async_trait>(
&'life0 self,
path: &'life1 str,
entry: StorageEntry,
) -> Pin<Box<dyn Future<Output = StorageResult<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn delete<'life0, 'life1, 'async_trait>(
&'life0 self,
path: &'life1 str,
) -> Pin<Box<dyn Future<Output = StorageResult<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn list<'life0, 'life1, 'async_trait>(
&'life0 self,
prefix: &'life1 str,
) -> Pin<Box<dyn Future<Output = StorageResult<Vec<String>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Provided Methods§
Sourcefn list_expired<'life0, 'life1, 'async_trait>(
&'life0 self,
prefix: &'life1 str,
now: DateTime<Utc>,
) -> Pin<Box<dyn Future<Output = StorageResult<Vec<String>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn list_expired<'life0, 'life1, 'async_trait>(
&'life0 self,
prefix: &'life1 str,
now: DateTime<Utc>,
) -> Pin<Box<dyn Future<Output = StorageResult<Vec<String>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Paths under prefix whose expires_at has already passed.
The default walks every key and fetches it, which is what the lease reaper used to do unconditionally. A backend that keeps expiry in a queryable column should override this: the reaper runs on an interval forever, so the naive scan is O(all leases) per pass per deployment.
Sourcefn replace_if_unchanged<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
path: &'life1 str,
expected: &'life2 [u8],
entry: StorageEntry,
) -> Pin<Box<dyn Future<Output = StorageResult<bool>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn replace_if_unchanged<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
path: &'life1 str,
expected: &'life2 [u8],
entry: StorageEntry,
) -> Pin<Box<dyn Future<Output = StorageResult<bool>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Replaces path’s value only if it still holds exactly expected,
returning false when it changed underneath.
Needed by key rotation, which decrypts and re-encrypts in three steps: without this, a write landing between the read and the write would be silently overwritten by a re-encryption of stale plaintext.
The default is read-compare-write, which is not atomic — override it wherever the backend can do the comparison itself.
Sourcefn ping<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = StorageResult<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn ping<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = StorageResult<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Cheap liveness probe for the health endpoint. It must stay cheap: a load balancer calls it constantly, against every replica, forever.
Sourcefn try_acquire_lock<'life0, 'life1, 'async_trait>(
&'life0 self,
_key: &'life1 str,
) -> Pin<Box<dyn Future<Output = StorageResult<bool>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn try_acquire_lock<'life0, 'life1, 'async_trait>(
&'life0 self,
_key: &'life1 str,
) -> Pin<Box<dyn Future<Output = StorageResult<bool>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Best-effort cross-process mutual exclusion, so exactly one replica runs a singleton background task. Granting unconditionally is correct for a single-node or in-memory backend, which is why that is the default.
A real implementation must tie the lock to the connection holding it,
so that a crashed node releases it without anyone noticing — that is
what makes failover work without heartbeats or timeouts. There is
deliberately no release: the lock is held for the process lifetime
and the backend reclaims it when the connection dies.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".