//! Compaction: replacing the document with a snapshot in a new epoch. //! //! * `snapshot`: deterministic snapshot build (step 3). //! * `records`: the replicated compaction records under `_continuum/compactions/`. //! * `coordinator`: proposal, settle period and decision on the node that was asked. //! * `api`: `/admin/compaction(s)` handlers. //! //! Step 4 decides compactions but does not switch epochs yet; a committed record only //! names the new epoch. //! //! The rest of the node is reached exclusively through [`Host`], so this module never //! depends on `AppState`, peers or sync internals. mod api; mod coordinator; mod records; pub(crate) mod snapshot; use std::future::Future; use std::sync::Arc; use std::time::Duration; use automerge::AutoCommit; use axum::{ routing::{get, post}, Router, }; use tokio::sync::broadcast; use crate::replica::Epoch; use crate::sync::ChangeSet; /// What compaction needs from the node. Implemented by `AppState`. /// /// Used generically (`H: Host`), never as `dyn Host`, so the generic methods are fine. pub(crate) trait Host: Clone + Send + Sync + 'static { /// This node's peer ID. fn node_id(&self) -> String; /// The epoch of the local replica. fn current_epoch(&self) -> Epoch; /// Lower bound and default basis for the settle period. fn dormant_after(&self) -> Duration; fn is_node_ready(&self) -> impl Future + Send; /// Known (not forgotten) peers. fn peer_summaries(&self) -> impl Future> + Send; /// Store paths touched by each published document change. fn subscribe_changes(&self) -> broadcast::Receiver>; /// Reads the document on the blocking pool. fn read_doc(&self, f: F) -> impl Future + Send where F: FnOnce(&AutoCommit) -> T + Send + 'static, T: Send + 'static; /// Mutable access to the document without publishing a change (e.g. forks). fn update_doc(&self, f: F) -> impl Future + Send where F: FnOnce(&mut AutoCommit) -> T + Send + 'static, T: Send + 'static; /// One committed change (rolled back on error), published and replicated. fn commit_doc(&self, f: F) -> impl Future> + Send where F: FnOnce(&mut AutoCommit) -> Result + Send + 'static, T: Send + 'static, E: Send + 'static; } #[derive(Clone, Debug)] pub(crate) struct PeerSummary { pub(crate) id: String, /// Not dormant. pub(crate) active: bool, } /// Admin routes. Merged into the authenticated part of the node's router. pub(crate) fn router() -> Router { Router::new() .route("/admin/compaction", post(api::start::)) .route("/admin/compactions", get(api::list::)) .route( "/admin/compactions/{id}", get(api::get::).delete(api::abort::), ) }