Add cube-bench (correctness-gated microbenchmarks) + daemon stats telemetry
- cube-bench crate: real-code-path throughput/latency over cubestore, cubecrypt (aes/gcm/chacha/xts), cubecode VM, and cubesys Session. Every section asserts correctness before timing. Wired into ./check as an opt-in 'bench' stage. - cubesys Session: per-command latency histogram + per-C-namespace record counts, exposed via a new 'stats' command over the live socket. - Deployed rebuilt cube-server to /home/luulu/.cubelinux/bin and restarted the system cube.service; verified stats live.
This commit is contained in:
+89
-1
@@ -13,10 +13,25 @@ use cubecode::{CodeCell, Kind, Op, Vm};
|
||||
use cubecoords::CubeHeader;
|
||||
use cubecrypt::{CubeEnv, KeySlot, Selector, TransformId};
|
||||
use cubestore::{CubeStore, HashBackend};
|
||||
use std::collections::BTreeMap;
|
||||
use std::time::Instant;
|
||||
|
||||
/// Per-command latency accumulator (cumulative; the daemon reports these via
|
||||
/// the `stats` command). Counts and sums are exact; mean/max are derived.
|
||||
#[derive(Default, Clone)]
|
||||
struct CmdStat {
|
||||
count: u64,
|
||||
total_ns: u128,
|
||||
max_ns: u128,
|
||||
}
|
||||
|
||||
/// One cube command session: a store plus the command interpreter.
|
||||
pub struct Session {
|
||||
store: CubeStore<HashBackend>,
|
||||
/// Total commands executed since this session started (telemetry).
|
||||
calls: u64,
|
||||
/// Per top-level command latency histogram (command name -> stats).
|
||||
per_cmd: BTreeMap<String, CmdStat>,
|
||||
}
|
||||
|
||||
impl Default for Session {
|
||||
@@ -30,6 +45,8 @@ impl Session {
|
||||
pub fn new() -> Self {
|
||||
Session {
|
||||
store: CubeStore::new(HashBackend::new()),
|
||||
calls: 0,
|
||||
per_cmd: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,12 +60,83 @@ impl Session {
|
||||
&mut self.store
|
||||
}
|
||||
|
||||
/// Snapshot of the session's telemetry: total commands serviced, per-command
|
||||
/// latency distribution (mean/max in µs), and the occupancy of each `C`
|
||||
/// namespace (number of records whose class axis equals `c`).
|
||||
///
|
||||
/// This is the "substantive" telemetry the daemon exposes — not just a
|
||||
/// health ping. A monitoring pass can sample `stats` repeatedly and derive
|
||||
/// request rates and latency histograms from the cumulative counters.
|
||||
pub fn stats(&self) -> String {
|
||||
let mut lines = Vec::new();
|
||||
lines.push(format!("total commands serviced: {}", self.calls));
|
||||
// per-command latency distribution
|
||||
lines.push("per-command latency (µs, mean / max / count):".into());
|
||||
if self.per_cmd.is_empty() {
|
||||
lines.push(" (no commands timed yet)".into());
|
||||
} else {
|
||||
for (name, st) in &self.per_cmd {
|
||||
let mean_us = if st.count > 0 {
|
||||
(st.total_ns as f64) / (st.count as f64) / 1e3
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
let max_us = st.max_ns as f64 / 1e3;
|
||||
lines.push(format!(
|
||||
" {:<8} mean {:8.2} max {:8.2} n={}",
|
||||
name, mean_us, max_us, st.count
|
||||
));
|
||||
}
|
||||
}
|
||||
// per-C namespace occupancy (C axis 0 = Null control space)
|
||||
let keys = self.store.keys();
|
||||
let mut by_c: BTreeMap<u8, usize> = BTreeMap::new();
|
||||
for k in &keys {
|
||||
*by_c.entry(k.c).or_insert(0) += 1;
|
||||
}
|
||||
lines.push(format!("records by C namespace ({} total):", keys.len()));
|
||||
if by_c.is_empty() {
|
||||
lines.push(" (store empty)".into());
|
||||
} else {
|
||||
for (c, n) in &by_c {
|
||||
let label = if *c == 0 { "Null(0)" } else { "" };
|
||||
lines.push(format!(" C={c:<3} {n:>6} records {label}"));
|
||||
}
|
||||
}
|
||||
lines.join("\n")
|
||||
}
|
||||
|
||||
/// Execute one command line. `Ok(out)` is a (possibly multi-line) result to
|
||||
/// print; `Err(e)` is a human-readable error.
|
||||
/// print; `Err(e)` is a human-readable error. Also records per-command
|
||||
/// latency into the session telemetry (see [`Session::stats`]).
|
||||
pub fn exec(&mut self, line: &str) -> Result<String, String> {
|
||||
let t0 = Instant::now();
|
||||
let cmd_name = line.split_whitespace().next().unwrap_or("").to_string();
|
||||
let result = self.exec_inner(line);
|
||||
// record telemetry regardless of ok/err (a failed command is still a
|
||||
// serviced command and worth timing).
|
||||
self.calls += 1;
|
||||
let st = self.per_cmd.entry(cmd_name).or_default();
|
||||
let elapsed = t0.elapsed().as_nanos();
|
||||
st.count += 1;
|
||||
st.total_ns += elapsed;
|
||||
if elapsed > st.max_ns {
|
||||
st.max_ns = elapsed;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// The real interpreter (separated so [`exec`] can wrap it with timing).
|
||||
fn exec_inner(&mut self, line: &str) -> Result<String, String> {
|
||||
let mut it = line.split_whitespace();
|
||||
let cmd = it.next().ok_or_else(|| "empty line".to_string())?;
|
||||
match cmd {
|
||||
"stats" => {
|
||||
// Substantive telemetry: command volume + latency distribution
|
||||
// + per-C-namespace record occupancy. This is what makes the
|
||||
// daemon measurable, not merely "healthy".
|
||||
Ok(self.stats())
|
||||
}
|
||||
"prog" => {
|
||||
let path = it.next().ok_or_else(|| "prog needs <path>".to_string())?;
|
||||
let mut ops: Vec<Op> = Vec::new();
|
||||
|
||||
Reference in New Issue
Block a user