Skip to content

v0.2.0 — writes that do not lose each other

Choose a tag to compare

@javimosch javimosch released this 05 Sep 20:38
· 38 commits to master since this release
1a5fe3c

Atomic update operators, preconditions, self-bounding collections, grouped counts, and a scheduler fix — all of it from fit-checking bkn against a 131k-line control plane, asking not "could it run on bkn" but "would it be less code on bkn".

patch was a lost update

It read a document, merged in Go, and wrote the whole thing back, so concurrent patches erased each other. Measured against the previous code:

before now
16 concurrent $inc tries = 1 — 15 updates lost tries = 16
two patches, two fields one erased the other both survive
12 contenders claiming a job all 12 "won" it exactly 1

Fields may now carry operators computed from the current value, written under compare-and-set:

bkn store patch app/runs r1 --data '{"tries":{"$inc":1},"log":{"$append":"started\n"}}'
bkn store patch app/runs r1 --data '{"status":"done"}' --if status=running
bkn store patch app/runs r1 --data '{"worker":"w1"}'   --if-absent worker   # claims it exactly once

A plain object is still a plain value — only a single $-prefixed key is an operator. A failed precondition writes nothing and exits 95.

Collections that bound themselves

The trim query and the job that ran it both go away:

bkn store create app/memories --retain-last 20 --retain-per tag,repo_id,user_id

Enforced on every write and the moment the policy is declared, so a bound set on a collection that already holds a million documents applies immediately.

Rollups

How many, not which — one field, one aggregate, never documents:

bkn store count app/runs --where repo_id=7 --by status

total counts matching documents and groups counts distinct values before any limit, so truncation is visible rather than silent.

@every fired early, and sometimes twice

Schedule.Next rounded intervals down to the second to match second-resolution storage, so an @every 1s job ticked at :01.999 was scheduled 1 ms later and re-fired for the rest of that second. It rounds up now: late is harmless, early is a bug. Observed in a live deployment — one pair of runs in the same second across 11,246.

What it deliberately will not do

AGENTS.md gained a Deliberate omissions section, each entry argued from the codebase that asked for it: no transactions (a transaction is caller-held state, and a one-shot CLI over a stateless API has nowhere to hold it — locks is the multi-statement answer), no joins (denormalize), no regex/LIKE, no multi-field sort (ordering is already total), no age-based retention.

Together these took the fit-checked codebase from 33 statements beyond the store's surface to 11 — and all 11 were refused rather than absorbed. Three primitives, no query language.

Upgrading

Schema change: two columns on collections and one index, applied automatically on open. Verified against a copy of a live 16 MB database — 140 documents and 23,156 events read back byte-identical, and the previous binary can still read and write the migrated file, so a rollback is safe.

curl -L https://github.com/javimosch/bkn/releases/download/v0.2.0/bkn -o bkn && chmod +x bkn
./bkn install

Linux x86_64, statically linked, no runtime dependencies.

bkn update does not use these tags — versions are content hashes served at GET /version and /dl/bkn. A tag is for people; the hash is what a machine updates against.