Resilience for MoonBit without I/O: circuit breaking, load shedding, rate limits and deadlines.
| Answers | Refuses when | |
|---|---|---|
| Breaker | Is this backend likely to work? | a draw falls under a refusal fraction built from the last ten seconds of outcomes |
| Shedder | Should this server take on another call? | the CPU is over its threshold and more calls are in flight than the server has shown it can finish |
| Bucket | Is this call within a rate? | the token is spent; next says when the next one lands |
| Sliding | Is it within a quota over any window-long stretch? | the count reaches the quota |
| Period | Is it within a quota this period? | the count for the open period is spent |
| Gate | Is there room right now? | held has reached limit — it never asks what time it is |
| Backoff | When to try again, and how long first? | more(attempts) is false |
| Deadline | How much of the budget is left? | expired is true |
| Window | What does the rolling window hold? | never; it is the array of buckets the others read |
let breaker = @moonward.Breaker::new()
let now = 0L
match breaker.allow(now, 0.42) {
Some(call) => call.pass(now) // or call.fail(now)
None => println("refused")
}w = max( k − (k − floor) · failing / size , floor )
drop = ( total − protection − w · accepts ) / ( total + 1 )
drop ×= ( size − working ) / sizeburst bucket sliding period
0 21 20 40
1 21 20 40
2 21 20 40
3 21 20 40
five tokens in 200 ms| Preset | Values |
|---|---|
| sre | k 1.5 decaying to 1.1, protection 5, a probe after 1 s, 40 buckets over 10 s |
| shed | 50 buckets over 5 s, CPU threshold 900‰, 1 s assumed response time, average weighted 0.9, 1 s to cool, never under 0.1 of capacity |
| backoff | 3 attempts, 100 ms doubling to a 1 s cap, each wait spread by a fifth either way |
| Program | What it shows |
|---|---|
| src/examples/minimal | one breaker, one failing backend, four lines of output — the smallest thing that works |
| src/examples/limiters | the same bursty traffic through all three quotas |
| src/examples/tour | a breaker under an outage, a shedder under rising load, one step through each limiter |
| src/examples/guide | the guide as one program, in the order you read it |
w = max(k - (k - floor) × failing / size, floor)
drop = (total - protection - w × accepts) / (total + 1)
drop ×= (size - working) / sizepub struct Bucket {
rate : Double
burst : Double
tokens : Double
at : Int64
}pub struct Deadline {
start : Int64
budget : Int64
}pub struct Gate {
limit : Int
held : Int
}| Rigid | the computed delay, unchanged. Reproducible, and it synchronises clients |
| Full | uniform over [0, cap]. The one that spreads clients furthest |
| Equal | half the cap plus uniform over [0, cap/2]. Keeps a floor under the wait |
| Decorrelated | uniform over [base, previous × 3]. Grows from where it last landed rather than from the attempt number |
| Spread(f) | uniform over [cap × (1 − f), cap × (1 + f)], f held to [0, 1]. So the wait can pass the cap by f |
pub struct Period {
quota : Int
period : Int64
align : Bool
start : Int64?
count : Int
}Install
Download zipResilience for MoonBit without I/O: circuit breaking, load shedding, rate limits and deadlines.