Resilience without I/O: circuit breaking, rate limiting, adaptive load shedding, backoff, rolling windows and deadlines, with the clock and the randomness passed in.
Dependencies
let now = 0L // the caller's clock, in nanoseconds
let breaker = @moonward.Breaker::new()
match breaker.allow(now, 0.42) {
Some(call) => call.pass(now + 30_000_000L) // or call.fail(...)
None => println("backend refused")
}
let shedder = @moonward.Shedder::new()
match shedder.allow(now, 400) {
Some(ticket) => ticket.pass(now + 30_000_000L) // or ticket.fail()
None => println("overloaded")
}
let daily = @moonward.Period::new(1000, @moondate.Span::new(days=1L), align=true)
let quota = daily.take(now) // Under
let rate = @moonward.Bucket::new(100.0, burst=200.0).take(now) // true
let wait = @moonward.backoff.delay(2, random=0.5) // 100 ms| What it answers | |
|---|---|
| Breaker | Whether to call a backend that has been failing, as a probability rather than a switch |
| Shedder | Whether this server should take on another call |
| Bucket | Whether a call is within a rate, and how long until one would be |
| Sliding | Whether it is within a quota counted over any window-long stretch |
| Period | The same quota counted per fixed period, cheaper and coarser |
| Gate | Whether there is room right now, which has nothing to do with time |
| Backoff | How long to wait before trying again, and whether to |
| Deadline | How much of a time budget is left |
| Window[B] | The rolling window the breaker and the shedder read; any type with a Default can be its bucket |
| Preset | Values | Source |
|---|---|---|
| sre | k 1.5 decaying to 1.1, protection 5, probe after 1 s, 40 buckets over 10 s | go-zero googlebreaker.go |
| shed | 50 buckets over 5 s, CPU threshold 900‰, 1 s assumed response time, average weight 0.9, 1 s to cool, never under 0.1 of capacity | go-zero adaptiveshedder.go |
| backoff | 3 attempts, 100 ms doubling to 1 s, a fifth either way | gRPC proposal A6 for the schedule and spread, as grpc-go and grpc-java draw it; the AWS SDKs' standard mode for the attempts |
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 without I/O: circuit breaking, rate limiting, adaptive load shedding, backoff, rolling windows and deadlines, with the clock and the randomness passed in.
Dependencies