///|
/// Load shedding under CPU pressure (← go-zero's `load.AdaptiveShedder`, wired
/// from `RestConf.CpuThreshold`): admit while measured CPU sits at or below the
/// threshold, shed above it. Both numbers are per-mille, so go-zero's default
/// `900` means 90%.
///
/// go-zero reads `stat.CpuUsage()` and additionally sheds on in-flight count
/// against a moving pass/latency estimate. No MoonBit backend can read a CPU
/// meter, so the usage source is injected the way `Clock` is, and a shedder built
/// without one reports `0` and never sheds — the in-flight half is not modelled.
pub struct Shedder {
threshold : Int64
usage : () -> Int64
}
///|
/// A shedder that sheds once `usage` exceeds `threshold` per-mille.
pub fn Shedder::new(threshold : Int64, usage? : () -> Int64) -> Shedder {
{ threshold, usage: usage.unwrap_or(() => 0L), }
}
///|
/// Whether a request is admitted at the current usage.
pub fn Shedder::allow(self : Shedder) -> Bool {
(self.usage)() <= self.threshold
}
///|
/// The CPU usage the shedder is reading, per mille.
pub fn Shedder::usage(self : Shedder) -> Int64 {
(self.usage)()
}
///|
/// Shedding middleware (← go-zero's `SheddingHandler`): answer `503 Service
/// Unavailable` without running the app while the shedder is refusing, and pass
/// everything else through. Non-HTTP scopes are never shed.
pub fn shedding(s : Shedder) -> Middleware {
inner => {
(scope, receive, send) => {
match scope {
Http(_) =>
if s.allow() {
inner(scope, receive, send)
} else {
for event in service_unavailable_events() {
send(event)
}
}
_ => inner(scope, receive, send)
}
}
}
}