pub trait StoragePort: Send + Sync {
// Required methods
fn capabilities(&self) -> StorageCapabilities;
fn append<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
topic_name: &'life1 str,
topic_key: Option<&'life2 str>,
actor_json: Value,
payload_json: Value,
) -> Pin<Box<dyn Future<Output = Result<Event, PhotonError>> + Send + 'async_trait>>
where 'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait;
fn subscribe(
&self,
topic_name: String,
topic_key_filter: Option<String>,
after_seq: Option<i64>,
) -> Pin<Box<dyn Stream<Item = Result<Event, PhotonError>> + Send>>;
fn get_event<'life0, 'life1, 'async_trait>(
&'life0 self,
event_id: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<Option<Event>, PhotonError>> + Send + 'async_trait>>
where 'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait;
fn list_by_topic<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
topic_name: &'life1 str,
topic_key: Option<&'life2 str>,
after_seq: Option<i64>,
limit: usize,
) -> Pin<Box<dyn Future<Output = Result<Vec<Event>, PhotonError>> + Send + 'async_trait>>
where 'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait;
fn list_recent<'life0, 'async_trait>(
&'life0 self,
limit: usize,
) -> Pin<Box<dyn Future<Output = Result<Vec<Event>, PhotonError>> + Send + 'async_trait>>
where 'life0: 'async_trait,
Self: 'async_trait;
fn load_checkpoint<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
subscription_name: &'life1 str,
topic_name: &'life2 str,
topic_key: Option<&'life3 str>,
) -> Pin<Box<dyn Future<Output = Result<Option<i64>, PhotonError>> + Send + 'async_trait>>
where 'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
Self: 'async_trait;
fn commit_checkpoint<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
subscription_name: &'life1 str,
topic_name: &'life2 str,
topic_key: Option<&'life3 str>,
last_seq: i64,
) -> Pin<Box<dyn Future<Output = Result<(), PhotonError>> + Send + 'async_trait>>
where 'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
Self: 'async_trait;
// Provided methods
fn truncate_before<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
topic_name: &'life1 str,
topic_key: Option<&'life2 str>,
truncate_bound: i64,
) -> Pin<Box<dyn Future<Output = Result<u64, PhotonError>> + Send + 'async_trait>>
where 'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait { ... }
fn delivery_seq_pin<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
topic_name: &'life1 str,
topic_key: Option<&'life2 str>,
) -> Pin<Box<dyn Future<Output = Option<i64>> + Send + 'async_trait>>
where 'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait { ... }
}Expand description
Storage adapter contract — append, subscribe, checkpoints, and optional retention.
Each method documents its behavior under Contract. Built-in implementations:
| Adapter | Type | Crate |
|---|---|---|
| In-process | InProcStoragePort | photon-backend (mem) |
SQLite | SqliteStoragePort | photon-backend-sqlite |
NATS JetStream | NatsStoragePort | photon-backend-nats |
| Kafka | KafkaStoragePort | photon-backend-kafka |
| Fluvio | FluvioStoragePort | photon-backend-fluvio |
§Example (use a built-in port)
use std::sync::Arc;
use photon_backend::{InProcStoragePort, StoragePort, TransportCrypto};
let port: Arc<dyn StoragePort> = Arc::new(InProcStoragePort::new(
TransportCrypto::from_env()?,
));
let _caps = port.capabilities();Install at boot via the public crate PhotonBuilder.
Host walkthrough: Integrating the host.
Required Methods§
Sourcefn capabilities(&self) -> StorageCapabilities
fn capabilities(&self) -> StorageCapabilities
Adapter capabilities for contract tests and telemetry.
Sourcefn append<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
topic_name: &'life1 str,
topic_key: Option<&'life2 str>,
actor_json: Value,
payload_json: Value,
) -> Pin<Box<dyn Future<Output = Result<Event, PhotonError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait,
fn append<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
topic_name: &'life1 str,
topic_key: Option<&'life2 str>,
actor_json: Value,
payload_json: Value,
) -> Pin<Box<dyn Future<Output = Result<Event, PhotonError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait,
Append one event to a topic partition.
§Contract
- Assigns a monotonically increasing
seqper(topic_name, topic_key)partition. - Persists a sealed envelope; actor and payload plaintext must not be written to storage or broker records.
- Returns a decrypted
Eventincluding stableevent_id, suitable for API callers and live fanout.
Sourcefn subscribe(
&self,
topic_name: String,
topic_key_filter: Option<String>,
after_seq: Option<i64>,
) -> Pin<Box<dyn Stream<Item = Result<Event, PhotonError>> + Send>>
fn subscribe( &self, topic_name: String, topic_key_filter: Option<String>, after_seq: Option<i64>, ) -> Pin<Box<dyn Stream<Item = Result<Event, PhotonError>> + Send>>
Stream events for a topic partition, optionally replaying after after_seq.
§Contract
- When
after_seqis set, only events withseq > after_seqare yielded. - When
topic_key_filteris set, only matching partition keys are delivered. - The stream runs until dropped or an error; live adapters may block for new events.
Sourcefn get_event<'life0, 'life1, 'async_trait>(
&'life0 self,
event_id: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<Option<Event>, PhotonError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait,
fn get_event<'life0, 'life1, 'async_trait>(
&'life0 self,
event_id: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<Option<Event>, PhotonError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait,
Point lookup by event id (optional — see StorageCapabilities::supports_get_event).
§Contract
- Returns
Nonewhen the id is unknown or the event was truncated by retention.
Sourcefn list_by_topic<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
topic_name: &'life1 str,
topic_key: Option<&'life2 str>,
after_seq: Option<i64>,
limit: usize,
) -> Pin<Box<dyn Future<Output = Result<Vec<Event>, PhotonError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait,
fn list_by_topic<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
topic_name: &'life1 str,
topic_key: Option<&'life2 str>,
after_seq: Option<i64>,
limit: usize,
) -> Pin<Box<dyn Future<Output = Result<Vec<Event>, PhotonError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait,
Bounded page of events for one topic (ops browse).
§Contract
- When
supports_list_eventsis false, returns an empty vec (brokers). - When
after_seqis set, only events withseq > after_seqare included. - When
topic_keyis set, only that partition is included. - Results are ordered by
seqascending and capped atlimit(zero → empty). - Returned events are decrypted like
Self::get_event.
Sourcefn list_recent<'life0, 'async_trait>(
&'life0 self,
limit: usize,
) -> Pin<Box<dyn Future<Output = Result<Vec<Event>, PhotonError>> + Send + 'async_trait>>where
'life0: 'async_trait,
Self: 'async_trait,
fn list_recent<'life0, 'async_trait>(
&'life0 self,
limit: usize,
) -> Pin<Box<dyn Future<Output = Result<Vec<Event>, PhotonError>> + Send + 'async_trait>>where
'life0: 'async_trait,
Self: 'async_trait,
Bounded cross-topic page of newest events (ops browse).
§Contract
- When
supports_list_eventsis false, returns an empty vec (brokers). - Ordered by
created_atdescending, capped atlimit(zero → empty). - Returned events are decrypted like
Self::get_event.
Sourcefn load_checkpoint<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
subscription_name: &'life1 str,
topic_name: &'life2 str,
topic_key: Option<&'life3 str>,
) -> Pin<Box<dyn Future<Output = Result<Option<i64>, PhotonError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
Self: 'async_trait,
fn load_checkpoint<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
subscription_name: &'life1 str,
topic_name: &'life2 str,
topic_key: Option<&'life3 str>,
) -> Pin<Box<dyn Future<Output = Result<Option<i64>, PhotonError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
Self: 'async_trait,
Load durable subscription high-water seq.
§Contract
- Returns
Nonewhen no checkpoint exists (caller chooses replay start policy).
Sourcefn commit_checkpoint<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
subscription_name: &'life1 str,
topic_name: &'life2 str,
topic_key: Option<&'life3 str>,
last_seq: i64,
) -> Pin<Box<dyn Future<Output = Result<(), PhotonError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
Self: 'async_trait,
fn commit_checkpoint<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
subscription_name: &'life1 str,
topic_name: &'life2 str,
topic_key: Option<&'life3 str>,
last_seq: i64,
) -> Pin<Box<dyn Future<Output = Result<(), PhotonError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
Self: 'async_trait,
Persist durable subscription high-water seq.
§Contract
- Stored
last_seqis monotonic per subscription partition: a regressive commit must not lower an existing checkpoint.
Provided Methods§
Sourcefn truncate_before<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
topic_name: &'life1 str,
topic_key: Option<&'life2 str>,
truncate_bound: i64,
) -> Pin<Box<dyn Future<Output = Result<u64, PhotonError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait,
fn truncate_before<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
topic_name: &'life1 str,
topic_key: Option<&'life2 str>,
truncate_bound: i64,
) -> Pin<Box<dyn Future<Output = Result<u64, PhotonError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait,
Trim events before truncate_bound for a partition (no-op when unsupported).
§Contract
- Default implementation returns
Ok(0)without removing events. - Supporting adapters remove events with
seq < truncate_bound.
Sourcefn delivery_seq_pin<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
topic_name: &'life1 str,
topic_key: Option<&'life2 str>,
) -> Pin<Box<dyn Future<Output = Option<i64>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait,
fn delivery_seq_pin<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
topic_name: &'life1 str,
topic_key: Option<&'life2 str>,
) -> Pin<Box<dyn Future<Output = Option<i64>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait,
Last delivered seq pin for retention watermarks (optional).
§Contract
- Default returns
None(no delivery-layer retention pin).
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".