Repository navigation
Project Tango v3.1.1
Coming from 3.1.0? Read this before anything else. The two data-loss
defects fixed below are repaired automatically on first run: the missing seed
products are re-added and written back, with no manual step. See
3.1.0.
Bug fixes and release tooling. No tool names, parameters or response fields
changed. Two data-loss defects and one write failure, all found while trying to
make a server notice that another process had written, plus the workflow that
gives every release a verified artifact.
Fixed
- A server listed thirteen products and then refused to sell one of them.
Saved state can legitimately hold fewer products than the seed catalog — a
state.jsonwritten before a product was added, or one whose rows were pruned
— and the startup reconcile put the missing ones back in memory. A transaction
then re-read the store under its write lock and adopted that smaller row set
verbatim, so the catalog collapsed to whatever the file happened to contain,
andsimulate_order_placementansweredno product found with id "prod_001"
for a productlist_all_productshad just returned. The commit that followed
persisted the loss, so the erosion survived every restart. Reconciliation now
happens wherever stored state is adopted, and because a commit writes what is
in memory, the stored catalog is repaired rather than eroded. - A running server kept serving a catalog another process had replaced.
Reads came from the process's own copy, which only caught up when that process
wrote something itself — so an agent could place an order in one window and be
told stale inventory in the next. Reads now compare a cheap change token first
(PRAGMA data_version,staton the JSON backend) and reload only when
another process committed. The check is skipped during a transaction, where a
reload would discard state that has not committed yet.
Added
- Releases carry a verified installable tarball. A new workflow builds it,
installs it into a clean directory, drives the binary that install ships, and
only then attaches it to the release — onrelease: published, so it happens
without being asked. Two things this fixes:v3.1.0shipped source only, and
an artifact attached from a contributor's machine turned out to be corrupt
there (GitHub's asset endpoint stored the multipart envelope along with the
file on every framing tried, so a "successful" upload produced an unusable
download). Attaching from a runner also means the published file is the one
that was executed, not one built alongside the release and never run. StateStore.changeToken?()— an optional cheap token that moves when
another process committed. Optional on purpose: a backend that cannot detect
another writer keeps the previous behaviour instead of re-reading blindly.- Order ids carry their process id.
nextOrderIdproved unique only within
one process: its sequence is module state, so two servers sharing a data
directory minted the same id for orders placed in the same millisecond. With
orders.ida primary key, the stored list then held that id twice and the
second write was rejected outright — one server's order could not be placed at
all. The pid closes the gap with no randomness: two live processes cannot share
one, and a recycled pid only collides with an id from a different millisecond.
Fixed
- A rejected save reported itself as a successful one.
save()swallowed
every write failure and logged it, which is right for the environment (a
read-only disk, a closed handle) and wrong for the data:SQLITE_CONSTRAINT
means the rows are not storable, which is a defect in what was built rather
than where it is stored. That combination is how a caller comes to believe an
order was placed when the transaction behind it was rolled back — the exact
"silent partial write" this project promises cannot happen. Constraint failures
are now rethrown; environmental ones still degrade with a warning.