Files
usenet-indexer/tests/unit/nntp/hedge.spec.ts
T
Daan MeijerandClaude Opus 5.5 8475969145
Test and deploy / test (push) Successful in 24s
Test and deploy / deploy (push) Skipped
Rebuild: hedged chunk fetches and connection max age (both off by default)
A chunk not in after NNTP_HEDGE_AFTER_SECONDS (10) is asked again on one
of NNTP_HEDGE_CONNECTIONS spare connections; the first answer wins and
the other connection is closed (lib/nntp/hedge.ts, tested). Connections
can be retired after NNTP_CONNECTION_MAX_MINUTES (±25%). The prefetch
report counts hedges and slow first requests by connection age.

Homelab, 15 min each: hedging on (40 + 6) 280k articles/s, off 273k.
The second request won about half of its races, but ~25-35% of first
requests were slow (>10 s) at every connection age, so neither helps:
Eweka is slow under our load, not on particular connections. Both
off by default; the age statistics stay.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 22:13:44 +02:00

51 lines
2.3 KiB
TypeScript

import { test } from '@japa/runner'
import { hedge, type Request } from '#lib/nntp/index'
/** A request answering `value` after ms (or failing), recording whether it was cancelled. */
function request<T>(ms: number, value: T, fail = false) {
const r = { cancelled: false } as Request<T> & { cancelled: boolean }
r.done = new Promise<T>((resolve, reject) => setTimeout(() => (fail ? reject(new Error(String(value))) : resolve(value)), ms))
r.cancel = () => (r.cancelled = true)
return r
}
test.group('nntp / hedge', () => {
test('a quick answer: no second request', async ({ assert }) => {
let asked = 0
const result = await hedge(() => request(5, 'a'), () => (asked++, request(5, 'b')), 50)
assert.deepEqual(result, { value: 'a', late: false, hedged: false, secondWon: false })
assert.equal(asked, 0)
})
test('a stalled request: the second answers, the first is cancelled', async ({ assert }) => {
const first = request(1000, 'a')
const result = await hedge(() => first, () => request(10, 'b'), 20)
assert.deepEqual(result, { value: 'b', late: true, hedged: true, secondWon: true })
assert.isTrue(first.cancelled)
})
test('late, but the first still wins: the second is cancelled', async ({ assert }) => {
const second = request(1000, 'b')
const result = await hedge(() => request(40, 'a'), () => second, 20)
assert.deepEqual(result, { value: 'a', late: true, hedged: true, secondWon: false })
assert.isTrue(second.cancelled)
})
test('no connection free: waits for the first', async ({ assert }) => {
const result = await hedge(() => request(40, 'a'), () => null, 10)
assert.deepEqual(result, { value: 'a', late: true, hedged: false, secondWon: false })
})
test('one fails: the other answers; both fail: the first error', async ({ assert }) => {
const result = await hedge(() => request(30, 'a', true), () => request(40, 'b'), 10)
assert.equal(result.value, 'b')
await assert.rejects(() => hedge(() => request(30, 'a', true), () => request(40, 'b', true), 10), 'a')
})
test('a failure before afterMs fails at once', async ({ assert }) => {
let asked = 0
await assert.rejects(() => hedge(() => request(5, 'a', true), () => (asked++, request(5, 'b')), 50), 'a')
assert.equal(asked, 0)
})
})