Skip to main content

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}