Smart Byte (1 crate, 2127 LOC, 35 tests)
smart-byte-atproto ATProto (Bluesky federation) adapter.
PLC + did:web resolution; XRPC client; CARv1 encode/decode
with CIDv1 (dag-cbor + raw, SHA-256); sketch MST with
order-independent BLAKE3 root; Repo + UnsignedCommit /
SignedCommit (Ed25519 over CBOR); firehose transport
trait + in-memory impl; AT URI ↔ smart-byte AID bridge
with SAID commitment.
OpenPay (1 crate, 2397 LOC, 44 tests)
op-openbanking UK OBIE R/W v3.1.10/3.1.11, Berlin Group NextGenPSD2
XS2A v1.3.13, STET PSD2 v1.7, Australia CDR Banking v1.31,
FDX v5/v6, SGFinDex shapes. AISP / PISP / PIIS / VRP
traits. FAPI 1.0 Advanced (PS256/ES256, mTLS, RFC 8705
certificate binding, JWK registration). Operators wire
their own JWS signer + HTTP transport — keeps the crate
KMS-agnostic.
EOC (1 crate, 2179 LOC, 32 tests)
eoc-distributed Distributed-inference scheduling. Worker trait with
Capability + Accelerator (CPU/GPU/TPU/NPU/Mixed) +
Load.busyness()/projected_micro_joules()/projected_g_co2e().
RoundRobin / LeastBusy / LatencyWeighted / JouleWeighted
(default) / CarbonWeighted routers. Work-stealing
scheduler. vLLM-style continuous batching with KV-block
budget. Ray-Serve-style auto-scaler with cooldown.
KV-cache-aware sticky-session routing.
Co-Authored-By: Claude Opus 4.7 <[email protected]>
169 lines
5 KiB
Rust
169 lines
5 KiB
Rust
//! KV-cache locality routing.
|
||
//!
|
||
//! When a worker already holds the prefix KV-cache for a session, sending
|
||
//! the *next* turn to the same worker avoids re-prefilling, which is
|
||
//! often 10-100× cheaper in joules than placing the work cold. This is
|
||
//! the trick behind vLLM's `PrefixCachingScheduler`, SGLang's RadixAttn,
|
||
//! and the "sticky session" mode in LiteLLM router.
|
||
//!
|
||
//! [`KvCacheAwareRouter`] is a thin index: `session_id -> worker_id`, with
|
||
//! a fallback to the joule-weighted router when the session is unknown
|
||
//! (cold start) or the previously-used worker is no longer alive.
|
||
|
||
use std::collections::HashMap;
|
||
|
||
use crate::error::{DistributedError, Result};
|
||
use crate::router::{Request, Router, Strategy};
|
||
use crate::worker::Worker;
|
||
|
||
/// Locality-aware request descriptor: like [`Request`] but with a
|
||
/// session id.
|
||
#[derive(Debug, Clone)]
|
||
pub struct LocalRequest<'a> {
|
||
/// Caller-side session id (matches the previous turn's session).
|
||
pub session_id: String,
|
||
/// Model.
|
||
pub model: &'a str,
|
||
/// Expected new tokens for the cold-start fallback's joule maths.
|
||
pub expected_tokens: u32,
|
||
}
|
||
|
||
/// KV-cache locality router.
|
||
#[derive(Debug)]
|
||
pub struct KvCacheAwareRouter {
|
||
sticky: HashMap<String, String>,
|
||
fallback: Router,
|
||
}
|
||
|
||
impl Default for KvCacheAwareRouter {
|
||
fn default() -> Self {
|
||
Self::new(Strategy::JouleWeighted)
|
||
}
|
||
}
|
||
|
||
impl KvCacheAwareRouter {
|
||
/// Construct with a fallback strategy for cold sessions.
|
||
pub fn new(fallback: Strategy) -> Self {
|
||
Self {
|
||
sticky: HashMap::new(),
|
||
fallback: Router::new(fallback),
|
||
}
|
||
}
|
||
|
||
/// Borrow the fallback router.
|
||
pub fn fallback(&self) -> &Router {
|
||
&self.fallback
|
||
}
|
||
|
||
/// Pick a worker for `req`. If the session has been seen and the
|
||
/// previously-used worker is still in `workers` and serves the
|
||
/// model, returns it (cache hit). Otherwise falls back to
|
||
/// joule-weighted placement and records the binding.
|
||
pub fn pick<'w>(
|
||
&mut self,
|
||
workers: &'w [&'w dyn Worker],
|
||
req: &LocalRequest<'_>,
|
||
) -> Result<&'w dyn Worker> {
|
||
if workers.is_empty() {
|
||
return Err(DistributedError::NoWorkers);
|
||
}
|
||
if let Some(prev) = self.sticky.get(&req.session_id) {
|
||
if let Some(w) = workers
|
||
.iter()
|
||
.copied()
|
||
.find(|w| w.id() == prev && w.capability().serves(req.model))
|
||
{
|
||
return Ok(w);
|
||
}
|
||
// Stale binding — drop it.
|
||
self.sticky.remove(&req.session_id);
|
||
}
|
||
let pick = self.fallback.pick(
|
||
workers,
|
||
&Request {
|
||
model: req.model,
|
||
expected_tokens: req.expected_tokens,
|
||
},
|
||
)?;
|
||
self.sticky
|
||
.insert(req.session_id.clone(), pick.id().to_string());
|
||
Ok(pick)
|
||
}
|
||
|
||
/// Forget the binding for `session_id`. Caller would invoke this on
|
||
/// session end, KV eviction, or worker failure.
|
||
pub fn forget(&mut self, session_id: &str) {
|
||
self.sticky.remove(session_id);
|
||
}
|
||
|
||
/// Number of remembered sessions.
|
||
pub fn len(&self) -> usize {
|
||
self.sticky.len()
|
||
}
|
||
|
||
/// Whether there are no remembered sessions.
|
||
pub fn is_empty(&self) -> bool {
|
||
self.sticky.is_empty()
|
||
}
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
use crate::worker::{Accelerator, Capability, InMemoryWorker, Load};
|
||
|
||
fn mk(id: &str, micro_j: u32) -> InMemoryWorker {
|
||
InMemoryWorker::new(
|
||
id,
|
||
Capability {
|
||
models: vec!["m".into()],
|
||
accelerator: Accelerator::Gpu,
|
||
max_concurrency: 8,
|
||
continuous_batching: true,
|
||
paged_kv: true,
|
||
zone: "EU-FR".into(),
|
||
},
|
||
Load {
|
||
micro_joules_per_token: micro_j,
|
||
..Load::idle()
|
||
},
|
||
)
|
||
}
|
||
|
||
#[test]
|
||
fn sticky_after_first_call() {
|
||
let a = mk("a", 200);
|
||
let b = mk("b", 50);
|
||
let pool: Vec<&dyn Worker> = vec![&a, &b];
|
||
let mut r = KvCacheAwareRouter::default();
|
||
let first = r
|
||
.pick(
|
||
&pool,
|
||
&LocalRequest {
|
||
session_id: "s1".into(),
|
||
model: "m",
|
||
expected_tokens: 10,
|
||
},
|
||
)
|
||
.expect("ok")
|
||
.id()
|
||
.to_string();
|
||
// Even if we flip the load so the OTHER worker would now be
|
||
// cheaper, the sticky binding must keep us on `first`.
|
||
let cheaper = mk("c", 1);
|
||
let pool2: Vec<&dyn Worker> = vec![&a, &b, &cheaper];
|
||
let second = r
|
||
.pick(
|
||
&pool2,
|
||
&LocalRequest {
|
||
session_id: "s1".into(),
|
||
model: "m",
|
||
expected_tokens: 10,
|
||
},
|
||
)
|
||
.expect("ok")
|
||
.id()
|
||
.to_string();
|
||
assert_eq!(first, second);
|
||
}
|
||
}
|