holperf/src/seed.rs
//! Seed one store per format: the same package count, the same small and large
//! artifact sizes, a sealed znippy archive on the same disk (/home on oden).
use std::path::{Path, PathBuf};
use std::sync::Arc;
use anyhow::Result;
use traits::RepositoryBackendTrait;
use znippy_common::plugin::PluginRegistry;
use crate::gen;
use crate::harness::Scenario;
pub type Open = Box<dyn Fn() -> Arc<dyn RepositoryBackendTrait> + Send + Sync>;
/// Write `files` under `dir/src`, pack into `dir/store.znippy` (once; reused).
pub fn pack(
dir: &Path,
files: impl FnOnce() -> Vec<(String, Vec<u8>)>,
plugin: Option<PluginRegistry>,
) -> Result<PathBuf> {
let archive = dir.join("store.znippy");
if archive.exists() {
return Ok(archive);
}
let src = dir.join("src");
let _ = std::fs::remove_dir_all(&src);
for (p, b) in files() {
let f = src.join(&p);
std::fs::create_dir_all(f.parent().unwrap())?;
std::fs::write(f, b)?;
}
std::fs::create_dir_all(dir)?;
znippy_compress::compress_dir(&src, &archive, false, plugin.as_ref(), None, None)?;
std::fs::remove_dir_all(&src)?;
Ok(archive)
}
fn name(i: usize) -> String {
format!("pkg{i:05}")
}
/// The index requests cycle over these packages.
fn picks(n: usize) -> Vec<usize> {
(0..64).map(|k| (k * 7919) % n).collect()
}
pub fn want(only: Option<&[String]>, f: &str) -> bool {
only.is_none_or(|o| o.iter().any(|x| x == f))
}
pub fn all(
root: &Path,
n: usize,
small: usize,
large: usize,
only: Option<&[String]>,
) -> Result<Vec<Scenario>> {
let mut v = Vec::new();
let vers = ["1.0.0", "1.1.0"];
// ── Rust (sealed archive, the "drift" path) ──
if want(only, "rust") {
let dir = root.join(format!("rust-{n}"));
let a = pack(
&dir,
|| {
let mut f = Vec::new();
for i in 0..n {
for (j, ver) in vers.iter().enumerate() {
f.push((
format!("{}-{ver}.crate", name(i)),
gen::crate_file(&name(i), ver, small, (i * 2 + j) as u64),
));
}
}
f.push((
"bigpkg-1.0.0.crate".into(),
gen::crate_file("bigpkg", "1.0.0", large, 7),
));
f
},
None,
)?;
let open: Open = Box::new(move || {
Arc::new(r_rust::RustRepoZnippy::with_archive("rust".into(), a.clone()).unwrap())
});
v.push(Scenario {
format: "rust",
route: "rust".into(),
open,
dir,
index: picks(n)
.iter()
.map(|&i| format!("/rust/index/pk/g0/{}", name(i)))
.collect(),
meta2: vec!["/rust/index/config.json".into()],
small: picks(n)
.iter()
.map(|&i| format!("/rust/crates/{}/1.0.0/download", name(i)))
.collect(),
large: vec!["/rust/crates/bigpkg/1.0.0/download".into()],
small_size: small as u64,
large_size: large as u64,
packages: n,
});
}
// ── Rust writable (HotArchive, the /cache + /sparring path), seeded by put ──
if want(only, "rust-hot") {
let dir = root.join(format!("rusthot-{n}"));
let a = dir.join("hot.znippy");
if !a.exists() {
std::fs::create_dir_all(&dir)?;
let r = r_rust::RustRepoZnippy::with_writable_archive("rusthot".into(), a.clone());
for i in 0..n {
for (j, ver) in vers.iter().enumerate() {
let id = traits::ArtifactId {
namespace: None,
name: name(i),
version: (*ver).into(),
};
r.put(
&id,
&gen::crate_file(&name(i), ver, small, (i * 2 + j) as u64),
)?;
}
}
let id = traits::ArtifactId {
namespace: None,
name: "bigpkg".into(),
version: "1.0.0".into(),
};
r.put(&id, &gen::crate_file("bigpkg", "1.0.0", large, 7))?;
}
let open: Open = Box::new(move || {
Arc::new(r_rust::RustRepoZnippy::with_writable_archive(
"rusthot".into(),
a.clone(),
))
});
v.push(Scenario {
format: "rust-hot",
route: "rusthot".into(),
open,
dir,
index: picks(n)
.iter()
.map(|&i| format!("/rusthot/index/pk/g0/{}", name(i)))
.collect(),
meta2: vec![],
small: picks(n)
.iter()
.map(|&i| format!("/rusthot/crates/{}/1.0.0/download", name(i)))
.collect(),
large: vec!["/rusthot/crates/bigpkg/1.0.0/download".into()],
small_size: small as u64,
large_size: large as u64,
packages: n,
});
}
// ── Python / pip (PEP 503 + 691) ──
if want(only, "pip") {
let dir = root.join(format!("pip-{n}"));
let a = pack(
&dir,
|| {
let mut f = Vec::new();
for i in 0..n {
for (j, ver) in vers.iter().enumerate() {
f.push((
format!("packages/{0}/{0}-{ver}-py3-none-any.whl", name(i)),
gen::wheel(&name(i), ver, small, (i * 2 + j) as u64),
));
}
}
f.push((
"packages/bigpkg/bigpkg-1.0.0-py3-none-any.whl".into(),
gen::wheel("bigpkg", "1.0.0", large, 7),
));
f
},
None,
)?;
let open: Open = Box::new(move || {
Arc::new(r_python::PipRepoZnippy::with_archive("pypi".into(), a.clone()).unwrap())
});
v.push(Scenario {
format: "pip",
route: "pypi".into(),
open,
dir,
index: picks(n)
.iter()
.map(|&i| format!("/pypi/simple/{}/", name(i)))
.collect(),
meta2: vec!["/pypi/simple/".into()],
small: picks(n)
.iter()
.map(|&i| format!("/pypi/packages/{0}/{0}-1.0.0-py3-none-any.whl", name(i)))
.collect(),
large: vec!["/pypi/packages/bigpkg/bigpkg-1.0.0-py3-none-any.whl".into()],
small_size: small as u64,
large_size: large as u64,
packages: n,
});
}
// ── Maven (no typed view: the chain-view live path) and Maven with the plugin view ──
for (fmt, plugin) in [("maven", false), ("maven-view", true)] {
if !want(only, fmt) {
continue;
}
let dir = root.join(format!("{fmt}-{n}"));
let reg = plugin
.then(|| PluginRegistry::with_plugin(Box::new(znippy_plugin_maven::NativeMavenPlugin)));
let a = pack(
&dir,
|| {
let mut f = Vec::new();
for i in 0..n {
for (j, ver) in vers.iter().enumerate() {
let base = format!("org/bench/{0}/{ver}/{0}-{ver}", name(i));
f.push((
format!("{base}.jar"),
gen::jar("org.bench", &name(i), ver, small, (i * 2 + j) as u64),
));
f.push((
format!("{base}.pom"),
gen::pom("org.bench", &name(i), ver).into_bytes(),
));
}
}
f.push((
"org/bench/bigpkg/1.0.0/bigpkg-1.0.0.jar".into(),
gen::jar("org.bench", "bigpkg", "1.0.0", large, 7),
));
f.push((
"org/bench/bigpkg/1.0.0/bigpkg-1.0.0.pom".into(),
gen::pom("org.bench", "bigpkg", "1.0.0").into_bytes(),
));
f
},
reg,
)?;
let open: Open = Box::new(move || {
Arc::new(r_maven::MavenRepoZnippy::with_archive("maven".into(), a.clone()).unwrap())
});
v.push(Scenario {
format: fmt,
route: "maven".into(),
open,
dir,
index: picks(n)
.iter()
.map(|&i| format!("/maven/org/bench/{}/maven-metadata.xml", name(i)))
.collect(),
meta2: picks(n)
.iter()
.map(|&i| format!("/maven/org/bench/{0}/1.0.0/{0}-1.0.0.jar.sha1", name(i)))
.collect(),
small: picks(n)
.iter()
.map(|&i| format!("/maven/org/bench/{0}/1.0.0/{0}-1.0.0.jar", name(i)))
.collect(),
large: vec!["/maven/org/bench/bigpkg/1.0.0/bigpkg-1.0.0.jar".into()],
small_size: small as u64,
large_size: large as u64,
packages: n,
});
}
// ── npm (packument needs the typed view: NpmPlugin) ──
if want(only, "npm") {
let dir = root.join(format!("npm-{n}"));
let reg = PluginRegistry::with_plugin(Box::new(
znippy_common::plugins::native::npm_native::NpmPlugin,
));
let a = pack(
&dir,
|| {
let mut f = Vec::new();
for i in 0..n {
for (j, ver) in vers.iter().enumerate() {
f.push((
format!("{0}/-/{0}-{ver}.tgz", name(i)),
gen::npm_tgz(&name(i), ver, small, (i * 2 + j) as u64),
));
}
}
f.push((
"bigpkg/-/bigpkg-1.0.0.tgz".into(),
gen::npm_tgz("bigpkg", "1.0.0", large, 7),
));
f
},
Some(reg),
)?;
let open: Open = Box::new(move || {
Arc::new(r_npm::NpmRepoZnippy::with_archive("npm".into(), a.clone()).unwrap())
});
v.push(Scenario {
format: "npm",
route: "npm".into(),
open,
dir,
index: picks(n)
.iter()
.map(|&i| format!("/npm/{}", name(i)))
.collect(),
meta2: vec!["/npm/bigpkg".into()],
small: picks(n)
.iter()
.map(|&i| format!("/npm/{0}/-/{0}-1.0.0.tgz", name(i)))
.collect(),
large: vec!["/npm/bigpkg/-/bigpkg-1.0.0.tgz".into()],
small_size: small as u64,
large_size: large as u64,
packages: n,
});
}
// ── Go (GOPROXY) ──
if want(only, "go") {
let dir = root.join(format!("go-{n}"));
let a = pack(
&dir,
|| {
let mut f = Vec::new();
let mut add = |m: &str, ver: &str, size: usize, seed: u64| {
f.push((format!("{m}/@v/{ver}.zip"), gen::go_zip(m, ver, size, seed)));
f.push((
format!("{m}/@v/{ver}.mod"),
format!("module {m}\n").into_bytes(),
));
f.push((
format!("{m}/@v/{ver}.info"),
format!("{{\"Version\":\"{ver}\",\"Time\":\"2026-01-01T00:00:00Z\"}}")
.into_bytes(),
));
};
for i in 0..n {
for (j, ver) in ["v1.0.0", "v1.1.0"].iter().enumerate() {
add(
&format!("example.com/m/{}", name(i)),
ver,
small,
(i * 2 + j) as u64,
);
}
}
add("example.com/m/bigpkg", "v1.0.0", large, 7);
f
},
None,
)?;
let open: Open = Box::new(move || {
Arc::new(r_go::GoRepoZnippy::with_archive("go".into(), a.clone()).unwrap())
});
v.push(Scenario {
format: "go",
route: "go".into(),
open,
dir,
index: picks(n)
.iter()
.map(|&i| format!("/go/example.com/m/{}/@v/list", name(i)))
.collect(),
meta2: picks(n)
.iter()
.map(|&i| format!("/go/example.com/m/{}/@v/v1.0.0.info", name(i)))
.collect(),
small: picks(n)
.iter()
.map(|&i| format!("/go/example.com/m/{}/@v/v1.0.0.zip", name(i)))
.collect(),
large: vec!["/go/example.com/m/bigpkg/@v/v1.0.0.zip".into()],
small_size: small as u64,
large_size: large as u64,
packages: n,
});
}
crate::seed2::more(root, n, small, large, only, &mut v)?;
Ok(v)
}