Seed relay fetches plain overviews by default
For 2026 a.b.ath articles Eweka's gzip replies have a long tail (p90 12.4 s per 20k vs 3.9 s plain; 6.1k vs 10.5k headers/s). SEED_NNTP_COMPRESS turns compression back on; plain replies are deflated for nodes asynchronously. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7893876435
commit
3e6b5bed56
5 files changed
+21
-5
No files matched your search
@@ -31,7 +31,8 @@ class SeedFetchPool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A failure is retried once without compression, on a connection of its
|
* Pooled connections fetch compressed only with SEED_NNTP_COMPRESS (plain is
|
||||||
|
* faster for recent articles). A failure is retried once without compression, on a connection of its
|
||||||
* own: either the pooled connection was dead (dropped while idle), or the
|
* own: either the pooled connection was dead (dropped while idle), or the
|
||||||
* server stalled mid-reply in compressed mode. Eweka does that for some
|
* server stalled mid-reply in compressed mode. Eweka does that for some
|
||||||
* chunks: it stops after most of the stream and never sends the rest
|
* chunks: it stops after most of the stream and never sends the rest
|
||||||
@@ -105,7 +106,7 @@ class SeedFetchPool {
|
|||||||
if (this.open < this.size) {
|
if (this.open < this.size) {
|
||||||
this.open++
|
this.open++
|
||||||
try {
|
try {
|
||||||
return { client: await this.connect(true), usedAt: Date.now() }
|
return { client: await this.connect(indexerConfig.seed.nntpCompress), usedAt: Date.now() }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.open--
|
this.open--
|
||||||
this.waiters.shift()?.()
|
this.waiters.shift()?.()
|
||||||
|
|||||||
@@ -72,6 +72,12 @@ export default {
|
|||||||
* answered" in 10 minutes came in 0.3 s on a fresh connection.
|
* answered" in 10 minutes came in 0.3 s on a fresh connection.
|
||||||
*/
|
*/
|
||||||
nntpTimeoutMs: env.get('SEED_NNTP_TIMEOUT_MS', 90 * 1000),
|
nntpTimeoutMs: env.get('SEED_NNTP_TIMEOUT_MS', 90 * 1000),
|
||||||
|
/**
|
||||||
|
* Ask the news server for gzip-compressed overviews. Off: for 2026 articles Eweka's
|
||||||
|
* compressed replies have a long tail (p90 12.4 s per 20k, against 3.9 s plain; 6.1k
|
||||||
|
* against 10.5k headers/s, LOG.md 2026-10-06 (9)). Nodes get gzip from the relay either way.
|
||||||
|
*/
|
||||||
|
nntpCompress: env.get('SEED_NNTP_COMPRESS', false),
|
||||||
},
|
},
|
||||||
|
|
||||||
backfill: {
|
backfill: {
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
|
import { promisify } from 'node:util'
|
||||||
import net from 'node:net'
|
import net from 'node:net'
|
||||||
import tls from 'node:tls'
|
import tls from 'node:tls'
|
||||||
import { deflateSync, unzipSync } from 'node:zlib'
|
import { deflate, deflateSync, unzipSync } from 'node:zlib'
|
||||||
|
|
||||||
|
const deflateAsync = promisify(deflate)
|
||||||
|
|
||||||
export interface XoverClientOptions {
|
export interface XoverClientOptions {
|
||||||
host: string
|
host: string
|
||||||
@@ -100,7 +103,9 @@ export class XoverClient {
|
|||||||
const reply = await this.command(`XOVER ${from}-${to}`)
|
const reply = await this.command(`XOVER ${from}-${to}`)
|
||||||
if (reply.startsWith('420') || reply.startsWith('423')) return deflateSync(Buffer.alloc(0))
|
if (reply.startsWith('420') || reply.startsWith('423')) return deflateSync(Buffer.alloc(0))
|
||||||
await this.expect(reply, ['224'])
|
await this.expect(reply, ['224'])
|
||||||
return reply.includes('COMPRESS=GZIP') ? (await this.compressedRaw()).raw : deflateSync(Buffer.from(await this.plainBody()))
|
if (reply.includes('COMPRESS=GZIP')) return (await this.compressedRaw()).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 })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ export default await Env.create(new URL('../', import.meta.url), {
|
|||||||
NNTP_TLS_SERVERNAME: Env.schema.string.optional(),
|
NNTP_TLS_SERVERNAME: Env.schema.string.optional(),
|
||||||
SEED_NNTP_CONNECTIONS: Env.schema.number.optional(),
|
SEED_NNTP_CONNECTIONS: Env.schema.number.optional(),
|
||||||
SEED_NNTP_TIMEOUT_MS: Env.schema.number.optional(),
|
SEED_NNTP_TIMEOUT_MS: Env.schema.number.optional(),
|
||||||
|
SEED_NNTP_COMPRESS: Env.schema.boolean.optional(),
|
||||||
|
|
||||||
// Indexer (config/indexer.ts)
|
// Indexer (config/indexer.ts)
|
||||||
BACKFILL_MAX_QUEUED: Env.schema.number.optional(),
|
BACKFILL_MAX_QUEUED: Env.schema.number.optional(),
|
||||||
|
|||||||
@@ -26,7 +26,9 @@ test.group('SeedFetchPool', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
await new Promise<void>((resolve) => server.listen(0, '127.0.0.1', resolve))
|
await new Promise<void>((resolve) => server.listen(0, '127.0.0.1', resolve))
|
||||||
const saved = { host: nntpConfig.host, port: nntpConfig.port, secure: nntpConfig.secure, connections: indexerConfig.seed.nntpConnections }
|
const saved = { host: nntpConfig.host, port: nntpConfig.port, secure: nntpConfig.secure, connections: indexerConfig.seed.nntpConnections, compress: indexerConfig.seed.nntpCompress }
|
||||||
|
// The fallback from a compressed pooled connection (SEED_NNTP_COMPRESS on).
|
||||||
|
indexerConfig.seed.nntpCompress = true
|
||||||
Object.assign(nntpConfig, { host: '127.0.0.1', port: (server.address() as net.AddressInfo).port, secure: false })
|
Object.assign(nntpConfig, { host: '127.0.0.1', port: (server.address() as net.AddressInfo).port, secure: false })
|
||||||
indexerConfig.seed.nntpConnections = 2
|
indexerConfig.seed.nntpConnections = 2
|
||||||
try {
|
try {
|
||||||
@@ -39,6 +41,7 @@ test.group('SeedFetchPool', () => {
|
|||||||
SeedFetchPool.closeIdle()
|
SeedFetchPool.closeIdle()
|
||||||
Object.assign(nntpConfig, { host: saved.host, port: saved.port, secure: saved.secure })
|
Object.assign(nntpConfig, { host: saved.host, port: saved.port, secure: saved.secure })
|
||||||
indexerConfig.seed.nntpConnections = saved.connections
|
indexerConfig.seed.nntpConnections = saved.connections
|
||||||
|
indexerConfig.seed.nntpCompress = saved.compress
|
||||||
server.close()
|
server.close()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in new issue
Block a user