Skip to main content

ubiquisync_core/sync/
file_log.rs

1//! The file-log flavor: single-writer, multi-reader logs on shared storage.
2//!
3//! A file log is append-only segment directories on cloud storage, one per
4//! origin. Only the originating device writes its own log. These traits encode
5//! that: a file log reads any origin ([`FileLogPuller`] / [`LogSource`]) but
6//! writes only its own ([`FileLogSink`]), so it's a [`FileLogReplica`], never a
7//! full [`Replica`](super::Replica). Single-writer files are why dumb file sync
8//! suffices and each log's extent is a trustworthy cursor.
9
10use crate::codec::DecodedEntry;
11use crate::log_entry::LogEntry;
12use crate::uuid::Uuid;
13
14use super::error::SyncError;
15use super::source::LogSource;
16
17/// Synchronous reader over the segment directories — one per origin. Sync
18/// because it decodes local files; an adapter pairs it with a watch/poll loop to
19/// present it as an async [`LogSource`].
20pub trait FileLogPuller<E> {
21    /// Origins present in this store.
22    fn list_peers(&self) -> Vec<Uuid>;
23
24    /// Bounded batch of `peer`'s entries at or after `from`; empty when drained.
25    /// The synchronous counterpart to [`LogSource::read_since`].
26    fn read_entries(
27        &self,
28        peer: Uuid,
29        from: u64,
30    ) -> Result<Vec<(u64, DecodedEntry<E>)>, SyncError>;
31}
32
33/// Write side of a file log: append to your own origin only. No method writes a
34/// foreign origin — that's the single-writer invariant. The caller supplies each
35/// entry's index (the oplog assigns them; the file log follows).
36pub trait FileLogSink<E> {
37    /// The origin this sink writes — the local node's id.
38    fn self_id(&self) -> Uuid;
39
40    /// Append `entries` at their given indices; batched so one call can be one
41    /// segment write. Real entries only — expunging rewrites an existing
42    /// segment, it never appends.
43    fn write(&mut self, entries: &[(u64, LogEntry<E>)]) -> Result<(), SyncError>;
44}
45
46/// A file log as a replica: multi-reader ([`LogSource`]), single-writer
47/// ([`FileLogSink`]). The single-writer counterpart to
48/// [`Replica`](super::Replica); owned by its mirror, not shared.
49pub trait FileLogReplica<E>: FileLogSink<E> + LogSource<E> {}
50
51/// Anything that is both is a [`FileLogReplica`].
52impl<E, T: FileLogSink<E> + LogSource<E>> FileLogReplica<E> for T {}