mirror of
https://github.com/marcoallegretti/karapace.git
synced 2026-03-27 14:03:09 +00:00
- Engine: init → resolve → lock → build → enter/exec → freeze → archive → destroy - Cached store_root_str avoiding repeated to_string_lossy() allocations - WAL-protected build, enter, exec, destroy, commit, restore, GC operations - Overlay drift detection: diff/commit/export via upper_dir scanning - Deterministic snapshot commit with composite identity hashing - Atomic restore via staging directory swap - StoreLock: compile-time enforcement via type parameter on gc() - Signal handler: SIGINT/SIGTERM graceful shutdown with AtomicBool - Push/pull delegation to karapace-remote backend - Crash recovery: stale .running marker cleanup on Engine::new() - Integration tests, E2E tests, crash injection tests, ENOSPC simulation - Criterion benchmarks: build, rebuild, commit, restore, GC, verify
202 lines
7 KiB
Rust
202 lines
7 KiB
Rust
use criterion::{criterion_group, criterion_main, Criterion};
|
|
use std::fs;
|
|
use std::path::Path;
|
|
|
|
fn create_test_manifest(dir: &Path) -> std::path::PathBuf {
|
|
let manifest_path = dir.join("karapace.toml");
|
|
fs::write(
|
|
&manifest_path,
|
|
r#"
|
|
manifest_version = 1
|
|
[base]
|
|
image = "rolling"
|
|
[system]
|
|
packages = ["git", "clang"]
|
|
[runtime]
|
|
backend = "mock"
|
|
"#,
|
|
)
|
|
.unwrap();
|
|
manifest_path
|
|
}
|
|
|
|
fn bench_build(c: &mut Criterion) {
|
|
c.bench_function("engine_build_mock_2pkg", |b| {
|
|
b.iter_with_setup(
|
|
|| {
|
|
let store_dir = tempfile::tempdir().unwrap();
|
|
let project_dir = tempfile::tempdir().unwrap();
|
|
let manifest = create_test_manifest(project_dir.path());
|
|
let engine = karapace_core::Engine::new(store_dir.path());
|
|
(store_dir, project_dir, manifest, engine)
|
|
},
|
|
|(_sd, _pd, manifest, engine)| {
|
|
engine.build(&manifest).unwrap();
|
|
},
|
|
);
|
|
});
|
|
}
|
|
|
|
fn bench_rebuild_unchanged(c: &mut Criterion) {
|
|
c.bench_function("engine_rebuild_unchanged", |b| {
|
|
b.iter_with_setup(
|
|
|| {
|
|
let store_dir = tempfile::tempdir().unwrap();
|
|
let project_dir = tempfile::tempdir().unwrap();
|
|
let manifest = create_test_manifest(project_dir.path());
|
|
let engine = karapace_core::Engine::new(store_dir.path());
|
|
let result = engine.build(&manifest).unwrap();
|
|
(store_dir, project_dir, manifest, engine, result)
|
|
},
|
|
|(_sd, _pd, manifest, engine, _result)| {
|
|
engine.build(&manifest).unwrap();
|
|
},
|
|
);
|
|
});
|
|
}
|
|
|
|
fn bench_commit(c: &mut Criterion) {
|
|
c.bench_function("engine_commit_100files", |b| {
|
|
b.iter_with_setup(
|
|
|| {
|
|
let store_dir = tempfile::tempdir().unwrap();
|
|
let project_dir = tempfile::tempdir().unwrap();
|
|
let manifest = create_test_manifest(project_dir.path());
|
|
let engine = karapace_core::Engine::new(store_dir.path());
|
|
let result = engine.build(&manifest).unwrap();
|
|
let env_id = result.identity.env_id.to_string();
|
|
|
|
// Create 100 files in the upper directory to simulate drift
|
|
let upper = store_dir.path().join("env").join(&env_id).join("upper");
|
|
fs::create_dir_all(&upper).unwrap();
|
|
for i in 0..100 {
|
|
fs::write(
|
|
upper.join(format!("file_{i:03}.txt")),
|
|
format!("content {i}"),
|
|
)
|
|
.unwrap();
|
|
}
|
|
|
|
(store_dir, project_dir, engine, env_id)
|
|
},
|
|
|(_sd, _pd, engine, env_id)| {
|
|
engine.commit(&env_id).unwrap();
|
|
},
|
|
);
|
|
});
|
|
}
|
|
|
|
fn bench_restore(c: &mut Criterion) {
|
|
c.bench_function("engine_restore_snapshot", |b| {
|
|
b.iter_with_setup(
|
|
|| {
|
|
let store_dir = tempfile::tempdir().unwrap();
|
|
let project_dir = tempfile::tempdir().unwrap();
|
|
let manifest = create_test_manifest(project_dir.path());
|
|
let engine = karapace_core::Engine::new(store_dir.path());
|
|
let result = engine.build(&manifest).unwrap();
|
|
let env_id = result.identity.env_id.to_string();
|
|
|
|
// Create files and commit a snapshot
|
|
let upper = store_dir.path().join("env").join(&env_id).join("upper");
|
|
fs::create_dir_all(&upper).unwrap();
|
|
for i in 0..50 {
|
|
fs::write(
|
|
upper.join(format!("file_{i:03}.txt")),
|
|
format!("content {i}"),
|
|
)
|
|
.unwrap();
|
|
}
|
|
let snapshot_hash = engine.commit(&env_id).unwrap();
|
|
|
|
(store_dir, project_dir, engine, env_id, snapshot_hash)
|
|
},
|
|
|(_sd, _pd, engine, env_id, snapshot_hash)| {
|
|
engine.restore(&env_id, &snapshot_hash).unwrap();
|
|
},
|
|
);
|
|
});
|
|
}
|
|
|
|
fn bench_gc(c: &mut Criterion) {
|
|
c.bench_function("gc_50envs", |b| {
|
|
b.iter_with_setup(
|
|
|| {
|
|
let store_dir = tempfile::tempdir().unwrap();
|
|
let layout = karapace_store::StoreLayout::new(store_dir.path());
|
|
layout.initialize().unwrap();
|
|
let meta_store = karapace_store::MetadataStore::new(layout.clone());
|
|
let obj_store = karapace_store::ObjectStore::new(layout.clone());
|
|
|
|
// Create 50 environments: 25 live (ref_count=1), 25 dead (ref_count=0)
|
|
for i in 0..50 {
|
|
let obj_hash = obj_store.put(format!("obj-{i}").as_bytes()).unwrap();
|
|
let meta = karapace_store::EnvMetadata {
|
|
env_id: format!("env_{i:04}").into(),
|
|
short_id: format!("env_{i:04}").into(),
|
|
name: None,
|
|
state: karapace_store::EnvState::Built,
|
|
manifest_hash: obj_hash.into(),
|
|
base_layer: "".into(),
|
|
dependency_layers: vec![],
|
|
policy_layer: None,
|
|
created_at: "2026-01-01T00:00:00Z".to_owned(),
|
|
updated_at: "2026-01-01T00:00:00Z".to_owned(),
|
|
ref_count: u32::from(i < 25),
|
|
checksum: None,
|
|
};
|
|
meta_store.put(&meta).unwrap();
|
|
}
|
|
|
|
// Create 200 orphan objects
|
|
for i in 0..200 {
|
|
obj_store
|
|
.put(format!("orphan-object-{i}").as_bytes())
|
|
.unwrap();
|
|
}
|
|
|
|
(store_dir, layout)
|
|
},
|
|
|(_sd, layout)| {
|
|
let gc = karapace_store::GarbageCollector::new(layout);
|
|
gc.collect(false).unwrap();
|
|
},
|
|
);
|
|
});
|
|
}
|
|
|
|
fn bench_verify_store(c: &mut Criterion) {
|
|
c.bench_function("verify_store_200objects", |b| {
|
|
b.iter_with_setup(
|
|
|| {
|
|
let store_dir = tempfile::tempdir().unwrap();
|
|
let layout = karapace_store::StoreLayout::new(store_dir.path());
|
|
layout.initialize().unwrap();
|
|
let obj_store = karapace_store::ObjectStore::new(layout.clone());
|
|
|
|
// Create 200 objects
|
|
for i in 0..200 {
|
|
obj_store
|
|
.put(format!("verify-object-{i}").as_bytes())
|
|
.unwrap();
|
|
}
|
|
|
|
(store_dir, layout)
|
|
},
|
|
|(_sd, layout)| {
|
|
karapace_store::verify_store_integrity(&layout).unwrap();
|
|
},
|
|
);
|
|
});
|
|
}
|
|
|
|
criterion_group!(
|
|
benches,
|
|
bench_build,
|
|
bench_rebuild_unchanged,
|
|
bench_commit,
|
|
bench_restore,
|
|
bench_gc,
|
|
bench_verify_store,
|
|
);
|
|
criterion_main!(benches);
|