Push new log entries to peers on commit
A broadcast channel forwards each freshly committed entry straight to connected peers, so a registration propagates in milliseconds. The periodic digest drops to a 10s anti-entropy backstop.
This commit is contained in:
parent
d0556ebe8c
commit
df7b413862
3 changed files with 101 additions and 25 deletions
|
|
@ -7,6 +7,7 @@ use argon2::password_hash::rand_core::OsRng;
|
|||
use argon2::password_hash::SaltString;
|
||||
use argon2::{Argon2, PasswordHash, PasswordHasher, PasswordVerifier};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::broadcast;
|
||||
|
||||
use super::scram::{self, Hash};
|
||||
|
||||
|
|
@ -66,11 +67,12 @@ pub struct EventLog {
|
|||
lamport: u64, // logical clock, ticked on every event
|
||||
versions: HashMap<String, u64>, // per-origin highest seq applied (version vector)
|
||||
entries: Vec<LogEntry>, // full log, kept so peers can pull what they lack
|
||||
outbound: Option<broadcast::Sender<LogEntry>>, // push newly committed entries to peers
|
||||
}
|
||||
|
||||
impl EventLog {
|
||||
fn open(path: PathBuf, origin: String) -> (Self, Vec<Event>) {
|
||||
let mut log = Self { path, origin, lamport: 0, versions: HashMap::new(), entries: Vec::new() };
|
||||
let mut log = Self { path, origin, lamport: 0, versions: HashMap::new(), entries: Vec::new(), outbound: None };
|
||||
if let Ok(data) = std::fs::read_to_string(&log.path) {
|
||||
for line in data.lines().filter(|l| !l.trim().is_empty()) {
|
||||
match serde_json::from_str::<LogEntry>(line) {
|
||||
|
|
@ -104,6 +106,7 @@ impl EventLog {
|
|||
let entry = LogEntry { origin: self.origin.clone(), seq: self.next_seq(), lamport: self.lamport, event };
|
||||
self.persist(&entry)?;
|
||||
self.versions.insert(entry.origin.clone(), entry.seq);
|
||||
self.notify(&entry);
|
||||
self.entries.push(entry);
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -119,10 +122,23 @@ impl EventLog {
|
|||
self.lamport = self.lamport.max(entry.lamport) + 1; // Lamport receive rule
|
||||
self.versions.insert(entry.origin.clone(), entry.seq);
|
||||
let event = entry.event.clone();
|
||||
self.notify(&entry);
|
||||
self.entries.push(entry);
|
||||
Ok(Some(event))
|
||||
}
|
||||
|
||||
// Push a freshly committed entry to connected peers, if any are wired up.
|
||||
// Best effort: a lagging subscriber just relies on the periodic digest.
|
||||
fn notify(&self, entry: &LogEntry) {
|
||||
if let Some(tx) = &self.outbound {
|
||||
let _ = tx.send(entry.clone());
|
||||
}
|
||||
}
|
||||
|
||||
fn set_outbound(&mut self, tx: broadcast::Sender<LogEntry>) {
|
||||
self.outbound = Some(tx);
|
||||
}
|
||||
|
||||
// Our version vector: highest seq applied per origin.
|
||||
fn version_vector(&self) -> HashMap<String, u64> {
|
||||
self.versions.clone()
|
||||
|
|
@ -208,6 +224,11 @@ impl Db {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Wire the log to a broadcast channel; each new entry is pushed to peers.
|
||||
pub fn set_outbound(&mut self, tx: broadcast::Sender<LogEntry>) {
|
||||
self.log.set_outbound(tx);
|
||||
}
|
||||
|
||||
/// Our version vector, advertised to peers so they can send what we lack.
|
||||
pub fn version_vector(&self) -> HashMap<String, u64> {
|
||||
self.log.version_vector()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue