Sign in

    moonbitstack/moonzero/breaker does not have a README file

    Unavailable

    pub suberror Unavailable

    Raised by Breaker::run for a call the throttle shed (← go-zero's ErrServiceUnavailable).

    Breaker

    pub struct Breaker {
    win :
    Window

    clock :
    Clock

    roll : () -> Double
    last_pass : Int64?
    }

    Google SRE's client-side throttling breaker (← go-zero's googleBreaker, the algorithm behind every breaker it hands out). There is no open/closed state machine and no failure streak: the breaker keeps a rolling Window of call outcomes and sheds a fraction of new calls, so a struggling backend keeps receiving as much load as it can still serve rather than being cut off wholesale and then flooded again on recovery.

    With accepts successes out of total calls in the window, failing the run of all-failure buckets at the head of the window and working the run of all-success ones:

    w = k - (k - min_k) * failing / buckets drop = (total - protection - max(w, min_k) * accepts) / (total + 1) drop *= (buckets - working) / buckets

    A non-positive drop admits everything, which is the whole healthy case. A sustained failure run decays w towards min_k, so past successes count for less and the throttle bites harder; a run of clean buckets scales drop back down as the backend recovers.

    Both sources of nondeterminism are injected — clock for time, rand for the shed roll — so every decision is reproducible in a test.

    Breaker::allow

    fn Breaker::allow(self : Breaker) -> Promise?

    Ask to make one call. Some promise means it may proceed and the caller settles that promise once the call finishes; None means the throttle shed it. A shed is recorded too, so a breaker that is dropping never reads back as idle and throttle itself off.

    Breaker::drop_ratio

    fn Breaker::drop_ratio(self : Breaker) -> Double

    The fraction of new calls the breaker is currently shedding — 0.0 while the window is healthy. The force-pass probe is deliberately not folded in: this is the standing throttle, which is what a dashboard or an alert wants.

    Breaker::new

    fn Breaker::new(clock :
    Clock
    , rand? : () -> Double, buckets? : Int, bucket_ms? : Int64) -> Breaker

    A breaker over a buckets × bucket_ms window (go-zero's ten seconds in forty slices by default) reading time from clock. rand supplies the shed roll as a draw from [0, 1); omit it and the breaker draws from a system-seeded generator, pass one to fix the decisions.

    Breaker::run

    fn[T] Breaker::run(self : Breaker, req : () -> T raise) -> T raise

    Run req under the breaker (← go-zero's Breaker.Do). A shed call raises Unavailable and req never runs; otherwise req returning settles the promise as a success, and req raising settles it as a failure and propagates.

    Promise

    pub struct Promise {
    b : Breaker
    }

    The receipt for an admitted call (← go-zero's breaker.Promise). Exactly one of accept / reject settles it; until then the window holds no record of how the call went.

    Promise::accept

    fn Promise::accept(self : Promise) -> Unit

    Settle the call as a success.

    Promise::reject

    fn Promise::reject(self : Promise) -> Unit

    Settle the call as a failure.

    Source Files