mirror of
https://github.com/razzant/ouroboros.git
synced 2026-10-19 10:04:16 +00:00
The usage ledger was an append-only JSONL file folded whole by every process: each of the 41 processes parsed it at first use and again after every compaction swap, any append dropped every cached render, and a single-key question re-summed every live final row. Serving the readers from addressed records after reopen needed persisted per-key records, which the accepted design criterion resolves as one relational store. ouroboros/usage_store.py owns state/usage.sqlite: one row per attempt, updated on each transition under a revision check; summaries per global, root, group, task, provider, model, category and their unattributed and per-root/per-task axes, maintained in the writing transaction by subtracting the previous row's contribution from its keys and adding the new row's to the new keys, with the reducer the old fold used (cash as exact decimal text in Python arithmetic, counts and token presence weighted by an imported aggregate's folded count, cash and tokens added once); the earliest binding per root and group; dirty owners for the maintenance duty; one-shot identities; the publication marker continued from the journal. Admission reads two or three summary rows; displays read the same rows and report an unavailable fact after the short wait instead of serving a stale copy; dispatch rechecks known spend and the pause fence; the acquisition slices that check Stop and deadlines stay around the short transactions. Two lock tiers, decided once at import and recorded in the store: SQLite's own locks where the kernel enforces them, and no SQLite locking under the existing name-tier money lock on Drive, FUSE and NFS roots (durability there stays best effort, as the journal's appends were). The migration is a visible startup phase before any worker: it imports the last row of every attempt, aggregates with their weights, bindings and provenance, commits, marks the import complete, then renames the journal to state/usage_attempts.jsonl.imported; the archive stays on disk and is read only by the explicit seal audit. scripts/export_usage_journal.py writes a journal the previous validator accepts for a downgrade. Deleted with their replacements: usage compaction (the archive chain, anchors, segment cache and growth floors), the writer-view memo with its generation checks and stale-display backoff, the journal's tail quarantine, dense-sequence validation and incremental resume, the legacy import module, docs/USAGE_COMPACTION.md and the dead outputs. docs/USAGE_STORE.md describes the store.
37 lines
1.3 KiB
Python
37 lines
1.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Export the usage store as the journal an older Ouroboros release reads.
|
|
|
|
Usage: python scripts/export_usage_journal.py <data_root>
|
|
|
|
Run it OFFLINE, with the Ouroboros server stopped, as the step before checking
|
|
out a release older than the usage store (docs/USAGE_STORE.md "Downgrade"). It
|
|
writes ``state/usage_attempts.jsonl`` from ``state/usage.sqlite`` (a chain the
|
|
older validator accepts: each attempt's minimal legal transition chain ending in
|
|
its current row, imported aggregates under their baseline header, dense seq),
|
|
aligns the ``state.json`` freshness marker with it, and renames the store aside
|
|
so the next upgrade imports the journal again. A git revert alone is not a data
|
|
rollback. Prints one JSON report.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import pathlib
|
|
import sys
|
|
|
|
REPO_ROOT = pathlib.Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(REPO_ROOT))
|
|
|
|
|
|
def main(argv: list[str]) -> int:
|
|
if len(argv) != 1:
|
|
print(__doc__, file=sys.stderr)
|
|
return 2
|
|
root = pathlib.Path(argv[0]).expanduser().resolve()
|
|
from ouroboros.usage_store import export_journal
|
|
|
|
print(json.dumps(export_journal(root), sort_keys=True))
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main(sys.argv[1:]))
|