Eweka drops quiet connections, so the next command on one failed
("Spot inspection failed: NNTP connection closed"), and idle connections
held account slots that downloads could use. The coordinator pool now
closes free connections (and the plain one) on its sync once idle for
60 s; dry prober lanes, the spotter between rounds and the spot inspector
with nothing to inspect close theirs too, reconnecting when work comes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
70 lines
2.3 KiB
TypeScript
70 lines
2.3 KiB
TypeScript
import { test } from '@japa/runner'
|
|
import net from 'node:net'
|
|
import env from '#start/env'
|
|
import coordinatorConfig from '#config/coordinator'
|
|
import pool from '#services/nntp_pool'
|
|
import type { NntpShare } from '#services/nntp_shares'
|
|
|
|
/** A tiny NNTP server that only logs in, counting the connections open. */
|
|
function server(): Promise<{ port: number; open: () => number; close: () => void }> {
|
|
return new Promise((resolve) => {
|
|
let open = 0
|
|
const srv = net.createServer((sock) => {
|
|
open++
|
|
sock.on('close', () => open--)
|
|
sock.write('200 welcome\r\n')
|
|
sock.on('data', (chunk) => {
|
|
for (const line of chunk.toString().split('\r\n')) {
|
|
if (line.startsWith('AUTHINFO USER')) sock.write('381 more\r\n')
|
|
else if (line.startsWith('AUTHINFO PASS')) sock.write('281 ok\r\n')
|
|
else if (line === 'XFEATURE COMPRESS GZIP') sock.write('290 Feature Enabled\r\n')
|
|
else if (line === 'QUIT') sock.end('205 bye\r\n')
|
|
}
|
|
})
|
|
})
|
|
srv.listen(0, '127.0.0.1', () =>
|
|
resolve({
|
|
port: (srv.address() as net.AddressInfo).port,
|
|
open: () => open,
|
|
close: () => srv.close(),
|
|
})
|
|
)
|
|
})
|
|
}
|
|
|
|
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms))
|
|
const share = { sync: async () => 4 } as unknown as NntpShare
|
|
|
|
test.group('nntp / pool', () => {
|
|
test('a connection free for nntpIdleCloseMs closes on the next sync; one used since stays', async ({
|
|
assert,
|
|
cleanup,
|
|
}) => {
|
|
const { port, open, close } = await server()
|
|
const idleCloseMs = coordinatorConfig.nntpIdleCloseMs
|
|
env.set('NNTP_HOST', '127.0.0.1')
|
|
env.set('NNTP_PORT', port)
|
|
env.set('NNTP_SECURE', false)
|
|
coordinatorConfig.nntpIdleCloseMs = 100
|
|
cleanup(async () => {
|
|
coordinatorConfig.nntpIdleCloseMs = idleCloseMs
|
|
await pool.close()
|
|
close()
|
|
})
|
|
await pool.share(share)
|
|
// Two at once: two connections.
|
|
await Promise.all([pool.use(() => sleep(20)), pool.use(() => sleep(20))])
|
|
assert.equal(open(), 2)
|
|
await sleep(150)
|
|
// One used again: the other has been free for longer than 100 ms.
|
|
await pool.use(async () => {})
|
|
await pool.share(share)
|
|
await sleep(50)
|
|
assert.equal(open(), 1)
|
|
await sleep(150)
|
|
await pool.share(share)
|
|
await sleep(50)
|
|
assert.equal(open(), 0)
|
|
})
|
|
})
|