Sausage: host, broker, commit-on-close filesystem, cargo actor, console, agent

Imported from the spike branch, with:
- a library crate (Host API) for the CLI and the tests
- JSON test scenarios for candidates
- commit on close only for new or truncated files
- a Nix flake: nix run -- init, nix run -- run
This commit is contained in:
2026-10-15 10:45:23 +01:00
commit 1f49d0f7bb
42 changed files with 7126 additions and 0 deletions
+45
View File
@@ -0,0 +1,45 @@
//! {"type":"bench","target":"echo","n":1000}: ping-pong with the target, one
//! message at a time, and report the round-trip times.
use serde_json::{Value, json};
use std::io::{BufRead, Write};
use std::time::Instant;
fn main() {
let mut out = std::io::stdout().lock();
let mut lines = std::io::stdin().lock().lines();
while let Some(Ok(line)) = lines.next() {
let Ok(msg) = serde_json::from_str::<Value>(&line) else { continue };
match msg["body"]["type"].as_str() {
Some("shutdown") => break,
Some("bench") => {
let target = msg["body"]["target"].as_str().unwrap_or("echo").to_string();
let n = msg["body"]["n"].as_u64().unwrap_or(1000);
let mut times = Vec::new();
let started = Instant::now();
for i in 0..n {
let t = Instant::now();
writeln!(out, "{}", json!({"to": target, "id": i, "body": {"type": "ping", "i": i}})).unwrap();
out.flush().unwrap();
// Wait for the reply.
loop {
let Some(Ok(l)) = lines.next() else { return };
let Ok(r) = serde_json::from_str::<Value>(&l) else { continue };
if r["reply_to"].as_str().is_some_and(|s| s.ends_with(&format!("/{i}"))) {
break;
}
}
times.push(t.elapsed().as_micros() as u64);
}
let total = started.elapsed().as_secs_f64();
times.sort();
let p = |q: f64| times[((times.len() as f64 - 1.0) * q) as usize];
let reply = json!({"type": "bench_result", "n": n, "total_s": total,
"msgs_per_s": (n as f64 / total).round(),
"p50_us": p(0.5), "p90_us": p(0.9), "p99_us": p(0.99), "max_us": p(1.0)});
writeln!(out, "{}", json!({"to": msg["from"], "reply_to": msg["id"], "body": reply})).unwrap();
out.flush().unwrap();
}
_ => {}
}
}
}