The window worker: group leases, the stream serving the corpus, the snapshot through the API (design 1 and 5)
A worker thread leases a group and takes its ranges in article order through one carried window; the coordinator keeps one cursor a group (group_leases, migration 6500) and hands out the next range, the rules and nothing about where the chunks are: the range stream serves a stored range's chunks from the corpus file and a live one's from the spool. The window's snapshot (bincode: the frontier, the open files, releases and albums, the upload numbers, the batch-counter table) goes to the coordinator every EDGE_SNAPSHOT_MINUTES and when the group is given back, and the next holder restores it; a takeover after LEASE_TTL_SECONDS plans the ranges from the snapshot's article again. The prefetcher runs ahead of held groups' cursors and skips chunks the corpus has. LEASE_MIN_PRIORITY..LEASE_MAX_PRIORITY bound what is leased. edge-node holds groups by default; --ranges keeps the old per-range path. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
3c4fe324a1
commit
1f6e3316ec
19 files changed
+1358
-31
No files matched your search
+56
-5
@@ -409,6 +409,31 @@ releases, a third of them one poster's image dump (27.7 M rows, LOG.md
|
||||
consecutive ranges of a.b.ath (2016): one range is 15 hours of posting,
|
||||
so without the carry no release would finish inside a range (LOG.md
|
||||
2026-10-26 (111)).
|
||||
**The group lease (2026-10-26, the user's question in LOG.md (117)):**
|
||||
a worker thread leases a *group*, not a range, and asks the coordinator
|
||||
for the group's next range each time it finished one; the coordinator
|
||||
keeps one cursor a group (`group_leases`: the holder, its heartbeat,
|
||||
the next range in article order, the snapshot it stands at). The
|
||||
answer is the range (id, span, resume point), the rules version and the
|
||||
stream URL — nothing about where the chunks are: the stream serves a
|
||||
stored range's chunks from the corpus file and a live one's from the
|
||||
spool, so a worker needs no mount and no corpus layout, and a remote
|
||||
worker is the same binary with another URL. The overhead is a copy
|
||||
through the node API: the corpus is ~30 B an article compressed (2016;
|
||||
16–18 for 2021 and 2025), eight local workers at the bench's rate are
|
||||
~170 MB/s, which the HDD under the corpus bounds either way; a remote
|
||||
worker is bounded by its link (100 Mbit ≈ 400k articles/s). History
|
||||
therefore no longer waits for the corpus fill: a group's stored ranges
|
||||
are read from disk, an unstored one is fetched live and captured into
|
||||
the corpus on the way, and the prefetcher runs ahead of each held
|
||||
group's cursor (plus the capture work left). A group with no planned
|
||||
range left is the tail: the worker keeps the group and asks again
|
||||
every minute, the planner's tail ranges fall into its order. A lease
|
||||
whose holder went quiet for LEASE_TTL_SECONDS is taken over: the new
|
||||
holder downloads the group's snapshot (5) and redoes the ranges since
|
||||
it; the batch store's keys make the redo idempotent. The replay
|
||||
harness (8) stays for offline runs; the driver on homelab goes once the
|
||||
worker runs.
|
||||
|
||||
2. **The window.** The worker reads a block in article order and keeps its
|
||||
open files and releases in memory (counters, names, sizes; the segment
|
||||
@@ -490,8 +515,31 @@ releases, a third of them one poster's image dump (27.7 M rows, LOG.md
|
||||
of writes, so the state is snapshotted at a lease's end and on a timer,
|
||||
to a file on bulk, and a crash redoes the ranges since the last snapshot
|
||||
from the corpus or the prefetch cache: the deterministic keys make the
|
||||
redo idempotent. Not built yet; the replay harness (8) keeps a group's
|
||||
window in one process.
|
||||
redo idempotent. **The snapshot goes through the API** (2026-10-26):
|
||||
`PUT /node/groups/:id/snapshot` with the serialized window (the
|
||||
frontier, the open files and releases, the open albums, the upload
|
||||
numbers, the batch-counter table; gzip) and the article it stands at
|
||||
(the last batch's `next`), kept by the coordinator on bulk as
|
||||
`<snapshot dir>/<group id>`; `GET` gives it back to the next holder. A
|
||||
worker snapshots when it gives a group back (a stop) and every
|
||||
SNAPSHOT_MINUTES while it holds one, always right after a batch was
|
||||
stored, so the snapshot's article is a resume point. The replay
|
||||
harness (8) keeps a group's window in one process without one.
|
||||
**Built 2026-10-26** (LOG.md (117)): migration 6500 `group_leases`;
|
||||
`app/services/group_leases.ts` (lease by the most urgent planned
|
||||
range within LEASE_MIN_PRIORITY..LEASE_MAX_PRIORITY, the longest free
|
||||
first; takeover after LEASE_TTL_SECONDS, the ranges from the snapshot's
|
||||
article on planned again; next; heartbeat; release; the snapshot
|
||||
streamed to `SNAPSHOT_DIR/<group id>.gz`); the node API's
|
||||
`groups/lease`, `groups/:id/next|heartbeat|release|snapshot`; the
|
||||
range stream serving a stored range's chunks from the corpus; the
|
||||
prefetcher ahead of held groups' cursors, skipping chunks the corpus
|
||||
has; `RangeIndexer::for_group/begin_range/snapshot/restore` (bincode,
|
||||
the files a table, SNAPSHOT_VERSION 1); `edge-node` holding groups by
|
||||
default (`--ranges` keeps the old path). Tests: the lease order,
|
||||
takeover and redo, the endpoints with a corpus-served stream and the
|
||||
snapshot through the API, the snapshot restoring a window between two
|
||||
ranges (the two-range release one, the album joined).
|
||||
|
||||
6. **The corpus stays as it is.** Measured on 300 unfiltered chunks
|
||||
(30 M lines, LOG.md (102)): 43% of lines lack a "(k/N)" counter, and of
|
||||
@@ -701,9 +749,12 @@ working on it as it fills; the scratch run's numbers (63 groups) were
|
||||
read before (LOG.md (115)). The tail waits for the window worker.
|
||||
|
||||
Next: the replay on homelab over the 2016 window (the NVMe bytes per
|
||||
header for real), the ordered range leases with the carried window in
|
||||
the edge-node and its snapshot (design 1 and 5), the prober writing
|
||||
`release_texts` kind 2, the spot/NZB match on pending keys (decision f). Gone from the list: block
|
||||
header for real), the window worker (design 1 and 5: group leases, the
|
||||
stream serving stored chunks, the snapshot through the API; being built,
|
||||
LOG.md (117)), the prober writing `release_texts` kind 2, the spot/NZB
|
||||
match on pending keys (decision f), the tail's provisional writes at a
|
||||
range end (the window's open releases shown while they upload: the files
|
||||
sent provisionally marked, the finish sending the rest). Gone from the list: block
|
||||
leases, the planner and the block merger (design 1, amended).
|
||||
|
||||
## Decisions
|
||||
|
||||
Reference in new issue
Block a user