mirror of
https://github.com/stablyai/orca.git
synced 2026-09-29 16:02:50 +00:00
fix(crash-reporting): co-time the statfs by tick, not sample identity
A tick whose host read fails leaves the pre-gone sample object in place, so the identity check still read a 25 s-late statfs as co-timed.
This commit is contained in:
@@ -241,6 +241,43 @@ describe('pre-gone host memory', () => {
|
||||
}
|
||||
})
|
||||
|
||||
// Round 5: sample identity alone could not see these ticks. A host read that
|
||||
// returns nothing leaves the sample object in place, so `sample === issuedFor`
|
||||
// still held 25 s and two ticks later and the statfs re-qualified the verdict.
|
||||
it('will not let ticks with a failed host read pass a stale statfs off as co-timed', async () => {
|
||||
const platform = Object.getOwnPropertyDescriptor(process, 'platform')!
|
||||
Object.defineProperty(process, 'platform', { configurable: true, value: 'win32' })
|
||||
vi.useFakeTimers()
|
||||
let resolveVolume: (value: SwapVolumeFreeSpace) => void = () => {}
|
||||
try {
|
||||
setSystemMemoryInfoReaderForTest(() => BEFORE_THE_STORM)
|
||||
setSwapVolumeFreeSpaceReaderForTest(
|
||||
() =>
|
||||
new Promise<SwapVolumeFreeSpace>((resolve) => {
|
||||
resolveVolume = resolve
|
||||
})
|
||||
)
|
||||
void samplePreGoneSystemMemory(0)
|
||||
|
||||
// GlobalMemoryStatusEx starts failing: the sample is neither replaced nor erased.
|
||||
setSystemMemoryInfoReaderForTest(() => null)
|
||||
await samplePreGoneSystemMemory(10_000)
|
||||
await samplePreGoneSystemMemory(20_000)
|
||||
|
||||
resolveVolume({ freeMB: 40_000, volume: 'C:' })
|
||||
await vi.advanceTimersByTimeAsync(0)
|
||||
|
||||
vi.setSystemTime(25_000)
|
||||
const details = buildProcessGoneCrashDetails({}, 'renderer')
|
||||
// 25 s of lag: the label must not say co-timed beside that age.
|
||||
expect(details.systemMemoryPreGoneSwapVolumeAgeMs).toBe(25_000)
|
||||
expect(details.systemMemoryPreGonePressureSignal).toBe('available-commit-unqualified')
|
||||
} finally {
|
||||
vi.useRealTimers()
|
||||
Object.defineProperty(process, 'platform', platform)
|
||||
}
|
||||
})
|
||||
|
||||
it("arms the host sampler on its own unref'd 10 s timer, not the metric sweep's", async () => {
|
||||
vi.useFakeTimers()
|
||||
vi.setSystemTime(0)
|
||||
|
||||
@@ -30,6 +30,7 @@ let preGoneSample: PreGoneSystemMemorySample | null = null
|
||||
let preGoneTimer: ReturnType<typeof setInterval> | null = null
|
||||
let swapVolumeReadInFlight = false
|
||||
let samplingGeneration = 0
|
||||
let sampleTick = 0
|
||||
|
||||
const PRESSURE_SIGNAL_KEY = `${SYSTEM_MEMORY_KEY_PREFIX}PressureSignal`
|
||||
|
||||
@@ -74,7 +75,7 @@ function commitHostMemorySample(nowMs: number): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
async function mergeSwapVolumeFreeSpace(): Promise<void> {
|
||||
async function mergeSwapVolumeFreeSpace(issuedOnTick: number): Promise<void> {
|
||||
if (swapVolumeReadInFlight) {
|
||||
return
|
||||
}
|
||||
@@ -87,7 +88,9 @@ async function mergeSwapVolumeFreeSpace(): Promise<void> {
|
||||
// Why only its own tick qualifies: a statfs that outlived its tick carries a
|
||||
// pre-storm volume number, and the latch makes that lag unbounded. It still
|
||||
// ships beside its age, but it may not decide the verdict.
|
||||
const coTimed = preGoneSample === issuedFor
|
||||
// Why the tick counter and not sample identity: a tick whose host read fails
|
||||
// leaves the sample object in place, so identity alone reads as co-timed.
|
||||
const coTimed = issuedOnTick === sampleTick
|
||||
preGoneSample = {
|
||||
...preGoneSample,
|
||||
details: withSwapVolumeFreeSpace(preGoneSample.details, volume, process.platform, coTimed),
|
||||
@@ -105,10 +108,11 @@ export async function samplePreGoneSystemMemory(nowMs: number = Date.now()): Pro
|
||||
// Why commit before awaiting: the volume read is a statfs, and under the very
|
||||
// paging storm this targets it is slowest — it must never delay, or (via an
|
||||
// in-flight latch) skip, the cheap synchronous host reading.
|
||||
const tick = ++sampleTick
|
||||
if (!commitHostMemorySample(nowMs)) {
|
||||
return
|
||||
}
|
||||
await mergeSwapVolumeFreeSpace()
|
||||
await mergeSwapVolumeFreeSpace(tick)
|
||||
}
|
||||
|
||||
export function startPreGoneSystemMemorySampling(
|
||||
|
||||
Reference in New Issue
Block a user