//! Broadcast channels and op-tracking statics for the CRDT state layer. //! //! Provides the outbound sync channel ([`SYNC_TX`]), the event broadcast //! channel ([`CRDT_EVENT_TX`]), and the in-memory op journal //! ([`ALL_OPS`] / [`VECTOR_CLOCK`]) that tracks every applied op for //! delta-sync. use std::sync::{Mutex, OnceLock}; use bft_json_crdt::json_crdt::SignedOp; use tokio::sync::broadcast; use super::super::VectorClock; use super::super::hex; use super::super::types::CrdtEvent; /// Broadcast channel for CRDT events (stage transitions, etc.). pub(super) static CRDT_EVENT_TX: OnceLock> = OnceLock::new(); /// Broadcast channel for outbound ops to sync peers. pub(in crate::crdt_state) static SYNC_TX: OnceLock> = OnceLock::new(); /// All persisted ops as JSON strings, in causal (insertion) order. /// /// Pub(crate) so that `crdt_snapshot` can access it for compaction. pub(crate) static ALL_OPS: OnceLock>> = OnceLock::new(); /// Live vector clock tracking op counts per author. /// /// Updated in lockstep with `ALL_OPS` — every time an op is appended to the /// journal, the corresponding author's count is incremented here. This avoids /// re-parsing all ops when a peer requests `our_vector_clock()`. pub(crate) static VECTOR_CLOCK: OnceLock> = OnceLock::new(); /// Append an op's JSON to `ALL_OPS` and bump the author's count in `VECTOR_CLOCK`. /// /// Centralises the bookkeeping that must stay in sync between the two statics. pub(in crate::crdt_state) fn track_op(signed: &SignedOp, json: String) { if let Some(all) = ALL_OPS.get() && let Ok(mut v) = all.lock() { v.push(json); } if let Some(vc) = VECTOR_CLOCK.get() && let Ok(mut clock) = vc.lock() { let author_hex = hex::encode(&signed.author()); *clock.entry(author_hex).or_insert(0) += 1; } }