fix(stats): cap retained events before asynchronous persistence

This commit is contained in:
m4air
2026-09-15 20:29:30 -07:00
parent ba5f93a3f9
commit c10b3df9e9
2 changed files with 44 additions and 0 deletions
@@ -131,6 +131,46 @@ describe('StatsCollector async debounced save', () => {
expect(JSON.parse(readFileSync(statsPath(), 'utf-8')).aggregates.totalAgentsSpawned).toBe(5)
})
it('bounds retained events while a stalled write prevents serialization', async () => {
vi.useFakeTimers()
const { StatsCollector, initStatsPath } = await importCollector()
initStatsPath()
const collector = new StatsCollector()
gate.blocked = true
collector.record({ type: 'agent_start', at: 0 })
await vi.advanceTimersByTimeAsync(5_000)
await vi.waitFor(() => expect(gate.writeFileCalls).toBe(1))
const expectedEvents = Array.from({ length: 10_000 }, (_, index) => ({
type: 'agent_start',
at: index + 10_001
}))
try {
for (let at = 1; at <= 20_000; at += 1) {
collector.record({ type: 'agent_start', at })
if (at % 5_000 === 0) {
await vi.advanceTimersByTimeAsync(5_000)
expect(Reflect.get(collector, 'events').length).toBeLessThanOrEqual(10_000)
}
}
expect(gate.writeFileCalls).toBe(1)
expect(Reflect.get(collector, 'events')).toEqual(expectedEvents)
} finally {
const flushed = collector.flushAsync()
gate.blocked = false
gate.waiters.splice(0).forEach((resolve) => resolve())
await flushed
}
const persisted = JSON.parse(readFileSync(statsPath(), 'utf-8'))
expect(persisted.events).toEqual(expectedEvents)
expect(persisted.aggregates).toMatchObject({
totalAgentsSpawned: 20_001,
firstEventAt: 0
})
})
it('retries a queued final snapshot after the active write fails', async () => {
const { StatsCollector, initStatsPath } = await importCollector()
initStatsPath()
+4
View File
@@ -67,6 +67,10 @@ export class StatsCollector {
record(event: StatsEvent): void {
this.events.push(event)
// A stalled async write must not defer the in-memory retention limit.
if (this.events.length > MAX_EVENTS) {
this.events.splice(0, this.events.length - MAX_EVENTS)
}
this.updateAggregates(event)
this.scheduleSave()
}