// Copyright 2026 Leo Cheng
// SPDX-License-Identifier: Apache-2.0
///|
fn ms(n : Int64) -> Int64 {
n * 1_000_000L
}
///|
/// A fixed spread of draws in `[0, 1)`, so the run prints the same every time.
fn roll(i : Int64) -> Double {
(i * 7919L % 1000L).to_double() / 1000.0
}
///|
/// A backend that fails every call between two and six seconds in. One call
/// every 20 ms goes through the breaker; each half second prints how much it
/// refused and the ratio it stands at.
fn breaker() -> Unit {
let b = @moonward.Breaker::new()
let mut sent = 0
let mut refused = 0
for i in 0L..<500L {
let now = ms(i * 20L)
match b.allow(now, roll(i)) {
Some(p) => {
sent += 1
if now >= ms(2000L) && now < ms(6000L) {
p.fail(now)
} else {
p.pass(now)
}
}
None => refused += 1
}
if (i + 1L) % 25L == 0L {
let ratio = (b.ratio(now) * 100.0).round().to_int()
println(
"\{(i + 1L) * 20L} ms sent \{sent} refused \{refused} ratio \{ratio}%",
)
sent = 0
refused = 0
}
}
}
///|
/// A server whose calls take 50 ms, sent a call every millisecond while the CPU
/// is at 95%: once more calls are in flight than it has shown it can finish,
/// the shedder starts refusing.
fn shedder() -> Unit {
let s = @moonward.Shedder::new()
let open : Array[(@moonward.Ticket, Int64)] = []
let mut refused = 0
for i in 0L..<3000L {
let now = ms(i)
while open.length() > 0 && open[0].1 <= now {
let (t, _) = open.remove(0)
t.pass(now)
}
match s.allow(now, if i < 1000L { 200 } else { 950 }) {
Some(t) => open.push((t, now + ms(50L)))
None => refused += 1
}
}
println("in flight \{s.flying()} refused \{refused} of 3000")
}
///|
fn main {
breaker()
println("")
shedder()
println("")
let day = @moondate.Span::new(days=1L)
let bucket = @moonward.Bucket::new(2.0, burst=3.0)
let burst = [0, 1, 2, 3].map(_ => bucket.take(0L))
println(
"bucket \{@debug.to_string(burst)}, next in \{bucket.next(0L).total_seconds()} s",
)
let quota = @moonward.Period::new(2, day, align=true)
println("period \{@debug.to_string([0, 1, 2].map(_ => quota.take(0L)))}")
let gate = @moonward.Gate::new(1)
println("gate \{gate.enter()} \{gate.enter()}")
let delays = @moonward.backoff.schedule(randoms=[0.5, 0.5])
println("backoff \{@debug.to_string(delays.map(d => d.total_seconds()))} s")
let d = @moonward.Deadline::start(@moondate.Span::new(seconds=2L), 0L)
println("deadline \{d.expired(ms(1500L))} \{d.expired(ms(2000L))}")
}