A missing first article is an inspection result, not a failure
Eweka answers 'no such article' after ~450 ms; a quarter of inspection requests were for missing articles and each failed job was retried up to three times. Files get content_type 'missing' instead; homelab also records it from failure reports of nodes not rebuilt yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
bef68075be
commit
61192690a7
6 files changed
+57
-5
No files matched your search
@@ -234,11 +234,28 @@ test.group('Seed API', (group) => {
|
||||
;(await client.post('/api/v1/seed/inspect/release').json({ queue: first.queue, jobId: first.jobId, token: first.token }).headers(auth)).assertStatus(200)
|
||||
const again = await lease()
|
||||
assert.equal(again.jobId, first.jobId)
|
||||
;(await client.post('/api/v1/seed/inspect/failed').json({ queue: again.queue, jobId: again.jobId, token: again.token, error: '430 no such article' }).headers(auth)).assertStatus(200)
|
||||
;(await client.post('/api/v1/seed/inspect/failed').json({ queue: again.queue, jobId: again.jobId, token: again.token, error: 'NNTP BODY: no answer within 120 s' }).headers(auth)).assertStatus(200)
|
||||
// Retried later (backoff), like a local failure.
|
||||
assert.include(['delayed', 'waiting'], await (await Job.fromId(QueueService.collectionQueue, again.jobId))!.getState())
|
||||
})
|
||||
|
||||
test('a missing first article reported by a node is recorded, not retried', async ({ client, assert }) => {
|
||||
await QueueService.collectionQueue.drain()
|
||||
await QueueService.archiveWalkQueue.drain()
|
||||
const token = await SeedOrchestrator.issueToken('inspect-node-3')
|
||||
const auth = { authorization: `Bearer ${token}` }
|
||||
const now = new Date()
|
||||
const [file] = await FileStore.insert([
|
||||
{ filename: 'Wq7n3LpX9vT2rK8mZ5cYbA.bin', file_key: 'inspect-node-file-3'.padEnd(40, '0'), poster: 'x', date: now.getTime(), parts: 1, groups: '[]', segments: [{ number: 1, id: 'gone@test', size: 1 }], created_at: now, updated_at: now },
|
||||
])
|
||||
await QueueService.collectionQueue.add('process-collection', { fileId: file.id }, { jobId: `node-test-${file.id}-${Date.now()}` })
|
||||
const job = (await client.post('/api/v1/seed/inspect/lease').json({ max: 1 }).headers(auth)).body().jobs[0]
|
||||
const error = 'Seed API /api/v1/seed/articles?id=gone%40test: 410 {"error":"No such article"}'
|
||||
;(await client.post('/api/v1/seed/inspect/failed').json({ queue: job.queue, jobId: job.jobId, token: job.token, error }).headers(auth)).assertStatus(200)
|
||||
assert.equal((await db.from('files').where('id', file.id).first()).content_type, 'missing')
|
||||
assert.equal(await (await Job.fromId(QueueService.collectionQueue, job.jobId))!.getState(), 'completed')
|
||||
})
|
||||
|
||||
test('a range reported to its end but never finished is marked done, not leased', async ({ client, assert }) => {
|
||||
const token = await SeedOrchestrator.issueToken('late-node')
|
||||
const [range] = await db.table('seed_ranges').insert({ group_name: 'alt.binaries.test', start_id: 1, end_id: 100, next_id: 101 }).returning('id')
|
||||
|
||||
@@ -71,9 +71,16 @@ test.group('content probe', () => {
|
||||
assert.lengthOf(probe.archive!.entries, 0)
|
||||
})
|
||||
|
||||
test('a missing article fails the probe (the job is retried)', async ({ assert }) => {
|
||||
test('a missing first article is a result, not a failure (no retries)', async ({ assert }) => {
|
||||
const { task, fetch } = posted('gone.bin', Buffer.alloc(10, 1))
|
||||
task.segments[0].id = 'missing@test'
|
||||
await assert.rejects(() => probeInspection(task, fetch), /430/)
|
||||
assert.include(await probeInspection(task, fetch), { contentType: 'missing' })
|
||||
// Through the seed relay: 410.
|
||||
const relayed = await probeInspection(task, async () => {
|
||||
throw new Error('Seed API /api/v1/seed/articles?id=x: 410 {"error":"No such article"}')
|
||||
})
|
||||
assert.equal(relayed.contentType, 'missing')
|
||||
// Anything else still fails (and is retried).
|
||||
await assert.rejects(() => probeInspection(task, async () => { throw new Error('NNTP BODY: no answer within 120 s') }), /no answer/)
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user