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

nordisk/gunnar

crates/gunnar-server/tests/push_spool_sweep.rs

//! **A spool a crash left behind is swept when the server starts.**
//!
//! A pushed pack of 64 MiB or more is spooled to `incoming-*.pack` beside the
//! repository's store while it arrives (`storage::read_pushed_pack_spooling`),
//! and the file is removed when the push lands or fails — by `Drop`. A process
//! that dies mid-receive runs no `Drop`: the spool stays, full-size, under a
//! name nothing else ever reads, and on a data set near its floor that is the
//! room the next push is refused for. cov-13's eight 512 MiB pushes were 4 GiB
//! of exactly such files at the moment the box ran out.
//!
//! `Server::start` sweeps them before the first door opens — both engines'
//! places (`<repo>.git/gunnar-git.znippy/` for znippy, `<repo>.git/` for gix) —
//! and leaves every other file alone.

use std::sync::Arc;

use gunnar_core::{NewRepo, RepoRef};
use gunnar_server::stores::ServerStores;

#[test]
fn spool_files_a_crash_left_are_swept_at_start_and_nothing_else_is_touched() {
    let dir = tempfile::tempdir().expect("tempdir");
    let stores = Arc::new(ServerStores::open(dir.path()).expect("stores"));
    stores
        .catalog()
        .create_namespace("team")
        .expect("namespace");
    let path = RepoRef::new("team", "one").expect("legal");
    let repo = stores
        .create_repo(NewRepo::new(path), false)
        .expect("create");
    let git_dir = stores
        .repo_store(&repo)
        .expect("open")
        .git_dir()
        .to_path_buf();

    let znippy = git_dir.join("gunnar-git.znippy");
    std::fs::create_dir_all(&znippy).unwrap();
    let left = [
        znippy.join("incoming-a1B2c3.pack"),
        git_dir.join("incoming-d4E5f6.pack"),
    ];
    for f in &left {
        std::fs::write(f, vec![7u8; 1 << 20]).unwrap();
    }
    // Not a spool: a name that merely contains the word, and a non-pack.
    let kept = [
        znippy.join("not-incoming-x.pack"),
        znippy.join("incoming-notes.txt"),
    ];
    for f in &kept {
        std::fs::write(f, b"keep me").unwrap();
    }

    // An LFS upload's spool beside the transaction log, left the same way.
    let lfs_spool = stores.txlog().dir().join(gunnar_core::txlog::SPOOL_DIR);
    std::fs::create_dir_all(&lfs_spool).unwrap();
    let lfs_left = lfs_spool.join("4e07408562bedb8b-123.part");
    std::fs::write(&lfs_left, vec![1u8; 4096]).unwrap();

    let runtime = tokio::runtime::Builder::new_multi_thread()
        .enable_all()
        .build()
        .expect("runtime");
    let config = gunnar_server::ServeConfig {
        no_grpc: true,
        ..gunnar_server::ServeConfig::ephemeral()
    };
    let server = runtime
        .block_on(gunnar_server::Server::start(Arc::clone(&stores), config))
        .expect("the server starts");

    for f in &left {
        assert!(
            !f.exists(),
            "a spool a crash left behind is still there after start: {}",
            f.display()
        );
    }
    assert!(
        !lfs_left.exists(),
        "an LFS upload's spool a crash left is still there after start"
    );
    for f in &kept {
        assert!(
            f.exists(),
            "the sweep removed a file that is not a spool: {}",
            f.display()
        );
    }
    let _ = runtime.block_on(server.shutdown());
}