common/coordinator/traits.rs
1use crate::StorageRead;
2use crate::coordinator::WriteCommand;
3use crate::storage::StorageSnapshot;
4use async_trait::async_trait;
5use std::ops::Range;
6use std::sync::Arc;
7
8/// The level of durability for a write.
9///
10/// Durability levels form an ordered progression: `Applied < Written < Durable`.
11/// Each level provides stronger guarantees about write persistence.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
13pub enum Durability {
14 Applied,
15 Written,
16 Durable,
17}
18
19/// A delta accumulates writes and can produce a frozen snapshot for flushing.
20///
21/// The write coordinator manages a pipeline of three stages, each represented
22/// by a different type:
23///
24/// - **`Delta`** (this trait) — the mutable, in-progress batch. Writes are
25/// applied here until the delta is frozen.
26/// - **`Frozen`** — an immutable snapshot of the delta, sent to the
27/// [`Flusher`] to be persisted to storage.
28/// - **`Broadcast`** — a minimal representation of the flushed state
29/// that readers need to update their read image.
30pub trait Delta: Sized + Send + Sync + 'static {
31 /// Mutable state owned by the delta while it accumulates writes.
32 /// Returned to the write coordinator on [`freeze`](Delta::freeze) so the
33 /// next delta can continue where this one left off.
34 type Context: Send + Sync + 'static;
35 /// A single write operation applied via [`apply`](Delta::apply).
36 type Write: Send + 'static;
37 /// Immutable snapshot produced by [`freeze`](Delta::freeze), consumed by
38 /// the [`Flusher`] to persist the batch to storage.
39 type Frozen: Send + Sync + 'static;
40 /// Provides an interface for reading the frozen delta. Though Frozen is immutable, we
41 /// support specifying a distinct read type to allow implementers to provide a different
42 /// representation or view of flushed state. For example, readers that only allow reading
43 /// the data flushed to storage can materialize a minimal view of metadata to allow the reader
44 /// to cheaply update the read image when a new view is broadcast after a flush, while at the
45 /// same type allowing Frozen to be owned by the coordinator so the contained data doesn't
46 /// need to be copied during flush.
47 type FrozenView: Clone + Send + Sync + 'static;
48 /// Metadata returned from [`apply`](Delta::apply), delivered to the caller
49 /// through [`WriteHandle::wait`](super::WriteHandle::wait).
50 type ApplyResult: Clone + Send + 'static;
51 /// Provides an interface for reading the current delta. The specific read API
52 /// is up to the delta implementation. It is up to the implementation to provide
53 /// the APIs required for a given database, including support for snapshot isolation
54 type DeltaView: Clone + Send + Sync + 'static;
55
56 /// Create a new delta initialized from a snapshot context.
57 /// The delta takes ownership of the context while it is mutable.
58 fn init(context: Self::Context) -> Self;
59
60 /// Apply a write to the delta and return a result for the caller.
61 fn apply(&mut self, write: Self::Write) -> Result<Self::ApplyResult, String>;
62
63 /// Estimate the size of the delta in bytes.
64 fn estimate_size(&self) -> usize;
65
66 /// Freezes the current delta, creating an image with the delta
67 /// applied.
68 ///
69 /// Returns the frozen delta and the context (which was owned by the delta).
70 /// Implementations should ensure this operation is efficient (e.g., via
71 /// copy-on-write or reference counting) since it blocks writes. After this
72 /// is complete, the [`Flusher::flush_delta`] happens on a background thread.
73 fn freeze(self) -> (Self::Frozen, Self::FrozenView, Self::Context);
74
75 fn reader(&self) -> Self::DeltaView;
76}
77
78/// A value representing data written with some range of epochs
79#[derive(Clone)]
80pub struct EpochStamped<T> {
81 pub val: T,
82 /// The range of epochs contained in this value (exclusive end).
83 /// Start is the first epoch in the flush, end is one past the last epoch.
84 pub epoch_range: Range<u64>,
85}
86
87impl<T> EpochStamped<T> {
88 pub(crate) fn new(val: T, epoch_range: Range<u64>) -> Self {
89 Self { val, epoch_range }
90 }
91}
92
93/// A flusher persists frozen deltas and ensures storage durability.
94#[async_trait]
95pub trait Flusher<D: Delta>: Send + Sync + 'static {
96 /// Flush a frozen delta to storage.
97 ///
98 /// Consumes the frozen delta by value and returns a snapshot for readers
99 /// along with a broadcast payload for subscribers.
100 async fn flush_delta(
101 &mut self,
102 frozen: D::Frozen,
103 epoch_range: &Range<u64>,
104 ) -> Result<Arc<dyn StorageSnapshot>, String>;
105
106 /// Ensure storage durability (e.g. call storage.flush()).
107 async fn flush_storage(&self) -> Result<(), String>;
108}