🌳
pt0/serverF/getExpBackoffLimitedF.mts
1import * as _ from 'lodash-es'
4type RlHistRec = {lastRunAtMs: number, backoffNo: number, numSilenced: number, silencedSamples?: unknown[]}
6export const getExpBackoffLimited = ({
7 cache_key, rlHistH, backoffBaseMs=60 * 1000,
8 recoveryPeriodMs=1000 * 60 * 60 * 24,
9 backoffCapMs=recoveryPeriodMs,
10 silencedSample, maxSilencedSamples=10,
11}: {cache_key: string, rlHistH: Record<string, RlHistRec>, backoffBaseMs?: number, recoveryPeriodMs?: number, backoffCapMs?: number, silencedSample?: unknown, maxSilencedSamples?: number}) => {
12 assertDefined(rlHistH)
14 let rec: RlHistRec | undefined = rlHistH[cache_key]
16 const now = _.now()
18 let backoffNo = -1
19 let numSilenced = 0
20 let lastRunAtMs = 0
22 if (rec) {
23 ;({lastRunAtMs, backoffNo, numSilenced} = rec)
25 const minRecovResetMs = now - recoveryPeriodMs
26 if (lastRunAtMs < minRecovResetMs) {
27 delete rlHistH[cache_key]
28 rec = undefined
29 }
30 }
32 if (rec) {
33 // cap the exponential at backoffCapMs (default = recoveryPeriodMs, ~daily) so a PERSISTENT error
34 // keeps reminding once/day instead of fading below daily as 2^backoffNo grows unbounded.
35 const backoffWaitMs = Math.min(backoffBaseMs * Math.pow(2, backoffNo), backoffCapMs)
37 const mustWaitUntilMs = lastRunAtMs + backoffWaitMs
38 const silencedForMs = mustWaitUntilMs - now
39 const isLimited = silencedForMs > 0
41 if (isLimited) {
42 numSilenced += 1
43 rec.numSilenced += 1
44 if (silencedSample !== undefined) {
45 ;(rec.silencedSamples ||= []).push(silencedSample)
46 if (rec.silencedSamples.length > maxSilencedSamples) rec.silencedSamples.length = maxSilencedSamples
47 }
48 return {isLimited, backoffNo, numSilenced}
49 }
50 }
52 const prevSilencedSamples = rec?.silencedSamples
54 backoffNo += 1
56 rlHistH[cache_key] = {
57 numSilenced: 0,
58 lastRunAtMs: now,
59 backoffNo,
60 }
62 return {isLimited: false, backoffNo, numSilenced, silencedSamples: prevSilencedSamples}