Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/gunnar

bench/src/lib.rs

//! gunnar's benchmark harness.
//!
//! # Why there is code here at all
//!
//! There used to be none, deliberately: the house bencher contract is that a
//! repo exposes **one** example target
//! printing one `BenchRun` JSON line, and an *example* is the only Cargo target
//! that may use dev-time-only code without shipping it. That reasoning is intact
//! and `Cargo.toml` still records it.
//!
//! [`readside`] is the exception the one-implementation rule forces. Two
//! targets need the same one-fetch driver — `examples/readside-probe.rs`, the
//! diagnostic that answers "what did this fetch ask the object store for", and
//! `gunnar.fetch_readside` in `examples/nornir-bench.rs`, the row that persists
//! the answer. Copying a counting `ObjectStore` and a hand-built v2
//! `command=fetch` into both is the twin that rule forbids, and the copy that
//! drifts is the one whose numbers are quietly not about the same thing.
//!
//! Nothing here is compiled into gunnar: `publish = false`, and this crate is
//! not a member of the parent workspace.
//!
//! # Running the harness
//!
//! ```text
//! # full-size, publishable numbers (machine = the real host)
//! CARGO_TARGET_DIR=~/scratch/cargo/gunnar-bench \
//!   NORNIR_MACHINE=desktop \
//!   cargo run --release --example nornir-bench --manifest-path bench/Cargo.toml
//!
//! # one arm
//! NORNIR_BENCH_ONLY=gunnar.fetch_readside cargo run --release --example nornir-bench
//!
//! # shrunken development loop (machine = <host>-preview, can never overwrite a real row)
//! NORNIR_BENCH_PREVIEW=1 NORNIR_MACHINE=desktop cargo run --release --example nornir-bench
//! ```

/// Timing the ref advertisement (v2 `ls-refs`) against a real store, counted —
/// `ls-remote` re-peeled every annotated tag. The diagnostic that prints it is
/// `examples/advertise-probe.rs`; [`advertise::probe`] is the driver, kept
/// separate so a persisted `gunnar.advertise_*` arm reuses it rather than twins it.
pub mod advertise;
/// Serving one fetch, with every object-store call counted.
///
/// `gunnar.page_cache` measures a day of fetches as page-cache residency, which
/// cannot tell an inflate from a page fault or a commit from a blob. This can:
/// it wraps the real [`ObjectStore`] the real [`UploadPack`] talks to, so
/// `read` / `header` / `contains` are separated, reads are split by object kind,
/// and `/proc/self/io` plus `mincore` bracket the same call.
/// Building a fixture repository as a packfile, which is the only shape an
/// engine that stores packs verbatim accepts.
pub mod packbuild;
pub mod readside;
/// gunnar's PKI for tests and benches: one CA named `trust`, a server
/// certificate and N client certificates. Behind `cfg(test)` because
/// `cert-helper` is a dev-dependency.
#[cfg(test)]
pub mod trust;

/// Serving one request, broken into named stages.
///
/// [`readside`] answers *what did this fetch ask the object store for*; this
/// answers *which stage asked, and what did that stage cost in wall, CPU,
/// allocation and peak*. They share the shape but not the question, and this
/// one needs `gunnar-wire`'s `stages` feature — which is why the manifest turns
/// it on and why nothing else in the tree does.
pub mod stages;

/// The `.idx` reader and the two candidate replacements for it, shared by
/// `gunnar.oid_index` and its verification sweep for the same one-copy reason
/// [`readside`] exists: the arm that times a structure and the check that proves
/// the structure is correct must be timing and proving the same code.
pub mod oid_index;

/// The `have` membership test: the negotiation-shaped follow-up to
/// [`oid_index`], sharing its structures rather than re-implementing them.
pub mod have_lookup;

/// The engine seam: **one door**, and today it is shut.
pub mod engine;

/// **How many bytes a repository's object store holds**, on either engine.
///
/// Here rather than in `examples/nornir-bench.rs` for the one-copy reason
/// [`readside`] is here: three arms need the same reading —
/// `gunnar.real_repo_clone`, `gunnar.vs_forge_fetch_gc` and
/// `gunnar.vs_forge_gc_orders` — and they live in two different files
/// (`examples/nornir-bench.rs` and `examples/forge/mod.rs`). Three copies of a
/// store-shape classifier is three chances for one of them to keep reading
/// `objects/pack` and publishing a 0 that looks like a tie.
pub mod store_bytes;

/// **The admission gate's refusal counters, read off the server's own log.**
///
/// The `vs_forge_*` concurrent arms clone against a real `gunnar serve`
/// container; this reads that server's `*.transfer.refused` lines to report the
/// in-flight peak and the refusal count per arm — counters those arms did not
/// publish before. Here rather than inline in `examples/forge/concurrent.rs`
/// for the same one-copy reason [`store_bytes`] is here: the arm that reports
/// the counter and the test that proves the parser reads the real
/// `gunnar_ssh::Admission` counter (not a fabricated 0) have to be the same
/// code, and a `#[test]` runs under `cargo test -p gunnar-bench`, not an example.
pub mod admission_log;

/// **`~` in a path literal, expanded against `$HOME`.**
///
/// Every scratch and target path in this repository is written as `~/…` rather
/// than as one developer's absolute home: a path with a username baked into it
/// is a path that is wrong for everybody else, and it does not belong in a
/// public tree. `~` is a SHELL convention, so nothing expands it inside a Rust
/// string — this re-export is how a `~/…` literal becomes a real path, and every
/// literal used as a filesystem path goes through it.
///
/// # Why this is a re-export, and why it was briefly a body
///
/// `nornir mimir analyze duplicates` ranked this cluster first — four copies in
/// this repository (`xtask/src/lib.rs`, here, `installtest/src/lib.rs`,
/// `deploy/upcloud/src/site.rs`), three byte-identical, each claiming to be
/// "the one place that does" it — and put the keep in
/// `edda/crates/nornir-xtask-support`, beside the std-only path primitives
/// (`target_dir`, `workspace_root_of`) that already live there.
///
/// Between 2026-09-10 and 2026-09-12 this was a cited COPY of the body, because
/// the re-export had been written against `nornir-xtask-support = "0.1.1"` and
/// 0.1.1 did not exist on crates.io (the bump was committed in edda as
/// `55984e1` but never published, and the only other road was a `../edda` path,
/// which `bench/Cargo.toml` forbids: gunnar builds from ONE clone). The publish
/// landed 2026-09-12 (`dwarves crate-release`, Gate B rehearsed), so the body
/// went back to being the crate's.
///
/// The four properties the copies had between them are still pinned right here
/// by [`expand_home_expands_only_a_leading_tilde_slash`] — now as proof that the
/// re-export does what this repository's 4 000-odd `~/…` literals assume: the
/// `~/` expansion, an absolute path passed through, a BARE `~` left alone (a
/// legal relative filename; expanding it would change what `./~` means), and
/// the `/root` fallback when `$HOME` is unset.
///
/// [`expand_home_expands_only_a_leading_tilde_slash`]: #
pub use nornir_xtask_support::expand_home;

#[cfg(test)]
mod expand_home_tests {
    use super::expand_home;
    use std::path::PathBuf;

    /// ★ **All four properties of `expand_home`, so the copy cannot drift from
    /// the crate it is standing in for.**
    ///
    /// Written the day the copy came back (2026-09-10). When
    /// `nornir-xtask-support 0.1.1` is published and the body above becomes a
    /// re-export again, this test stays: it then proves the re-export still
    /// does what this repository's 4 000-odd `~/…` literals assume, which is
    /// the property that actually matters to callers here.
    ///
    /// `$HOME` is set and unset with `std::env::set_var` in ONE test rather
    /// than two, because the environment is process-global and two tests
    /// touching it race under cargo's thread-per-test harness — the flake that
    /// costs an afternoon and gets "fixed" by deleting the assertion.
    #[test]
    fn expand_home_expands_only_a_leading_tilde_slash() {
        // SAFETY: single-threaded within this test, and no other test in this
        // crate reads or writes `HOME`.
        unsafe { std::env::set_var("HOME", "/home/someone") };

        // 1. the `~/` expansion — the whole reason the function exists.
        assert_eq!(
            expand_home("~/scratch/corpus"),
            PathBuf::from("/home/someone/scratch/corpus")
        );

        // 2. an absolute path passes straight through, so a caller may hand an
        //    override in without the function second-guessing it.
        assert_eq!(expand_home("/tmp/fixed"), PathBuf::from("/tmp/fixed"));
        assert_eq!(expand_home("relative/path"), PathBuf::from("relative/path"));

        // 3. a BARE `~` is left alone. `~` with no slash is a legal relative
        //    filename; expanding it would silently change what `./~` means.
        assert_eq!(expand_home("~"), PathBuf::from("~"));
        assert_eq!(expand_home("~notauser/x"), PathBuf::from("~notauser/x"));

        // 4. `$HOME` unset falls back to `/root` — the behaviour every one of
        //    the four copies had, and the one that bites in a systemd unit or a
        //    container that starts with no `HOME`.
        unsafe { std::env::remove_var("HOME") };
        assert_eq!(
            expand_home("~/.ssh/id_ed25519"),
            PathBuf::from("/root/.ssh/id_ed25519"),
            "no HOME must land in /root, not in a relative directory named `~`"
        );
    }
}