Skip to content

Commit 064b4aa

Browse files
committed
test(realtime): assert the retry delay, not a count inside a window
The streak-reset guard could pass on the very regression it exists to catch. It counted read attempts inside a 1900ms window, and the jittered delay for a carried streak is 1600–2400ms — so whenever jitter landed below about 0.95, a second read fell inside the window and the assertion held even though the idle reads had never cleared `failures`. A single falsification run happened to draw a long delay, which is exactly how a guard like this goes quiet. Assert the delay itself instead. The first retry after a reset is 500ms ±20% (400–600ms); carried over it is the third, 2000ms ±20% (1600–2400ms). Those ranges are disjoint, so the check no longer depends on which jitter is drawn: against the old placement it now fails every time (measured 2160ms, 1925ms, 2046ms against the 1000ms bound).
1 parent 0b7e0ad commit 064b4aa

1 file changed

Lines changed: 17 additions & 7 deletions

File tree

apps/realtime/src/handlers/file-doc-store.test.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ interface Backing {
2020
readerClosed: boolean
2121
/** Failed reads served, so a test can prove the loop is not spinning at the read cadence. */
2222
reads: number
23+
/** When each read was attempted, so a test can assert the BACKOFF rather than a count in a window. */
24+
readTimes: number[]
2325
/** `connect()` calls, so a test can prove a closed reader is re-opened rather than abandoned. */
2426
connects: number
2527
}
@@ -64,6 +66,7 @@ function makeClient(): any {
6466
},
6567
xRead: async (streams: { key: string; id: string }[]) => {
6668
b().reads++
69+
b().readTimes.push(Date.now())
6770
if (b().readerClosed) {
6871
client.isOpen = false
6972
throw new Error('The client is closed')
@@ -152,6 +155,7 @@ describe('FileDocStore', () => {
152155
failXAdd: 0,
153156
readerClosed: false,
154157
reads: 0,
158+
readTimes: [],
155159
connects: 0,
156160
}
157161
stores = []
@@ -205,15 +209,21 @@ describe('FileDocStore', () => {
205209
state.backing!.readerClosed = false
206210
await sleep(1000)
207211

208-
// A fresh blip must retry at the START of the backoff curve, not at the cap.
212+
// A fresh blip must retry at the START of the backoff curve, not partway up it. Assert the DELAY
213+
// itself: counting attempts inside a fixed window cannot tell the two apart, because the jittered
214+
// delay for a carried streak (1.6–2.4s) overlaps any window wide enough to catch a reset one.
209215
state.backing!.readerClosed = true
210-
const before = state.backing!.reads
211-
await sleep(1900)
212-
const attempts = state.backing!.reads - before
216+
state.backing!.readTimes.length = 0
217+
await vi.waitFor(() => expect(state.backing!.readTimes.length).toBeGreaterThanOrEqual(2), {
218+
timeout: 5000,
219+
interval: 50,
220+
})
221+
const [first, second] = state.backing!.readTimes
213222

214-
// Streak reset ⇒ retries at ~0, ~0.5s, ~1.5s ⇒ 3 attempts (2 even if the machine is loaded and
215-
// every sleep overshoots by half). Streak carried over ⇒ ~2s then ~4s ⇒ at most 1.
216-
expect(attempts).toBeGreaterThanOrEqual(2)
223+
// Streak reset ⇒ the first delay is 500ms ±20% ⇒ 400–600ms. Streak carried over ⇒ it is the third
224+
// delay, 2000ms ±20% ⇒ 1600–2400ms. Disjoint ranges, so this cannot pass on the wrong one without
225+
// the machine stalling the shorter sleep by 65%.
226+
expect(second - first).toBeLessThan(1000)
217227
doc.destroy()
218228
})
219229

0 commit comments

Comments
 (0)