Skip to main content

apiary_core/
storage.rs

1//! The StorageBackend trait — the single interface for all storage operations.
2//!
3//! Every storage operation in Apiary goes through this trait. Implementations
4//! include [`LocalBackend`](crate) (filesystem) and [`S3Backend`](crate)
5//! (any S3-compatible endpoint). Application code never accesses the
6//! filesystem or object storage directly.
7
8use async_trait::async_trait;
9use bytes::Bytes;
10
11use crate::Result;
12
13/// The unified storage interface for all Apiary operations.
14///
15/// All data — ledger entries, cell files, registry state, heartbeats —
16/// is read and written through this trait. Implementations must be
17/// `Send + Sync` for use across async tasks and threads.
18///
19/// # Conditional Writes
20///
21/// [`put_if_not_exists`](StorageBackend::put_if_not_exists) is the
22/// serialisation mechanism for concurrent writes. It replaces consensus
23/// protocols (Raft, Paxos) with a single atomic operation provided by
24/// the storage layer.
25#[async_trait]
26pub trait StorageBackend: Send + Sync {
27    /// Write an object. Overwrites if it already exists.
28    async fn put(&self, key: &str, data: Bytes) -> Result<()>;
29
30    /// Read an object. Returns [`ApiaryError::NotFound`] if the key does not exist.
31    async fn get(&self, key: &str) -> Result<Bytes>;
32
33    /// List all object keys matching the given prefix.
34    async fn list(&self, prefix: &str) -> Result<Vec<String>>;
35
36    /// Delete an object. Does not error if the key does not exist.
37    async fn delete(&self, key: &str) -> Result<()>;
38
39    /// Conditional write: succeeds only if the key does not already exist.
40    ///
41    /// Returns `Ok(true)` if the write succeeded (key was created).
42    /// Returns `Ok(false)` if the key already existed (no write performed).
43    ///
44    /// This is the atomic operation that provides write serialisation
45    /// across concurrent nodes, replacing consensus protocols.
46    async fn put_if_not_exists(&self, key: &str, data: Bytes) -> Result<bool>;
47
48    /// Check if an object exists at the given key.
49    async fn exists(&self, key: &str) -> Result<bool>;
50}