mirror of
https://github.com/stablyai/orca.git
synced 2026-10-09 08:02:35 +00:00
* fix(runtime): terminate renderer graph reload generations * fix(runtime): harden renderer reload teardown * fix(runtime): fence renderer graph publication ownership * test(runtime): register renderer graph reload gate * test(runtime): record live reload validation * fix(runtime): ignore cancelled renderer navigations * chore: preserve main formatting during branch sync * chore: satisfy changed-code quality gate * fix(runtime): restore cancelled renderer reloads * fix(runtime): preserve committed reload fencing * test(runtime): prove cancelled reload timeout * docs(reliability): record reload cancellation oracle --------- Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
88 lines
2.3 KiB
TypeScript
88 lines
2.3 KiB
TypeScript
export const RUNTIME_GRAPH_RELOAD_TIMEOUT_MS = 15_000
|
|
|
|
export type RuntimeGraphReloadOutcome = 'success' | 'failure' | 'cancelled' | 'timeout'
|
|
|
|
export type RuntimeGraphReloadSettlement = Readonly<{
|
|
revision: number
|
|
windowId: number
|
|
outcome: RuntimeGraphReloadOutcome
|
|
durationMs: number
|
|
}>
|
|
|
|
type ActiveRuntimeGraphReload = Readonly<{
|
|
revision: number
|
|
windowId: number
|
|
startedAt: number
|
|
timer: ReturnType<typeof setTimeout>
|
|
}>
|
|
|
|
export class RuntimeGraphReloadLifecycle {
|
|
private revision = 0
|
|
private active: ActiveRuntimeGraphReload | null = null
|
|
|
|
constructor(
|
|
private readonly options: {
|
|
timeoutMs: number
|
|
onSettled?: (settlement: RuntimeGraphReloadSettlement) => void
|
|
onTimeout?: (revision: number, windowId: number) => void
|
|
}
|
|
) {}
|
|
|
|
begin(windowId: number): number {
|
|
const cancelled = this.active ? this.finish(this.active.revision, 'cancelled') : null
|
|
|
|
const revision = ++this.revision
|
|
const startedAt = Date.now()
|
|
const timer = setTimeout(() => {
|
|
const settlement = this.finish(revision, 'timeout')
|
|
if (!settlement) {
|
|
return
|
|
}
|
|
this.options.onSettled?.(settlement)
|
|
this.options.onTimeout?.(revision, windowId)
|
|
}, this.options.timeoutMs)
|
|
timer.unref?.()
|
|
this.active = { revision, windowId, startedAt, timer }
|
|
if (cancelled) {
|
|
this.options.onSettled?.(cancelled)
|
|
}
|
|
return revision
|
|
}
|
|
|
|
settle(revision: number, outcome: RuntimeGraphReloadOutcome): boolean {
|
|
const settlement = this.finish(revision, outcome)
|
|
if (!settlement) {
|
|
return false
|
|
}
|
|
this.options.onSettled?.(settlement)
|
|
return true
|
|
}
|
|
|
|
private finish(
|
|
revision: number,
|
|
outcome: RuntimeGraphReloadOutcome
|
|
): RuntimeGraphReloadSettlement | null {
|
|
const active = this.active
|
|
if (!active || active.revision !== revision) {
|
|
return null
|
|
}
|
|
|
|
clearTimeout(active.timer)
|
|
this.active = null
|
|
return {
|
|
revision,
|
|
windowId: active.windowId,
|
|
outcome,
|
|
durationMs: Math.max(0, Date.now() - active.startedAt)
|
|
}
|
|
}
|
|
|
|
settleActive(outcome: RuntimeGraphReloadOutcome): boolean {
|
|
return this.active ? this.settle(this.active.revision, outcome) : false
|
|
}
|
|
|
|
getActiveRevision(): number | null {
|
|
return this.active?.revision ?? null
|
|
}
|
|
}
|