Prober: pipelined bodies, merge-first order, a probe cache across resets
- XoverClient.bodies: PROBE_PIPELINE (8) BODY commands per connection at once, replies read in order (one at a time it ran 3-10 articles/s on 6 connections against a queue of 1M). - Claims in tiers: lone files first, then par2 sets and archives of releases without a readable name (what the par2 merge needs), then those of readable releases. - probe_cache: final answers by message-id (status, inspection with the 16 KiB hash and par2 file list), PROBE_VERSION 2. A reset empties the probe queue, not the cache; recreated candidates of probed articles are answered from it without the news server. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6d1f2cc2a1
commit
0727afe968
9 files changed
+160
-34
No files matched your search
@@ -246,6 +246,28 @@ export class XoverClient {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Several bodies, pipelined: every BODY is sent at once and the replies read in order, so one round trip
|
||||
* covers them all (one at a time, a connection waited ~0.1–2 s per article: 3–10 articles/s on 6; LOG.md
|
||||
* 2026-10-12 (16)). A missing article (430, 423) is null.
|
||||
*/
|
||||
async bodies(messageIds: string[]): Promise<(Buffer | null)[]> {
|
||||
return this.timed(async () => {
|
||||
this.sock.write(messageIds.map((m) => `BODY ${m.startsWith('<') ? m : `<${m}>`}\r\n`).join(''))
|
||||
const out: (Buffer | null)[] = []
|
||||
for (let i = 0; i < messageIds.length; i++) {
|
||||
const reply = await this.line()
|
||||
if (reply.startsWith('430') || reply.startsWith('423')) {
|
||||
out.push(null)
|
||||
continue
|
||||
}
|
||||
await this.expect(reply, ['222'])
|
||||
out.push(await this.rawBody())
|
||||
}
|
||||
return out
|
||||
})
|
||||
}
|
||||
|
||||
/** A multi-line body up to "\r\n.\r\n", as bytes, the terminator cut off. */
|
||||
private async rawBody(): Promise<Buffer> {
|
||||
let searched = 0
|
||||
|
||||
Reference in new issue
Block a user