Seed relay: nodes get their headers through the seed API
The seed API keeps a pool of NNTP connections (SEED_NNTP_CONNECTIONS) and serves GET /api/v1/seed/ranges/:id/chunk?from=&to= for leased ranges: the gzip stream the news server sent, passed on unopened. seed:node fetches that way by default (SEED_FETCH=relay), so a node needs only a token and SEED_CONNECTIONS; SEED_FETCH=direct keeps its own NNTP connections. All fetching then comes from the indexer's IP (the account allows two). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3d5cfb2527
commit
41859636d3
13 files changed
+289
-33
No files matched your search
@@ -5,6 +5,8 @@ import testUtils from '@adonisjs/core/services/test_utils'
|
||||
import SeedOrchestrator from '#services/SeedOrchestrator'
|
||||
import QueueService from '#services/QueueService'
|
||||
import indexerConfig from '#config/indexer'
|
||||
import SeedFetchPool from '#services/SeedFetchPool'
|
||||
import { deflateSync } from 'node:zlib'
|
||||
import { segmentFromHeader } from '#lib/ingest/segment'
|
||||
import { FileAggregator } from '#lib/aggregate/index'
|
||||
import { toFragment, toSeedFile, type SeedBatch } from '#lib/seed/wire'
|
||||
@@ -99,4 +101,31 @@ test.group('Seed API', (group) => {
|
||||
assert.equal(row.status, 'queued')
|
||||
assert.equal(row.attempts, 0)
|
||||
})
|
||||
|
||||
test('relay: a leased range gets its overview chunks through the API', async ({ client, assert }) => {
|
||||
const token = await SeedOrchestrator.issueToken('relay-node')
|
||||
await db.table('seed_ranges').insert({ group_name: 'alt.binaries.test', start_id: 1, end_id: 100, next_id: 1 })
|
||||
const auth = { authorization: `Bearer ${token}` }
|
||||
const range = (await client.post('/api/v1/seed/lease').headers(auth)).body().range
|
||||
const fetch = SeedFetchPool.fetch
|
||||
const connections = indexerConfig.seed.nntpConnections
|
||||
const asked: unknown[] = []
|
||||
SeedFetchPool.fetch = async (group, from, to) => {
|
||||
asked.push([group, from, to])
|
||||
return deflateSync(Buffer.from('5\tsubject\r\n'))
|
||||
}
|
||||
try {
|
||||
indexerConfig.seed.nntpConnections = 0
|
||||
;(await client.get(`/api/v1/seed/ranges/${range.id}/chunk`).qs({ from: 1, to: 50 }).headers(auth)).assertStatus(503)
|
||||
indexerConfig.seed.nntpConnections = 1
|
||||
const chunk = await client.get(`/api/v1/seed/ranges/${range.id}/chunk`).qs({ from: 50, to: 500 }).headers(auth)
|
||||
chunk.assertStatus(200)
|
||||
// Clamped to the range.
|
||||
assert.deepEqual(asked, [['alt.binaries.test', 50, 100]])
|
||||
;(await client.get(`/api/v1/seed/ranges/${range.id}/chunk`).qs({ from: 200, to: 300 }).headers(auth)).assertStatus(400)
|
||||
} finally {
|
||||
SeedFetchPool.fetch = fetch
|
||||
indexerConfig.seed.nntpConnections = connections
|
||||
}
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user