Forecast from the right end of the window, and from the level shown
Some checks failed
CI / backend (push) Failing after 8s
CI / naming (push) Successful in 2s
CI / frontend (push) Successful in 8s
CI / migrations-mysql (push) Failing after 6s

Four defects stacked into one nonsense report: cartridges at 20% claiming four
days, cartridges at 1% claiming weeks.

The root cause is a Zabbix API detail. `limit` caps the whole result set rather
than each item, and the query sorted ascending, so the cap kept the OLDEST rows
in the window. A four-cartridge printer polled every five minutes writes over
100k readings in 90 days; the forecast was fitted to the first few days of that
and nothing since. Every rate was real and every rate described a cartridge
thrown away three months ago. Nothing in the output looks wrong, which is why
it needed pinning in a test rather than a comment.

A 90-day burn rate does not need every individual poll, so a long window now
reads hourly trends - the table meant for this, a tenth of the rows, and kept
longer. Raw history serves short windows and any item a site keeps no trends
for. Both are fetched newest-first with the budget scaled per item.

Second, the countdown was computed from the last stored reading while the level
displayed was the live one, so the two could disagree by a whole cartridge. The
live level is now what the countdown divides. A live level far above the stored
run means it was swapped since the last reading, and that is reported as a
replacement rather than as a collapse in the burn rate.

Third, at or below 5% a cartridge reads as empty rather than as a slow drain.
At 1% losing a tenth of a point a day the arithmetic says ten days. The printer
is out of toner, and it is the first thing to order.

Fourth, the days-left column spanned the printer's rows, so the printer's
soonest figure was printed beside every supply it had. That alone accounts for
the shape of both complaints: a healthy cartridge wearing its neighbour's
deadline, and an empty one wearing a number that belonged to nothing on its row.

Also fixes float-typed supplies vanishing from any printer that also had an
integer-typed one - they live in different history tables and the fetch stopped
at whichever answered first.

Not verified against live data: Zabbix is not reachable from the dev box.
This commit is contained in:
cproudlock
2026-08-13 10:54:23 -04:00
parent 1e884dc02a
commit 7d66551622
7 changed files with 376 additions and 25 deletions

View File

@@ -79,6 +79,33 @@ each fix ships with the check, report or document that would have surfaced it.
### Fixed
- The toner forecast was reading the wrong end of the window. Zabbix applies a
query's `limit` to the whole answer rather than to each item, and combined
with an ascending sort that kept the OLDEST rows: a four-cartridge printer
polled every five minutes writes over 100k readings in 90 days, so a "90 day"
forecast was fitted to the first few days of the window and nothing since.
That is where "4 days left" beside a cartridge at 20% came from - the rate
was real, it just described a cartridge thrown away in May. Long windows now
read hourly trends, which is the table meant for this and is a tenth of the
rows; short ones read raw history, newest first, with a budget scaled per
item.
- The forecast now counts down from the level it displays. It showed the live
reading but computed days-left from the last stored one, so the two could
disagree by a whole cartridge. A live level far above the stored run is
treated as a swap that happened since the last reading rather than as a
collapse in the burn rate.
- A cartridge at or below 5% reads as empty rather than as a slow drain. At 1%
losing a tenth of a point a day the arithmetic said ten days; the printer is
out of toner, and it is the first thing that should be ordered.
- Days-left in the forecast is per cartridge again. It was rendered in a cell
spanning the printer's rows, which put the printer's soonest figure beside
every supply it had - a cartridge at 20% displaying "4 days" that belonged to
the black beside it, and a cartridge at 1% displaying weeks that belonged to
nothing on that row at all.
- Supplies typed as floats were dropped from the forecast on any printer that
also had an integer-typed one. The two live in different Zabbix history
tables and the fetch stopped at whichever answered first, so half a printer's
cartridges silently had no history at all.
- A shop-floor PC that reported the machine number of a machine ShopDB already
knew got a 500 from the collector, on every report, forever. The reported
machine number was being written to the PC's own `assets.assetnumber`, which