Rebuild: prefetch report samples what the connections do
Once a second: pool connections with a command in flight, loops fetching/writing/idle; per request GROUP time, XOVER time to the status line and the transfer, transfers with a silence over 5 s. The old "connections busy" (fetch time of the chunks finished in the minute) said 59-75% while all 40 connections were in flight all the time: dropped. Homelab: 39-40 of 40 in flight, no idle loops; the status line comes at once, the whole 8-35 s is the transfer, with data flowing (few gaps over 5 s): ~3 MB per chunk at 0.1-0.4 MB/s per connection. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8475969145
commit
4bf526ab6e
3 files changed
+109
-16
No files matched your search
@@ -40,12 +40,26 @@ export class XoverClient {
|
||||
private wake?: () => void
|
||||
private failure?: Error
|
||||
compressed = false
|
||||
/**
|
||||
* The last xoverRaw's timing, ms: until the status line came (the server
|
||||
* thinking), from there to the end (the transfer), and the longest silence
|
||||
* during the transfer.
|
||||
*/
|
||||
lastTiming = { replyMs: 0, bodyMs: 0, longestGapMs: 0 }
|
||||
private measuring = false
|
||||
private lastDataAt = 0
|
||||
private longestGap = 0
|
||||
|
||||
private constructor(
|
||||
private sock: net.Socket,
|
||||
private timeoutMs: number
|
||||
) {
|
||||
sock.on('data', (chunk: Buffer) => {
|
||||
if (this.measuring) {
|
||||
const now = performance.now()
|
||||
this.longestGap = Math.max(this.longestGap, now - this.lastDataAt)
|
||||
this.lastDataAt = now
|
||||
}
|
||||
this.chunks.push(chunk)
|
||||
this.length += chunk.length
|
||||
this.wake?.()
|
||||
@@ -119,12 +133,22 @@ export class XoverClient {
|
||||
|
||||
async xoverRaw(from: number, to: number): Promise<Buffer> {
|
||||
return this.timed(async () => {
|
||||
const sentAt = performance.now()
|
||||
const reply = await this.command(`XOVER ${from}-${to}`)
|
||||
const repliedAt = performance.now()
|
||||
if (reply.startsWith('420') || reply.startsWith('423')) return deflateSync(Buffer.alloc(0))
|
||||
await this.expect(reply, ['224'])
|
||||
if (reply.includes('COMPRESS=GZIP')) return (await this.compressedRaw(false)).raw
|
||||
// Plain replies (the relay's default): compressed off the event loop, at the fastest level.
|
||||
return deflateAsync(Buffer.from(await this.plainBody()), { level: 1 })
|
||||
this.measuring = true
|
||||
this.lastDataAt = repliedAt
|
||||
this.longestGap = 0
|
||||
try {
|
||||
if (reply.includes('COMPRESS=GZIP')) return (await this.compressedRaw(false)).raw
|
||||
// Plain replies (the relay's default): compressed off the event loop, at the fastest level.
|
||||
return deflateAsync(Buffer.from(await this.plainBody()), { level: 1 })
|
||||
} finally {
|
||||
this.measuring = false
|
||||
this.lastTiming = { replyMs: repliedAt - sentAt, bodyMs: performance.now() - repliedAt, longestGapMs: this.longestGap }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user