- seed:plan plans every part of a group's history no range covers (it started below the lowest known range and left a.b.ath's 13 most recent months out); seed_ranges.priority orders leases (seed:plan --priority). - binaries.part_count, kept by the parts insert in the same statement: claimCompleted compares it instead of counting parts every batch. - file_keys: 64-bit hashes of all file keys; FileStore.insert asks it before probing every files partition. - worker:file stores 100 completed binaries per job with set-based queries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
28 lines
1.1 KiB
TypeScript
28 lines
1.1 KiB
TypeScript
/**
|
|
* Seed planning arithmetic (CONCEPT.md D38), independent of the database.
|
|
*/
|
|
|
|
/** The parts of `from`..`to` (inclusive) that no range in `covered` (inclusive pairs) overlaps, oldest first. */
|
|
export function uncovered(from: number, to: number, covered: [number, number][]): [number, number][] {
|
|
const gaps: [number, number][] = []
|
|
let next = from
|
|
for (const [start, end] of [...covered].sort((a, b) => a[0] - b[0])) {
|
|
if (end < next) continue
|
|
if (start > to) break
|
|
if (start > next) gaps.push([next, Math.min(start - 1, to)])
|
|
next = Math.max(next, end + 1)
|
|
if (next > to) return gaps
|
|
}
|
|
if (next <= to) gaps.push([next, to])
|
|
return gaps
|
|
}
|
|
|
|
/** `gaps` cut into ranges of at most `size` articles, newest first (from the top of the newest gap down). */
|
|
export function cut(gaps: [number, number][], size: number): [number, number][] {
|
|
const ranges: [number, number][] = []
|
|
for (const [from, to] of [...gaps].sort((a, b) => b[1] - a[1])) {
|
|
for (let end = to; end >= from; end -= size) ranges.push([Math.max(from, end - size + 1), end])
|
|
}
|
|
return ranges
|
|
}
|