Skip to content

queue: compact the append-only store and enforce the row invariant #103

Description

@pitimon

Measured on a real install

lines:        34,492
unique keys:   5,595
superseded:   28,897  (83.8%)
size:             11 MB

Five sixths of the file is dead weight. Every read path parses all of it.

src/lib/local-api.js:122-160 reads the entire queue.jsonl with readFileSync, re-parses every line and re-dedups, on every endpoint call. A dashboard refresh hits 6-8 endpoints; auto-refresh defaults to 30s. Nothing ever compacts — the only rewrite in the codebase is a one-off migration (sync.js:1836).

Appends are already deduped per totals-change (rollout.js:1541,1591), which is the right design for an append-only log. The gap is that superseded rows are never reclaimed.

This is not urgent today. It is monotonic, it is on the hot path of every render, and the fix carries close to zero design risk because the readers already define what the output must be: keep the last row per key. Compaction only has to produce what every reader already computes.

Proposal

sync --compact, and an automatic trigger when the superseded ratio crosses a threshold.

  • Write to a temp file and rename — the atomic-replace pattern already used for the project queue at sync.js:2040-2043.
  • Hold the sync lock for the whole operation. The mkdir mutex and heartbeat from fix: correct expired DeepSeek prices, enforce curated expiries, harden Host + sync lock #91 exist precisely so a long-running writer cannot be robbed mid-write; this is the second real user of that.
  • Report what it reclaimed. Silent compaction of the file the README tells users to cat is the wrong default.

Second half — enforce the schema invariant

CLAUDE.md:56-63 states the column invariant in prose:

total = input + output + cache_creation + cache_read + reasoning

Nothing enforces it at runtime. A miswritten or corrupt row is aggregated and rendered, not flagged — and a parser bug of this shape is exactly the class CLAUDE.md:169-172 records at 1.6-7× magnitude.

Add a doctor check validating every row against it, plus bucket alignment (30-minute UTC) and non-negative counts. Same conversion as the curated expiry in #91 and the version lockstep in #96: a rule that lived in a document starts running.

Definition of done

  • sync --compact reduces this install's 34,492 lines to ~5,595 with byte-identical API responses before and after — that equality is the real test, not the line count
  • Compaction is safe against a concurrent append (test with the sync lock held)
  • doctor fails on a fixture with a column-sum violation, a misaligned bucket, and a negative count
  • Interrupted compaction leaves the original intact (kill between write and rename)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions