-
Notifications
You must be signed in to change notification settings - Fork 727
Expand file tree
/
Copy pathinstallationPool.ts
More file actions
56 lines (47 loc) · 1.73 KB
/
Copy pathinstallationPool.ts
File metadata and controls
56 lines (47 loc) · 1.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import { getServiceChildLogger } from '@crowd/logging'
const log = getServiceChildLogger('installation-pool')
// Park an installation before GitHub starts rejecting — avoids a failed request + requeue
const PROACTIVE_PARK_REMAINING = 50
/** Round-robins over installations, skipping ones parked until their rate-limit reset. */
export class InstallationPool {
private readonly parkedUntil = new Map<number, number>()
private roundRobinIdx = 0
constructor(private readonly ids: number[]) {}
select(): { installationId: number; waitMs: number } {
const now = Date.now()
const n = this.ids.length
for (let i = 0; i < n; i++) {
const idx = (this.roundRobinIdx + i) % n
const id = this.ids[idx]
if ((this.parkedUntil.get(id) ?? 0) <= now) {
this.roundRobinIdx = (idx + 1) % n
return { installationId: id, waitMs: 0 }
}
}
let soonestReset = Infinity
let soonestId = this.ids[0]
for (const id of this.ids) {
const reset = this.parkedUntil.get(id) ?? 0
if (reset < soonestReset) {
soonestReset = reset
soonestId = id
}
}
return { installationId: soonestId, waitMs: Math.max(1_000, soonestReset - now) }
}
park(installationId: number, untilMs: number): void {
this.parkedUntil.set(installationId, untilMs)
}
parkIfBudgetLow(
installationId: number,
remaining: number | null | undefined,
resetAt: string | null | undefined,
): void {
if (remaining == null || resetAt == null || remaining >= PROACTIVE_PARK_REMAINING) return
this.park(installationId, new Date(resetAt).getTime() + 5_000)
log.info(
{ installationId, remaining, resetAt },
'Budget low — proactively parking installation',
)
}
}