use std::collections::HashMap;
pub(crate) const HISTORY_WINDOW: usize = 512;
pub(crate) const HISTORY_ROW_CAP: usize = 256;
pub(crate) const HISTORY_TENANT_FLOOR: usize = 20;
pub(crate) const CTX_HAT_SLACK: u64 = 8;
pub(crate) struct ShadowConfig {
pub armed: bool,
pub budget_bytes: Option<u64>,
exempt: Vec<String>,
}
impl ShadowConfig {
pub(crate) fn from_env() -> Self {
let armed = std::env::var("MEMRA_ADMIT_PREDICT_SHADOW").is_ok_and(|v| v != "0");
let budget_bytes = std::env::var("MEMRA_ADMIT_PREDICT_BUDGET_MB")
.ok()
.and_then(|v| v.parse::<u64>().ok())
.map(|mb| mb.saturating_mul(1 << 20));
let exempt = std::env::var("MEMRA_ADMIT_PREDICT_EXEMPT_TENANTS")
.ok()
.map(|v| Self::parse_exempt(&v))
.unwrap_or_default();
if armed {
eprintln!(
"[admit-predict] shadow armed: budget_bytes={} exempt_tenants={} \
(logging only, nothing is rejected; enforcement is a separate flip)",
budget_bytes.map_or("unset".into(), |b| b.to_string()),
exempt.len(),
);
}
ShadowConfig {
armed,
budget_bytes,
exempt,
}
}
fn parse_exempt(raw: &str) -> Vec<String> {
raw.split(',')
.map(str::trim)
.filter(|t| !t.is_empty())
.map(str::to_string)
.collect()
}
pub(crate) fn is_exempt(&self, tenant_row: &str) -> bool {
let bare = tenant_row.strip_prefix("t:").unwrap_or(tenant_row);
self.exempt.iter().any(|t| t == bare)
}
}
#[derive(Default, Clone, Copy)]
struct ModelBook {
inflight: u64,
booked_bytes: u64,
shadow_booked_bytes: u64,
}
#[derive(Default)]
pub(crate) struct AdmissionBook {
models: HashMap<String, ModelBook>,
}
impl AdmissionBook {
pub(crate) fn admit(&mut self, model: &str, booked_bytes: u64, shadow_kv_hat: u64) {
let row = self.models.entry(model.to_string()).or_default();
row.inflight += 1;
row.booked_bytes = row.booked_bytes.saturating_add(booked_bytes);
row.shadow_booked_bytes = row.shadow_booked_bytes.saturating_add(shadow_kv_hat);
}
pub(crate) fn retire(&mut self, model: &str, booked_bytes: u64, shadow_kv_hat: u64) {
if let Some(row) = self.models.get_mut(model) {
row.inflight = row.inflight.saturating_sub(1);
row.booked_bytes = row.booked_bytes.saturating_sub(booked_bytes);
row.shadow_booked_bytes = row.shadow_booked_bytes.saturating_sub(shadow_kv_hat);
}
}
pub(crate) fn inflight(&self, model: &str) -> u64 {
self.models.get(model).map_or(0, |row| row.inflight)
}
pub(crate) fn shadow_booked_total(&self) -> u64 {
self.models
.values()
.map(|row| row.shadow_booked_bytes)
.sum()
}
pub(crate) fn inflight_snapshot(&self) -> HashMap<String, u64> {
self.models
.iter()
.map(|(model, row)| (model.clone(), row.inflight))
.collect()
}
pub(crate) fn booked_snapshot(&self) -> HashMap<String, u64> {
self.models
.iter()
.map(|(model, row)| (model.clone(), row.booked_bytes))
.collect()
}
}
struct Ring {
buf: Vec<u32>,
next: usize,
len: usize,
}
impl Ring {
fn new() -> Self {
Ring {
buf: vec![0; HISTORY_WINDOW],
next: 0,
len: 0,
}
}
fn push(&mut self, sample: u32) {
self.buf[self.next] = sample;
self.next = (self.next + 1) % HISTORY_WINDOW;
self.len = (self.len + 1).min(HISTORY_WINDOW);
}
}
#[derive(Default)]
pub(crate) struct CompletionHistory {
rows: HashMap<(String, String), Ring>,
global: HashMap<String, Ring>,
scratch: Vec<u32>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum LhatSource {
TenantP95,
GlobalP95,
MaxtokFallback,
}
impl LhatSource {
pub(crate) fn as_str(self) -> &'static str {
match self {
LhatSource::TenantP95 => "tenant-p95",
LhatSource::GlobalP95 => "global-p95",
LhatSource::MaxtokFallback => "maxtok-fallback",
}
}
}
impl CompletionHistory {
pub(crate) fn record(&mut self, tenant_row: &str, model: &str, completion_tokens: u32) {
self.global
.entry(model.to_string())
.or_insert_with(Ring::new)
.push(completion_tokens);
let key = (tenant_row.to_string(), model.to_string());
if let Some(ring) = self.rows.get_mut(&key) {
ring.push(completion_tokens);
return;
}
if self.rows.len() < HISTORY_ROW_CAP {
self.rows
.entry(key)
.or_insert_with(Ring::new)
.push(completion_tokens);
}
}
fn p95(scratch: &mut Vec<u32>, ring: &Ring) -> u32 {
scratch.clear();
scratch.extend_from_slice(&ring.buf[..ring.len]);
let n = scratch.len();
debug_assert!(n > 0, "p95 caller checks emptiness");
let rank = ((n as f64) * 0.95).ceil() as usize; let idx = rank.clamp(1, n) - 1;
let (_, value, _) = scratch.select_nth_unstable(idx);
*value
}
pub(crate) fn lhat(
&mut self,
tenant_row: &str,
model: &str,
max_tokens: Option<u64>,
) -> (u64, LhatSource) {
let key = (tenant_row.to_string(), model.to_string());
let predicted = match self.rows.get(&key) {
Some(ring) if ring.len >= HISTORY_TENANT_FLOOR => Some((
Self::p95(&mut self.scratch, ring) as u64,
LhatSource::TenantP95,
)),
_ => match self.global.get(model) {
Some(ring) if ring.len > 0 => Some((
Self::p95(&mut self.scratch, ring) as u64,
LhatSource::GlobalP95,
)),
_ => None,
},
};
match (predicted, max_tokens) {
(Some((p95, source)), Some(bound)) => (p95.min(bound), source),
(Some((p95, source)), None) => (p95, source),
(None, bound) => (bound.unwrap_or(0), LhatSource::MaxtokFallback),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Verdict {
Admit,
RejectSlot,
RejectKv,
}
impl Verdict {
pub(crate) fn as_str(self) -> &'static str {
match self {
Verdict::Admit => "admit",
Verdict::RejectSlot => "reject-slot",
Verdict::RejectKv => "reject-kv",
}
}
}
pub(crate) fn kv_hat(
prompt_tokens: u64,
predicted_completion: u64,
bytes_per_token: u64,
fixed_bytes: u64,
) -> u64 {
bytes_per_token
.saturating_mul(
prompt_tokens
.saturating_add(predicted_completion)
.saturating_add(CTX_HAT_SLACK),
)
.saturating_add(fixed_bytes)
}
pub(crate) fn earliest_completion_retry_s(
inflight: impl Iterator<Item = (u64, u64)>, p50_step_ms: f32,
) -> Option<u64> {
if p50_step_ms <= 0.0 {
return None;
}
inflight
.map(|(predicted_total, generated)| predicted_total.saturating_sub(generated))
.min()
.map(|remaining_tokens| {
let secs = (remaining_tokens as f64 * p50_step_ms as f64 / 1000.0).ceil() as u64;
secs.clamp(1, 60)
})
}
pub(crate) struct VerdictLine<'a> {
pub request_id: &'a str,
pub tenant_row: &'a str,
pub model: &'a str,
pub verdict: Verdict,
pub reason: LhatSource,
pub prompt_tokens: Option<u64>,
pub predicted_completion: u64,
pub kv_hat_bytes: Option<u64>,
pub booked_bytes: u64,
pub inflight: u64,
pub cap: u64,
pub budget_bytes: Option<u64>,
pub retry_after_s: Option<u64>,
pub exempt: bool,
}
pub(crate) fn shadow_verdict_line(line: &VerdictLine<'_>) -> String {
format!(
"[admit-predict] id={} tenant={:?} model={:?} verdict={} reason={} prompt={} \
predicted_completion={} kv_hat={} booked_bytes={} inflight={} cap={} \
budget_bytes={} retry_after_s={} exempt={}",
line.request_id,
line.tenant_row,
line.model,
line.verdict.as_str(),
line.reason.as_str(),
line.prompt_tokens.map_or("-".into(), |v| v.to_string()),
line.predicted_completion,
line.kv_hat_bytes.map_or("-".into(), |v| v.to_string()),
line.booked_bytes,
line.inflight,
line.cap,
line.budget_bytes.map_or("unset".into(), |v| v.to_string()),
line.retry_after_s.map_or("-".into(), |v| v.to_string()),
u8::from(line.exempt),
)
}
#[cfg(test)]
mod tests {
use super::*;
fn line(verdict: Verdict) -> VerdictLine<'static> {
VerdictLine {
request_id: "chatcmpl-abc123",
tenant_row: "t:acme",
model: "qwen/qwen3.8-27b",
verdict,
reason: LhatSource::TenantP95,
prompt_tokens: Some(1200),
predicted_completion: 2906,
kv_hat_bytes: Some(130_000_000),
booked_bytes: 9_000_000_000,
inflight: 7,
cap: 32,
budget_bytes: Some(35_423 << 20),
retry_after_s: Some(4),
exempt: false,
}
}
#[test]
fn verdict_line_locks_fields() {
let s = shadow_verdict_line(&line(Verdict::RejectKv));
assert!(s.starts_with("[admit-predict] "), "grep-stable prefix: {s}");
for field in [
"id=chatcmpl-abc123",
"tenant=\"t:acme\"",
"model=\"qwen/qwen3.8-27b\"",
"verdict=reject-kv",
"reason=tenant-p95",
"prompt=1200",
"predicted_completion=2906",
"kv_hat=130000000",
"booked_bytes=9000000000",
"inflight=7",
"cap=32",
"budget_bytes=37143707648",
"retry_after_s=4",
"exempt=0",
] {
assert!(s.contains(field), "line must carry `{field}`: {s}");
}
assert_eq!(s.lines().count(), 1);
}
#[test]
fn verdict_line_optional_fields_render_placeholders() {
let mut l = line(Verdict::RejectSlot);
l.prompt_tokens = None;
l.kv_hat_bytes = None;
l.budget_bytes = None;
l.retry_after_s = None;
l.exempt = true;
let s = shadow_verdict_line(&l);
for field in [
"verdict=reject-slot",
"prompt=-",
"kv_hat=-",
"budget_bytes=unset",
"retry_after_s=-",
"exempt=1",
] {
assert!(s.contains(field), "line must carry `{field}`: {s}");
}
}
#[test]
fn book_admit_retire_round_trip() {
let mut book = AdmissionBook::default();
book.admit("m", 100, 40);
book.admit("m", 50, 10);
book.admit("other", 7, 3);
assert_eq!(book.inflight("m"), 2);
assert_eq!(book.booked_snapshot()["m"], 150);
assert_eq!(book.inflight_snapshot()["m"], 2);
assert_eq!(book.shadow_booked_total(), 53);
book.retire("m", 100, 40);
assert_eq!(book.inflight("m"), 1);
assert_eq!(book.booked_snapshot()["m"], 50);
assert_eq!(book.shadow_booked_total(), 13);
book.retire("m", 50, 10);
book.retire("other", 7, 3);
assert_eq!(book.inflight("m"), 0);
assert_eq!(book.shadow_booked_total(), 0);
book.retire("m", 1, 1);
assert_eq!(book.inflight("m"), 0);
}
#[test]
fn history_tenant_floor_then_tenant_p95() {
let mut h = CompletionHistory::default();
for i in 0..HISTORY_TENANT_FLOOR - 1 {
h.record("t:a", "m", 100 + i as u32);
}
h.record("t:b", "m", 9_000); let (lhat, source) = h.lhat("t:a", "m", None);
assert_eq!(source, LhatSource::GlobalP95);
assert!(lhat >= 118, "global p95 sees the whale: {lhat}");
h.record("t:a", "m", 200);
let (lhat, source) = h.lhat("t:a", "m", None);
assert_eq!(source, LhatSource::TenantP95);
assert_eq!(lhat, 118, "nearest-rank p95 of {{100..=118, 200}}");
}
#[test]
fn history_clips_to_max_tokens_and_cold_start_falls_back() {
let mut h = CompletionHistory::default();
for _ in 0..HISTORY_TENANT_FLOOR {
h.record("t:a", "m", 5_000);
}
let (lhat, source) = h.lhat("t:a", "m", Some(256));
assert_eq!(
(lhat, source),
(256, LhatSource::TenantP95),
"clip to bound"
);
let (lhat, source) = h.lhat("t:zzz", "unknown-model", Some(1024));
assert_eq!((lhat, source), (1024, LhatSource::MaxtokFallback));
let (lhat, source) = h.lhat("t:zzz", "unknown-model", None);
assert_eq!((lhat, source), (0, LhatSource::MaxtokFallback));
}
#[test]
fn history_ring_is_rolling_and_rows_are_bounded() {
let mut h = CompletionHistory::default();
for _ in 0..HISTORY_WINDOW {
h.record("t:a", "m", 10);
}
for _ in 0..HISTORY_WINDOW {
h.record("t:a", "m", 20);
}
let (lhat, _) = h.lhat("t:a", "m", None);
assert_eq!(lhat, 20, "the whole window rolled over");
let mut h = CompletionHistory::default();
for i in 0..HISTORY_ROW_CAP {
h.record(&format!("t:{i}"), "m", 100);
}
assert_eq!(h.rows.len(), HISTORY_ROW_CAP);
h.record("t:overflow", "m", 100);
assert_eq!(h.rows.len(), HISTORY_ROW_CAP, "row map is bounded");
for _ in 0..HISTORY_TENANT_FLOOR + 5 {
h.record("t:overflow", "m", 300);
}
let (_, source) = h.lhat("t:overflow", "m", None);
assert_eq!(
source,
LhatSource::GlobalP95,
"an over-cap tenant predicts through the global arm"
);
}
#[test]
fn kv_hat_matches_contract_arithmetic() {
assert_eq!(kv_hat(100, 50, 1_000, 7), 1_000 * (100 + 50 + 8) + 7);
assert_eq!(kv_hat(u64::MAX, u64::MAX, 2, 1), u64::MAX);
}
#[test]
fn retry_hint_is_earliest_completion_clamped_to_shed_window() {
let hint = earliest_completion_retry_s([(150, 50), (30, 25)].into_iter(), 200.0);
assert_eq!(hint, Some(1), "5 tokens x 0.2 s = 1 s ceil");
let hint = earliest_completion_retry_s([(10_000, 0)].into_iter(), 200.0);
assert_eq!(hint, Some(60), "clamped to the shed contract's 60 s max");
assert_eq!(earliest_completion_retry_s(std::iter::empty(), 200.0), None);
assert_eq!(
earliest_completion_retry_s([(10, 0)].into_iter(), 0.0),
None,
"no latency estimate yet"
);
}
#[test]
fn exempt_matching_strips_tenant_row_prefix() {
let cfg = ShadowConfig {
armed: true,
budget_bytes: None,
exempt: ShadowConfig::parse_exempt("watchdog-orn, ten_UBB1F6Lf ,,orn-probe-dry"),
};
assert!(cfg.is_exempt("t:watchdog-orn"), "keyring row form");
assert!(cfg.is_exempt("ten_UBB1F6Lf"), "bare row form");
assert!(cfg.is_exempt("orn-probe-dry"));
assert!(!cfg.is_exempt("t:acme"));
assert!(!cfg.is_exempt(""));
}
}