The range stream polls the corpus as well as the spool while it waits
The corpus fill claims chunks of a leased range ahead of the leased run (the prefetcher's claim keys are per range and chunk), stores them in the corpus only, and the run leaves them there. The stream looked at the corpus once per chunk and then polled the spool only: when the store came seconds after that look, the stream waited its whole hour and the worker lost the group and its open batch (three groups on 2026-10-27, LOG.md (118)). Now the wait loop checks both. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
4af5e06400
commit
31fc204c76
3 files changed
+54
-11
No files matched your search
@@ -153,6 +153,38 @@ test.group('Node API', (group) => {
|
||||
}
|
||||
})
|
||||
|
||||
test('a chunk the corpus fill stores while the stream waits for it comes from the corpus', async ({ client, assert }) => {
|
||||
const corpus = await mkdtemp(join(tmpdir(), 'corpus-'))
|
||||
const saved = coordinatorConfig.corpus.dir
|
||||
coordinatorConfig.corpus.dir = corpus
|
||||
try {
|
||||
const [g] = await db.table('groups').insert({ name: GROUP, priority: 1 }).returning('id')
|
||||
// A capture range not in the corpus: its first chunk spooled by the leased run, the second claimed by
|
||||
// the corpus fill, which stores it in the corpus only, after the stream started.
|
||||
const [r] = await db.table('ranges').insert({ group_id: g.id, start_id: 1, end_id: 200, next_id: 1, capture: true }).returning('id')
|
||||
const rangeId = Number(r.id)
|
||||
await Spool.write(rangeId, { from: 1, to: 100 }, chunk(11))
|
||||
const node = await Node.create({ name: 'n1' })
|
||||
const token = (await Node.accessTokens.create(node)).value!.release()
|
||||
await db.from('ranges').where('id', rangeId).update({ status: 'leased', node_id: node.id, leased_at: new Date(), heartbeat_at: new Date() })
|
||||
|
||||
const pending = client.get(`/api/v1/node/ranges/${rangeId}/stream`).bearerToken(token)
|
||||
setTimeout(() => Corpus.write({ group: GROUP, start: 1, end: 200 }, { from: 101, to: 200 }, chunk(111), 2), 800)
|
||||
const stream = await pending
|
||||
stream.assertStatus(200)
|
||||
const raw = stream.body() as unknown
|
||||
const body: Buffer = Buffer.isBuffer(raw) ? raw : Buffer.from(stream.text(), 'binary')
|
||||
const frames = []
|
||||
for await (const frame of decodeFrames([body])) frames.push(frame)
|
||||
assert.deepEqual(frames.map((f) => [f.from, f.to]), [[1, 100], [101, 200]])
|
||||
assert.equal(parseOverviews(unzipSync(frames[1].data).toString('utf8')).length, 3)
|
||||
assert.isFalse(await Spool.has(rangeId, { from: 101, to: 200 }))
|
||||
} finally {
|
||||
coordinatorConfig.corpus.dir = saved
|
||||
await rm(corpus, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
test('a chunk that does not inflate: reported, removed from the spool, the range goes back', async ({ client, assert }) => {
|
||||
const [g] = await db.table('groups').insert({ name: GROUP, priority: 1 }).returning('id')
|
||||
const [r] = await db.table('ranges').insert({ group_id: g.id, start_id: 1, end_id: 300, next_id: 1, spooled_chunks: 3 }).returning('id')
|
||||
|
||||
Reference in new issue
Block a user