Skip to main content

StorageBackend

Trait StorageBackend 

Source
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§

Source

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,

Source

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,

Source

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,

Source

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§

Source

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.

Source

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.

Source

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.

Source

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".

Implementors§