gunnar-server 3.0.0

The gunnar git server: SSH and smart-HTTP git transports and the gRPC control plane. The appliance half, extracted from gunnar-cli so the server code lives in its own crate.
Documentation
//! **What one control-plane change costs main's disk in flushes** (perf twin-apply-fsync,
//! 2026-10-06; the load test's "~180 KiB of disk per txlog entry on main").
//!
//! A control document change on main is a run of frames: the shard blobs a sharded document's
//! write touched (`ControlBlob`, typically four: a leaf, a branch, the root, the document) and the
//! pointer move (`ControlDoc`). Each was appended with its own `fdatasync` AND stored with its own
//! durable redb commit (another fsync): ten flushes for one change, every one of them on the
//! primary's critical path.
//!
//! A blob is invisible until a document points at it (content-addressed; the pointer move is the
//! only thing that makes it part of any state), so the blob frames need to be durable only BY the
//! frame that publishes them — the `ControlDoc` frame, whose `fdatasync` covers every frame
//! staged before it in the same file — and the blob rows only BY the redb commit that moves the
//! pointer (a durable commit persists every earlier one). The bound: one log fsync and one redb
//! fsync per document change, counted by the log's and the control database's own counters
//! (`Log::frame_syncs`, `ControlDb::durable_commits`).

use gunnar_core::txlog::Kind;
use gunnar_core::{Principal, PrincipalKind};
use gunnar_server::stores::ServerStores;

const ACCOUNTS: usize = 30;

fn key_line() -> String {
    let key = gunnar_core::ssh_key::PrivateKey::from_openssh(gunnar_core::mint_ed25519().unwrap())
        .unwrap();
    key.public_key().to_openssh().unwrap()
}

#[test]
fn a_control_change_on_main_costs_one_log_fsync_and_one_redb_fsync() {
    let dir = tempfile::tempdir().unwrap();
    let stores = ServerStores::open(dir.path()).unwrap();
    let log = stores.txlog();
    let db = stores.control_db();
    let (h0, s0, c0) = (log.head_seq(), log.frame_syncs(), db.durable_commits());
    for i in 0..ACCOUNTS {
        let id = format!("u{i:05}");
        stores
            .auth()
            .add_principal(Principal::new(&id, &id, PrincipalKind::User).unwrap())
            .unwrap();
        stores.auth().register_key(&id, &key_line(), None).unwrap();
        stores.catalog().create_namespace(&id).unwrap();
    }
    let (h1, s1, c1) = (log.head_seq(), log.frame_syncs(), db.durable_commits());
    let (mut docs, mut blobs, mut other) = (0u64, 0u64, 0u64);
    for seq in h0 + 1..=h1 {
        match gunnar_core::txlog::Kind::from_u16(log.locate_blob(seq).unwrap().kind) {
            Some(Kind::ControlDoc) => docs += 1,
            Some(Kind::ControlBlob) => blobs += 1,
            _ => other += 1,
        }
    }
    let (syncs, commits) = (s1 - s0, c1 - c0);
    let line = format!(
        "{ACCOUNTS} sign-ups on main: {} frames ({docs} documents, {blobs} blobs, {other} other) cost {syncs} log fsyncs and {commits} redb durable commits — {:.2} and {:.2} per document change",
        h1 - h0,
        syncs as f64 / docs as f64,
        commits as f64 / docs as f64
    );
    eprintln!("{line}");
    assert!(
        docs > 0 && blobs > docs,
        "the workload is document changes with their shards: {line}"
    );
    assert!(
        syncs <= docs + other,
        "one log fsync per document change (its blobs ride on it): {line}"
    );
    assert!(
        commits <= docs + other,
        "one redb durable commit per document change (its blobs ride on it): {line}"
    );
}