Add gossip replication between nodes

Nodes run anti-entropy over a TCP link: each advertises its version
vector, the peer replies with the log entries it lacks, and ingest is
idempotent so re-delivery and reconnect after a split both converge.
The engine is shared behind a mutex; config gains [gossip] and [[peer]].
This commit is contained in:
Jean Chevronnet 2026-07-12 07:05:07 +00:00
parent 82e41e95b2
commit d0556ebe8c
No known key found for this signature in database
7 changed files with 319 additions and 20 deletions

View file

@ -9,7 +9,7 @@ use std::time::{Duration, Instant};
use base64::{engine::general_purpose::STANDARD, Engine as _};
use crate::proto::{NetAction, NetEvent, RegReply};
use db::{Db, RegError};
use db::{Db, LogEntry, RegError};
use scram::Verifier;
use service::{Sender, Service, ServiceCtx};
use state::Network;
@ -91,6 +91,29 @@ impl Engine {
self.db.scram_iterations
}
// Gossip pass-throughs to the account store, used by the replication layer.
pub fn gossip_digest(&self) -> HashMap<String, u64> {
self.db.version_vector()
}
pub fn gossip_missing(&self, peer: &HashMap<String, u64>) -> Vec<LogEntry> {
self.db.missing_for(peer)
}
pub fn gossip_ingest(&mut self, entry: LogEntry) -> std::io::Result<()> {
self.db.ingest(entry)
}
#[cfg(test)]
pub(crate) fn test_register(&mut self, name: &str) {
self.db.register(name, "pw", None).unwrap();
}
#[cfg(test)]
pub(crate) fn test_has_account(&self, name: &str) -> bool {
self.db.exists(name)
}
// Insert or refresh a client's in-progress SASL session, stamped now.
fn stash_sasl(&mut self, client: String, session: SaslSession) {
self.sasl_sessions.insert(client, TimedSession { touched: Instant::now(), session });