exoware_server/engine.rs
1//! Storage callbacks for the store services.
2//!
3//! Implement the capability traits your component serves. Errors are surfaced to clients as
4//! internal RPC failures (string message only; keep messages safe to expose if you rely on that).
5
6use std::collections::HashMap;
7use std::future::Future;
8
9use bytes::Bytes;
10use exoware_sdk::prune_policy::PrunePolicyDocument;
11
12/// Backend-defined query metadata.
13///
14/// Keep this lightweight: streaming query RPCs may emit detail on every frame.
15pub type QueryExtra = HashMap<String, buffa_types::google::protobuf::Value>;
16
17#[derive(Clone, Debug, Default)]
18pub struct RangeScanBatch {
19 /// Rows read by this cursor pull.
20 pub rows: Vec<(Bytes, Bytes)>,
21 /// Backend-specific query metadata after reading these rows.
22 pub extra: QueryExtra,
23}
24
25/// Owned pull-based range cursor for query RPCs.
26///
27/// Implementations own any state needed to produce batches, allowing query
28/// handlers to pull rows lazily without borrowing the engine.
29pub trait RangeScan: Send {
30 /// Pull up to `max_items` rows. Returning an empty `rows` batch marks EOF.
31 /// EOF may carry non-empty `extra` with final query metadata.
32 /// `extra` is emitted with the response frame built from the same batch.
33 fn next_batch(
34 &mut self,
35 max_items: usize,
36 ) -> impl Future<Output = Result<RangeScanBatch, String>> + Send;
37}
38
39/// Local sequence frontier visible to this process.
40pub trait Sequence: Send + Sync + 'static {
41 /// Highest sequence number this process can currently serve.
42 fn current_sequence(&self) -> u64;
43}
44
45/// Ingest write capability.
46pub trait Ingest: Send + Sync + 'static {
47 /// Persist key-value pairs atomically and return the new global sequence number for this write.
48 fn put_batch(
49 &self,
50 kvs: Vec<(Bytes, Bytes)>,
51 ) -> impl Future<Output = Result<u64, String>> + Send;
52}
53
54/// Query read capability.
55pub trait Query: Sequence {
56 type RangeScan: RangeScan + 'static;
57
58 /// Fetch the value for a single key plus backend-specific query metadata.
59 /// Returns `None` when the key does not exist.
60 fn get(
61 &self,
62 key: Bytes,
63 ) -> impl Future<Output = Result<(Option<Bytes>, QueryExtra), String>> + Send;
64
65 /// Cursor over keys in `[start, end]` (inclusive) when `end` is non-empty;
66 /// empty `end` means unbounded above. Matches `store.query.v1.RangeRequest`
67 /// / `ReduceRequest` on the wire. `limit` caps rows yielded.
68 fn range_scan(
69 &self,
70 start: Bytes,
71 end: Bytes,
72 limit: usize,
73 forward: bool,
74 ) -> impl Future<Output = Result<Self::RangeScan, String>> + Send;
75
76 /// Batch-get plus backend-specific query metadata. Returns `(key, Option<value>)`
77 /// for each input key, preserving order.
78 fn get_many(
79 &self,
80 keys: Vec<Bytes>,
81 ) -> impl Future<Output = Result<(Vec<(Bytes, Option<Bytes>)>, QueryExtra), String>> + Send;
82}
83
84/// Prune mutation capability.
85pub trait Prune: Send + Sync + 'static {
86 /// Apply a validated prune policy document sequentially.
87 fn apply_prune_policies(
88 &self,
89 document: PrunePolicyDocument,
90 ) -> impl Future<Output = Result<(), String>> + Send;
91}
92
93/// Retained per-sequence batch-log access for stream replay and lookups.
94pub trait Log: Sequence {
95 /// Return the (key, value) pairs written by the `put_batch` call that was
96 /// assigned `sequence_number`. Return `Ok(None)` when the batch is not
97 /// available from this log.
98 ///
99 /// Engines that don't retain a log return `Ok(None)` unconditionally,
100 /// which disables `GetBatch` and since-cursored `Subscribe` for that
101 /// deployment.
102 ///
103 /// The stream service maps unavailable batches to `BATCH_NOT_FOUND` when
104 /// they are beyond the visible sequence frontier and `BATCH_EVICTED`
105 /// otherwise.
106 fn get_batch(
107 &self,
108 sequence_number: u64,
109 ) -> impl Future<Output = Result<Option<Vec<(Bytes, Bytes)>>, String>> + Send;
110
111 /// Lowest retained batch sequence number, or `None` when the log is
112 /// empty. Surfaced in `BATCH_EVICTED` error details so clients know where
113 /// to resume from.
114 fn oldest_retained_batch(&self) -> impl Future<Output = Result<Option<u64>, String>> + Send;
115}
116
117/// Compatibility facade for backends that serve every store capability.
118pub trait StoreEngine: Ingest + Query + Prune + Log {}
119
120impl<T: Ingest + Query + Prune + Log> StoreEngine for T {}