mirror of
https://github.com/stablyai/orca.git
synced 2026-10-07 08:02:21 +00:00
Refresh only actual on-screen review cards and the selected visible review panel, with a single metadata owner per execution host. Open reviews refresh every 60 seconds when selected and 120 seconds otherwise. Settled merged reviews stop automatic refresh; pending merged checks continue, hidden rows stop, and work changes or stale re-exposure discover fresh state. Remove the polling setting. Preserve provider/SSH/runtime ownership, mixed-version cache behavior, failure backoff, bounded foreground admission, request coalescing, and mutation invalidation. Cover older persisted caches and unknown HEADs. Validated with 1,628 focused tests, typechecking, lint/code-quality gates, hidden Electron viewport checks, and parallel Codex/Claude Opus 5.5 adversarial reviews. Measurements and policy details are in PR #25788. Fixes #25746 Co-authored-by: ggbdpq <ggbdpq@gmail.com>
123 lines
4.0 KiB
TypeScript
123 lines
4.0 KiB
TypeScript
import type { PRRefreshQueueEntry } from './pr-refresh-queue'
|
|
|
|
const BACKGROUND_BUDGET_WINDOW_MS = 5 * 60_000
|
|
const MIN_BACKGROUND_SPACING_MS = 10_000
|
|
const BACKGROUND_BUDGET_MAX = 20
|
|
const ACTIVE_BURST_WINDOW_MS = 30_000
|
|
const ACTIVE_BURST_MAX = 3
|
|
|
|
export function usesActiveRefreshPacing(entry: PRRefreshQueueEntry): boolean {
|
|
// Selected admission survives deselection while queued; periodic follow-ups clear the flag.
|
|
return (
|
|
entry.reason === 'active' ||
|
|
(entry.reason === 'visible' && entry.priority >= 80 && entry.bypassBackgroundBudget === true)
|
|
)
|
|
}
|
|
|
|
export class PRRefreshPacing {
|
|
private readonly backgroundStarts: number[] = []
|
|
private readonly activeStartsByScope = new Map<string, number[]>()
|
|
private lastBackgroundStartAt = 0
|
|
|
|
clearActiveBurstWindow(windowId: number): void {
|
|
const windowPrefix = `${windowId}::`
|
|
for (const scope of Array.from(this.activeStartsByScope.keys())) {
|
|
if (scope.startsWith(windowPrefix)) {
|
|
this.activeStartsByScope.delete(scope)
|
|
}
|
|
}
|
|
}
|
|
|
|
noteBackgroundStart(): void {
|
|
const now = Date.now()
|
|
this.lastBackgroundStartAt = now
|
|
this.backgroundStarts.push(now)
|
|
this.pruneBackgroundStarts(now)
|
|
}
|
|
|
|
nextBudgetDelay(): number {
|
|
const now = Date.now()
|
|
this.pruneBackgroundStarts(now)
|
|
const spacingDelay =
|
|
this.lastBackgroundStartAt > 0
|
|
? Math.max(0, MIN_BACKGROUND_SPACING_MS - (now - this.lastBackgroundStartAt))
|
|
: 0
|
|
const windowDelay =
|
|
this.backgroundStarts.length < BACKGROUND_BUDGET_MAX
|
|
? 0
|
|
: Math.max(1_000, BACKGROUND_BUDGET_WINDOW_MS - (now - this.backgroundStarts[0]))
|
|
return Math.max(spacingDelay, windowDelay)
|
|
}
|
|
|
|
activeOrder(a: PRRefreshQueueEntry, b: PRRefreshQueueEntry): number {
|
|
if (!usesActiveRefreshPacing(a) || !usesActiveRefreshPacing(b)) {
|
|
return 0
|
|
}
|
|
if (this.activeBurstScope(a) !== this.activeBurstScope(b)) {
|
|
return 0
|
|
}
|
|
return b.queuedAt - a.queuedAt
|
|
}
|
|
|
|
entryDelay(entry: PRRefreshQueueEntry): number {
|
|
const activeDelay = usesActiveRefreshPacing(entry) ? this.nextActiveBurstDelay(entry) : 0
|
|
if (activeDelay > 0) {
|
|
return activeDelay
|
|
}
|
|
return entry.bypassBackgroundBudget !== true &&
|
|
(entry.reason === 'visible' || entry.reason === 'swr')
|
|
? this.nextBudgetDelay()
|
|
: 0
|
|
}
|
|
|
|
isActiveBurstDelayed(entry: PRRefreshQueueEntry): boolean {
|
|
return usesActiveRefreshPacing(entry) && this.nextActiveBurstDelay(entry) > 0
|
|
}
|
|
|
|
noteActiveStart(entry: PRRefreshQueueEntry): void {
|
|
const now = Date.now()
|
|
const scope = this.activeBurstScope(entry)
|
|
const activeStarts = this.pruneActiveStarts(scope, now)
|
|
activeStarts.push(now)
|
|
this.activeStartsByScope.set(scope, activeStarts)
|
|
}
|
|
|
|
private pruneBackgroundStarts(now: number): void {
|
|
while (
|
|
this.backgroundStarts.length > 0 &&
|
|
now - this.backgroundStarts[0] > BACKGROUND_BUDGET_WINDOW_MS
|
|
) {
|
|
this.backgroundStarts.shift()
|
|
}
|
|
}
|
|
|
|
private activeBurstScope(entry: PRRefreshQueueEntry): string {
|
|
const runtimeScope = entry.candidate.connectionId
|
|
? `ssh:${entry.candidate.connectionId}`
|
|
: `local:${entry.candidate.localGitOptions?.wslDistro ?? 'host'}`
|
|
return `${entry.windowId ?? 'global'}::${runtimeScope}`
|
|
}
|
|
|
|
private pruneActiveStarts(scope: string, now: number): number[] {
|
|
const activeStarts = this.activeStartsByScope.get(scope) ?? []
|
|
while (activeStarts.length > 0 && now - activeStarts[0] >= ACTIVE_BURST_WINDOW_MS) {
|
|
activeStarts.shift()
|
|
}
|
|
if (activeStarts.length === 0) {
|
|
this.activeStartsByScope.delete(scope)
|
|
} else {
|
|
this.activeStartsByScope.set(scope, activeStarts)
|
|
}
|
|
return activeStarts
|
|
}
|
|
|
|
private nextActiveBurstDelay(entry: PRRefreshQueueEntry): number {
|
|
const now = Date.now()
|
|
const activeStarts = this.pruneActiveStarts(this.activeBurstScope(entry), now)
|
|
if (activeStarts.length < ACTIVE_BURST_MAX) {
|
|
return 0
|
|
}
|
|
return Math.max(1, ACTIVE_BURST_WINDOW_MS - (now - activeStarts[0]))
|
|
}
|
|
}
|