ubiquisync_core/sync/processor.rs
1//! [`LogProcessor`]: the write side of a replica.
2
3use async_trait::async_trait;
4
5use crate::codec::DecodedEntry;
6use crate::sync::cursors::HasCursors;
7use crate::uuid::Uuid;
8
9use super::error::SyncError;
10
11/// The apply target of replication: absorbs entries addressed by `(peer, index)`.
12///
13/// Multi-writer — it accepts any origin, so it can relay and merge. (A file log,
14/// which writes only its own origin, is [`FileLogSink`](super::FileLogSink)
15/// instead.)
16///
17/// Entries for a peer must be applied in contiguous ascending order — a cursor
18/// is a single high-water mark that can't hold a hole. An already-seen `index`
19/// is an idempotent no-op ([`Applied::new`] `== false`), so multi-channel
20/// redelivery is safe; an `index` beyond the next expected one is a gap and is
21/// rejected.
22///
23/// `&self`: one processor is shared (`Arc<dyn LogProcessor>`) as the apply target
24/// of several sources while also read as a [`LogSource`](super::LogSource), so it
25/// mutates through interior mutability.
26#[async_trait]
27pub trait LogProcessor<E>: HasCursors {
28 /// Apply one entry at `(peer, index)`, advancing the cursor for `peer` to
29 /// `index + 1`. Idempotent (see the trait docs).
30 async fn apply(
31 &self,
32 peer: Uuid,
33 index: u64,
34 entry: DecodedEntry<E>,
35 ) -> Result<Applied, SyncError>;
36}
37
38/// Outcome of [`LogProcessor::apply`].
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub struct Applied {
41 /// `true` if newly applied; `false` if a re-delivery that changed nothing.
42 pub new: bool,
43}