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>
51 lines
2.3 KiB
TypeScript
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)
|
|
})
|
|
})
|