bend-kit-retry Releases (3): 0.3.0.0 0x14e48621ef66156b0eb652524b8594d4 indexed https://jend.leeeet.dev/files.txt?path=bend-kit-retry@0.3.0.0 0.2.0.0 0x6e7f3ef294181015ba054d138419a36c unindexed https://jend.leeeet.dev/files.txt?path=bend-kit-retry@0.2.0.0 0.1.0.0 0x12d3bb9bb57264f1d5e2fcf763dbe13b unindexed https://jend.leeeet.dev/files.txt?path=bend-kit-retry@0.1.0.0 Definitions (35): Retry.Budget (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=Budget) # left: attempts the whole operation may still start, the first one included. Nested layers share one Budget. # deadline: no attempt starts at or after this time. None: no deadline. type Budget is Data: Budget{left: Nat, deadline: Maybe<&2, Nat>} Retry.Deny (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=Deny) type Deny is Data: DenyLate{} DenySpent{} DenyOpen{} Retry.Gate (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=Gate) type Gate is Data: GateGo{} GateNo{why: Deny} Retry.Circuit (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=Circuit) # threshold: failures in a row, each inside window ms of the newest, that open the circuit. cooldown: ms it stays open. # probes: attempts allowed at once while half-open. type Circuit is Data: Circuit{threshold: Nat, window: Nat, cooldown: Nat, probes: Nat} Retry.State (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=State) # Closed: the failures since the last success that are still inside the window, newest first. # Open: no attempt before until. Half: probes in flight. type State is Data: CircuitClosed{fails: List<&2, Nat>} CircuitOpen{until: Nat} CircuitHalf{busy: Nat} Retry.Breaker (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=Breaker) type Breaker is Data: BreakerOff{} BreakerOn{circuit: Circuit, state: State} Retry.Outcome (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=Outcome) # Settled: a success. Failed: a failure that another try would repeat. Rejected: the caller is at fault (such as # a 4xx), so another try would repeat it, and it is not the target's failure. Unsafe: a transient failure of a # request that must not repeat. Transient: another try can help; after is the server's wait in ms. type Outcome is Data: Settled{} Failed{} Rejected{} Unsafe{} Transient{after: Maybe<&2, Nat>} Retry.Policy (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=Policy) # attempts: tries in one layer, the first one included. base: the first backoff in ms. cap: the longest wait in ms. # deadline: ms from the start of the operation to its deadline. None: no deadline. type Policy is Data: Policy{attempts: Nat, base: Nat, cap: U32, deadline: Maybe<&2, Nat>} Retry.Plan (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=Plan) type Plan is Data: PlanStop{} PlanWait{ms: Nat} Retry.Budget.new (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=Budget.new) def Budget.new(+attempts: Nat, +now: Nat, ms: Maybe<&2, Nat>) -> Budget: Retry.Budget.late (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=Budget.late) def Budget.late(+now: Nat, d: Maybe<&2, Nat>) -> Bool: Retry.prune (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=prune) # The failure times still inside the window: now < t + window. Newest first, so the first old time ends the list. def prune(+now: Nat, +window: Nat, fs: List<&2, Nat>) -> List<&2, Nat>: Retry.Circuit.probes (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=Circuit.probes) def Circuit.probes(c: Circuit) -> Nat: Retry.Circuit.cooldown (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=Circuit.cooldown) def Circuit.cooldown(c: Circuit) -> Nat: Retry.half.admit (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=half.admit) def half.admit(go: Bool, +k: Nat) -> State & Bool: Retry.open.admit (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=open.admit) def open.admit(early: Bool, +p: Nat, +u: Nat) -> State & Bool: Retry.State.admit (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=State.admit) # Closed admits. Open admits nothing before until; at or after until it turns half-open and admits a probe. # Half-open admits while fewer than probes are in flight. def State.admit(+c: Circuit, s: State, +now: Nat) -> State & Bool: Retry.closed.trip (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=closed.trip) def closed.trip(under: Bool, fs: List<&2, Nat>, +until: Nat) -> State: Retry.closed.fail (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=closed.fail) def closed.fail(+c: Circuit, fs: List<&2, Nat>, +now: Nat) -> State: Retry.State.done (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=State.done) # A closed success clears the failures. A closed failure opens the circuit when the window then holds threshold # failures. A half-open success closes it; a half-open failure opens it again for cooldown. An attempt that ends # while open changes nothing. def State.done(+c: Circuit, s: State, +now: Nat, failed: Bool) -> State: Retry.Breaker.new (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=Breaker.new) def Breaker.new(c: Circuit) -> Breaker: Retry.Breaker.on (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=Breaker.on) def Breaker.on(+c: Circuit, x: State & Bool) -> Breaker & Bool: Retry.Breaker.admit (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=Breaker.admit) def Breaker.admit(br: Breaker, +now: Nat) -> Breaker & Bool: Retry.Breaker.done (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=Breaker.done) def Breaker.done(br: Breaker, +now: Nat, failed: Bool) -> Breaker: Retry.admit.ok (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=admit.ok) def admit.ok(ok: Bool, br: Breaker, +p: Nat, d: Maybe<&2, Nat>) -> Breaker & Budget & Gate: Retry.admit.breaker (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=admit.breaker) def admit.breaker(x: Breaker & Bool, +p: Nat, d: Maybe<&2, Nat>) -> Breaker & Budget & Gate: Retry.admit.late (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=admit.late) def admit.late(late: Bool, br: Breaker, left: Nat, d: Maybe<&2, Nat>, +now: Nat) -> Breaker & Budget & Gate: Retry.admit (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=admit) # Ask to start one attempt at now. The deadline is checked first, then the budget, then the breaker. # Only GateGo takes one attempt from the budget. def admit(br: Breaker, b: Budget, +now: Nat) -> Breaker & Budget & Gate: Retry.delay (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=delay) # min(cap, base * 2^n), doubled one step at a time, so no step passes cap. def delay(n: Nat, +base: Nat, +cap: Nat) -> Nat: Retry.jitter (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=jitter) # Equal jitter: d / 2, plus r mod (d / 2 + 1). The wait is in [d / 2, d] for every r. def jitter(+d: Nat, +r: Nat) -> Nat: Retry.wait (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=wait) # The wait before retry n (0 first): the server's wait when it gave one, else the jittered backoff. At most cap. def wait(after: Maybe<&2, Nat>, p: Policy, +n: Nat, +r: Nat) -> Nat: Retry.plan.left (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=plan.left) def plan.left(left: Nat, +w: Nat, d: Maybe<&2, Nat>, +now: Nat) -> Plan: Retry.plan.at (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=plan.at) def plan.at(+w: Nat, b: Budget, +now: Nat) -> Plan: Retry.plan (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=plan) # After an attempt ends at now: stop, or wait ms and then ask admit again. Settled, Failed, Rejected, and Unsafe always stop. # A transient outcome stops when the budget is spent or the wait would reach the deadline. def plan(o: Outcome, p: Policy, b: Budget, +n: Nat, +now: Nat, +r: Nat) -> Plan: Retry.failed (https://jend.leeeet.dev/definition.txt?path=bend-kit-retry@0.3.0.0/retry.bend&name=failed) # Failed, Unsafe, and Transient count as failures for the breaker. Settled and Rejected do not. def failed(o: Outcome) -> Bool: