Skip to content

Merge usage ledger updates across processes; release 0.9.1 - #25

Merged
DevMortimer merged 2 commits into
mainfrom
ledger-merge
Oct 4, 2026
Merged

DevMortimer merged 2 commits into
mainfrom
ledger-merge

Conversation

@DevMortimer

Copy link
Copy Markdown
Owner

Fixes lost counts in the usage ledger when several processes write it at once.

Each process read usage.json once and then wrote its whole in-memory copy over the file on every update, so the last writer won. With several sessions running at once, the spend that /typesafe status reports fell short of the real use, and the usdPerDay, requestsPerDay, and inputTokensPerDay caps did not stop at their limit.

  • Each recordStart, recordSuccess, and recordFailure now adds its delta to the file's current content under a lock file next to it (usage.json.lock, created exclusively), then writes atomically as before.
  • today(), blocked(), and describe() read the file, so a day cap counts every process.
  • An update waits at most 200 ms for the lock. A lock older than 5 seconds belongs to a process that died and is taken over. A delta that cannot be written in time stays in the process and is added with its next update, on the day it was recorded.
  • UsageLedger gains an optional flush() that adds any held delta, for a host that wants to drain it before exit.
  • Owner-only mode, atomic writes, the per-request costs from 0.9.0, the day rollover, and ledger files written by older versions are kept. Versions before 0.9.1 still write the whole file and can still overwrite counts; the changelog says so.

The last commit bumps the version to 0.9.1 and moves the changelog notes under ## 0.9.1.

Verification:

  • npm run check: 153 tests, all pass.
  • New tests: 4 processes making 200 start-and-success pairs each into one ledger end with exact totals (800 started, 800 succeeded, exact tokens and cost); the same test against the previous code kept 200 of 800. Also: a cap sees the usage another process recorded, a stale lock is taken over, a held lock delays an update at most the bound and its delta is added later, a held delta lands on the day it was recorded, and a ledger file without cost fields is still read and extended.
  • 1,000 updates in one process cost about 0.25 to 0.4 ms more per update than before (lock, read, rename).

Each process read usage.json once and wrote its own copy over the file, so
concurrent processes lost each other's counts and the day caps saw only one
process. Updates now add their delta to the file's current content under a
lock file, and today(), blocked(), and describe() read the file.
@DevMortimer
DevMortimer merged commit 56373f0 into main Oct 4, 2026
14 of 16 checks passed
@DevMortimer
DevMortimer deleted the ledger-merge branch October 4, 2026 00:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant