metrale_bench/benchmarks/scheduler_equivalence/
host.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! 2026-09-26: The seam the scheduler-equivalence gate re-serves the checkpoint
4//! through: the serving process installs a [`RouterHost`], because this crate
5//! starts no server.
6//!
7//! Owner: bench, scheduler-equivalence gate.
8//! Invariants: none beyond the types.
9
10use std::collections::BTreeMap;
11use std::sync::Arc;
12
13use anyhow::Result;
14use futures::future::BoxFuture;
15use parking_lot::RwLock;
16
17use crate::plugin::TargetEndpoint;
18
19/// 2026-09-26: The device router a serve runs under: the `--scheduler-config` value.
20#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
21pub enum Router {
22    Sync,
23    Async,
24}
25
26impl Router {
27    pub fn flag_value(self) -> &'static str {
28        match self {
29            Router::Sync => "sync",
30            Router::Async => "async",
31        }
32    }
33}
34
35/// 2026-09-26: The speculation lane a serve is pinned to.
36#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
37pub enum Lane {
38    /// 2026-09-26: No `--speculative`: plain decode only.
39    SpecOff,
40    /// 2026-09-26: `--speculative --mtp-gate force`: no runtime MTP gate is
41    /// built, so the MTP step runs whenever speculation is eligible.
42    MtpForce,
43}
44
45impl Lane {
46    pub fn label(self) -> &'static str {
47        match self {
48            Lane::SpecOff => "spec-off",
49            Lane::MtpForce => "mtp-force",
50        }
51    }
52}
53
54/// 2026-09-26: One serve the host brings up: a router under a lane.
55#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
56pub struct ServeVariant {
57    pub router: Router,
58    pub lane: Lane,
59}
60
61impl ServeVariant {
62    pub fn label(self) -> String {
63        format!("{}/{}", self.router.flag_value(), self.lane.label())
64    }
65}
66
67/// 2026-09-26: Re-serves the current checkpoint under a variant; implemented by
68/// the serving process.
69pub trait RouterHost: Send + Sync {
70    /// 2026-09-26: Re-serve the current checkpoint under `variant` and return
71    /// the endpoint once it answers.
72    fn serve(&self, variant: ServeVariant) -> BoxFuture<'_, Result<TargetEndpoint>>;
73
74    /// 2026-09-26: The asynchronous router's cumulative counters. The driver
75    /// subtracts the reading before a lane's async legs from the one after.
76    /// A host without counters returns an empty map.
77    fn diagnostics(&self) -> Result<BTreeMap<String, f64>>;
78
79    /// 2026-09-26: Put back whatever was serving before the gate started.
80    /// Called from the benchmark's `cleanup`.
81    fn restore(&self) -> BoxFuture<'_, Result<()>>;
82}
83
84/// 2026-09-26: The installed host. A static because the registry builds a
85/// benchmark from `BenchmarkDescriptor::ctor`, a `fn()` that takes no handle;
86/// `serve_matrix::host` holds its host the same way.
87static HOST: RwLock<Option<Arc<dyn RouterHost>>> = RwLock::new(None);
88
89/// 2026-09-26: The serving process calls this as it starts; a later call
90/// replaces the earlier host.
91pub fn install(host: Arc<dyn RouterHost>) {
92    *HOST.write() = Some(host);
93}
94
95pub fn installed() -> Option<Arc<dyn RouterHost>> {
96    HOST.read().clone()
97}
98
99/// 2026-09-26: What to tell the operator when nothing is installed.
100pub const NO_HOST: &str = "the scheduler-equivalence gate needs the Metrale Engine server that \
101    serves the checkpoint: it re-serves the model under each router itself. Run it from the \
102    dashboard's Benchmarks pane, or headless with `met bench run scheduler-equivalence \
103    --pull-request-gate`, which starts the server in this process. Pointing it at a foreign \
104    endpoint with --url cannot work: the endpoint would stay on one router.";