// examples/resilience_gateway — runnable demonstration of the
// `src/infra_resilience` direction as an API-gateway workflow: retry a
// flaky upstream with capped exponential backoff, trip and recover a
// circuit breaker, rate-limit clients with token bucket and sliding
// window, isolate tenants with a bulkhead, adapt concurrency with AIMD,
// and cut tail latency with hedged requests.
//
// Run with:
// moon run examples/resilience_gateway
///|
fn main {
println("=== 1. capped exponential backoff + retry ===")
let policy : @res.BackoffPolicy = {
base_ms: 100,
cap_ms: 2000,
max_retries: 6,
}
let sched = []
for attempt in 0..<6 {
sched.push(@res.backoff_delay(policy, attempt).to_string())
}
println("delays (ms) : \{sched.join(", ")}")
println(
"jittered #3 : \{@res.backoff_delay_jittered(policy, 3, 42)} ms (deterministic seed)",
)
let (ok, attempts) = @res.retry_run(policy, fn(attempt) { attempt >= 2 })
println("flaky upstream succeeds on 3rd try: ok=\{ok}, attempts=\{attempts}")
println("")
println("=== 2. circuit breaker state machine ===")
let cb = @res.CircuitBreaker::create(3, 1000)
println("initial : \{cb.state_of()}")
cb.record_failure(0)
cb.record_failure(10)
cb.record_failure(20)
println("3 failures : \{cb.state_of()} (allow now: \{cb.allow(30)})")
println("after cooldown: allow=\{cb.allow(1500)} -> \{cb.state_of()}")
cb.record_success()
println("probe success: \{cb.state_of()}")
println("")
println("=== 3. rate limiting: token bucket vs sliding window ===")
let bucket = @res.TokenBucket::create(3, 1)
let bucket_hits = []
for i in 0..<5 {
bucket_hits.push(bucket.try_acquire(i.to_int64() * 10).to_string())
}
println("token bucket (cap 3): \{bucket_hits.join(", ")}")
let win = @res.SlidingWindowLimiter::create(1000, 3)
let win_hits = []
for i in 0..<5 {
win_hits.push(win.try_acquire(i.to_int64() * 100).to_string())
}
println("sliding window (3/s) : \{win_hits.join(", ")}")
println("after window slides : \{win.try_acquire(1500)}")
println("")
println("=== 4. bulkhead isolation per tenant ===")
let bh = @res.Bulkhead::create(2, 1)
let admissions = []
for _i in 0..<4 {
let verdict = match bh.try_acquire() {
Admitted => "admitted"
Enqueued => "enqueued"
Rejected => "rejected"
}
admissions.push(verdict)
}
println("4 concurrent calls (2 slots + 1 queue): \{admissions.join(", ")}")
let _ = bh.release()
println(
"after release: in-flight=\{bh.in_flight_count()}, queued=\{bh.queued_count()}, rejected=\{bh.rejected_count()}",
)
println("")
println("=== 5. AIMD adaptive concurrency ===")
let aimd = @res.AimdLimiter::create(10, 1, 100)
for _i in 0..<5 {
aimd.on_success()
}
println("after 5 successes : limit = \{aimd.current_limit()}")
aimd.on_overload()
println(
"after overload : limit = \{aimd.current_limit()} (multiplicative decrease)",
)
println("")
println("=== 6. hedged requests cut tail latency ===")
let hedge : @res.HedgePolicy = { hedge_after_ms: 50, max_hedges: 2 }
println(
"hedge fire times : \{@res.hedge_schedule(hedge).map(fn(t) { t.to_string() }).join(", ")} ms",
)
let latencies = [300, 80, 120]
let (winner, latency, wasted) = @res.hedge_outcome(hedge, latencies)
println(
"upstream latencies \{latencies.map(fn(t) { t.to_string() }).join("/")}ms -> winner #\{winner} at \{latency}ms (wasted \{wasted})",
)
}