This content reflects minia2a as of its publication date and is kept for the record. Current model: x402 pay-per-call in USDC on Base only, 5 free trial calls per signed wallet, no credits and no top-up rail.
2026-10-27 · engineering note
There is a class of bug that looks like a deployment problem and is really a read-path problem.
The shape is always the same: a file on disk changes while the service is running, and the service
kept its copy in memory from boot. Two surfaces then disagree — the static artefact people fetch is
new, the in-memory view people query is old — and nobody sees an error, because both answers are
well-formed.
The tempting fix is five lines of cron: watch the file's checksum, and restart the process when it
moves. It works. It is also the wrong fix, and worth explaining why, because the reason is not
"restarts are scary".
What the restart actually does
A restart is not a read. It is a sequence of events with a blast radius:
- in-flight requests are dropped, including paid ones that had already been charged;
- in-process state is thrown away — rate windows, caches, warm connections;
- it happens at a wall-clock time nobody chose, chosen instead by the mtime of a file;
- and it converts a correctness bug (stale data) into an availability bug (a window where the service is simply not there).
If the restart fails halfway — a bad file, a port already bound, a supervisor that gives up after
N attempts — the process can stay down until a human notices. The trigger was a data refresh. The
outcome was an outage. Those two facts should never be connected by a cron entry.
A restart triggered by a file change is an action nobody authorised, taken at a time nobody
picked, to fix a problem that was never about availability.
The process can look at its own inputs
If the process is the thing that needs to know, let the process look. The cost of looking is
smaller than people assume, provided you put a ceiling on it:
// cache holds the bytes AND the stamp they came from
if enough time has passed since the last stat: // e.g. 5s, shared by all callers
if mtime or size moved:
reload under the lock
return the currently published snapshot
Three design points make this safe, and each one is a bug if you skip it:
-
Stat outside the lock, with a throttle. One shared counter decides who pays for
the
stat; everybody else reads the current snapshot and moves on. Without the throttle you have traded a file read per restart for a syscall per request, on your hottest path. - Publish immutable snapshots. A reload builds a new map and swaps the pointer. It never mutates the map that readers are holding. Then "reload while serving" needs no read lock at all, and no reader can observe a half-built map.
- A failed read must not overwrite a good snapshot. A writer truncating the file and writing it again means there is a moment when the file is half-written and parses as garbage. If that moment is allowed to replace your good in-memory copy with an empty one, your data refresh just blanked a served surface. Keep the previous snapshot; remember the stamp you failed on; retry only when the file changes again.
That third point is the one that separates "reload" from "reload that mostly works". The window
between truncate and flush is exactly when a naive watcher fires.
What it costs, honestly
| Case | Cost |
|---|---|
| no change | one stat per window, shared; zero file reads |
| file changed | one read + parse, at most once per window |
| staleness bound | one window (5s in our case) |
The window size is a choice, not a measurement. If a caller must see the change immediately, shrink
it — the cost scales with the number of stats, not with traffic, because the throttle is shared. What
you should not do is leave the window out and stat per request "to be safe".
And be honest about what this does not fix. A cold start against a broken file still has nothing
good to fall back to: the first load has no previous snapshot, so it latches whatever the file says,
including "empty". That case is a deploy-time validation problem, not a reload problem, and should be
caught before the process is allowed to start.
The rule
If your fix needs to interrupt production to take effect, you have not found a deployment step.
You have found a missing read path. Data that changes underneath a long-lived process needs a way to
be observed — bounded, immutable, and forgiving of a half-written file — and that way should live in
the process, not in a scheduler that reaches in and kills it.
A cron can restart a service. It cannot make it correct. The service has to look.
minia2a is a pay-per-call API marketplace for agents: x402 challenges, USDC on Base, no
accounts. The docs describe the payment path, including the
five signed trial calls each wallet gets before the first 402.
Originally published at minia2a.uk.
Top comments (0)