mirror of
https://github.com/stablyai/orca.git
synced 2026-09-29 16:02:50 +00:00
108 lines
2.9 KiB
TypeScript
108 lines
2.9 KiB
TypeScript
import { createHash } from 'node:crypto'
|
|
import { resolve } from 'node:path'
|
|
import { recordCoalescedCrashBreadcrumb } from '../crash-reporting/crash-breadcrumb-store'
|
|
|
|
const VALIDATION_BACKOFF_TTL_MS = 5 * 60_000
|
|
const MAX_VALIDATION_BACKOFF_ENTRIES = 256
|
|
const VALIDATION_BREADCRUMB_MIN_INTERVAL_MS = 30_000
|
|
|
|
export type PRRefreshValidationDenialReason =
|
|
| 'unknown-repo'
|
|
| 'repo-path-mismatch'
|
|
| 'host-mismatch'
|
|
|
|
type ValidationBackoffIdentity = {
|
|
repoId?: string | null
|
|
repoPath: string
|
|
reason: PRRefreshValidationDenialReason
|
|
}
|
|
|
|
type ValidationBackoffEntry = {
|
|
expiresAt: number
|
|
}
|
|
|
|
type ValidationBackoffCounters = {
|
|
recorded: number
|
|
skipped: number
|
|
expired: number
|
|
}
|
|
|
|
const validationBackoff = new Map<string, ValidationBackoffEntry>()
|
|
const counters: ValidationBackoffCounters = {
|
|
recorded: 0,
|
|
skipped: 0,
|
|
expired: 0
|
|
}
|
|
|
|
function validationIdentityKey(identity: ValidationBackoffIdentity): string {
|
|
return [identity.repoId ?? '', resolve(identity.repoPath), identity.reason].join('\0')
|
|
}
|
|
|
|
function validationIdentityToken(key: string): string {
|
|
return createHash('sha256').update(key).digest('hex').slice(0, 12)
|
|
}
|
|
|
|
function evictOldestValidationBackoffEntries(): void {
|
|
while (validationBackoff.size > MAX_VALIDATION_BACKOFF_ENTRIES) {
|
|
const oldest = validationBackoff.keys().next()
|
|
if (oldest.done) {
|
|
break
|
|
}
|
|
validationBackoff.delete(oldest.value)
|
|
}
|
|
}
|
|
|
|
function recordValidationBreadcrumb(
|
|
reason: PRRefreshValidationDenialReason,
|
|
result: 'recorded' | 'backoff',
|
|
token: string
|
|
): void {
|
|
recordCoalescedCrashBreadcrumb({
|
|
name: 'pr_refresh_validation_skip',
|
|
coalesceKey: `pr-refresh-validation:${reason}:${token}`,
|
|
minIntervalMs: VALIDATION_BREADCRUMB_MIN_INTERVAL_MS,
|
|
data: {
|
|
reason,
|
|
result,
|
|
token,
|
|
recorded: counters.recorded,
|
|
skipped: counters.skipped,
|
|
expired: counters.expired
|
|
}
|
|
})
|
|
}
|
|
|
|
export function notePRRefreshValidationDenial(
|
|
identity: ValidationBackoffIdentity,
|
|
nowMs = Date.now()
|
|
): 'validation-denied' | 'validation-backoff' {
|
|
const key = validationIdentityKey(identity)
|
|
const existing = validationBackoff.get(key)
|
|
const token = validationIdentityToken(key)
|
|
if (existing && existing.expiresAt > nowMs) {
|
|
counters.skipped += 1
|
|
recordValidationBreadcrumb(identity.reason, 'backoff', token)
|
|
return 'validation-backoff'
|
|
}
|
|
if (existing) {
|
|
counters.expired += 1
|
|
validationBackoff.delete(key)
|
|
}
|
|
counters.recorded += 1
|
|
validationBackoff.set(key, { expiresAt: nowMs + VALIDATION_BACKOFF_TTL_MS })
|
|
evictOldestValidationBackoffEntries()
|
|
recordValidationBreadcrumb(identity.reason, 'recorded', token)
|
|
return 'validation-denied'
|
|
}
|
|
|
|
export function clearPRRefreshValidationBackoffForTests(): void {
|
|
validationBackoff.clear()
|
|
counters.recorded = 0
|
|
counters.skipped = 0
|
|
counters.expired = 0
|
|
}
|
|
|
|
export function getPRRefreshValidationBackoffCountForTests(): number {
|
|
return validationBackoff.size
|
|
}
|