baa7748e - Hand the latest balance over at write time instead of re-parsing it - #4520
Conversation
The Financial Overview key figures and the provider chart are served from a snapshot the job has just written itself — yet the endpoint re-read that row and re-parsed its 43 kB of JSON on every one of the 1,440 daily requests. Measured in production: 23 ms median for the real work, but 1,989 ms at p95, because the request queues behind whatever else the database is doing. The job now hands the finished result over directly. The endpoint returns it and never touches the database. There is deliberately no fallback: when the store is empty — the minute after a restart, before the job first runs — it answers as it already does today whenever data is missing, which is half of all calls. A fallback would run once per deploy, never be tested, and hide exactly the case it claims to handle.
144 of 430 production assets have approxPriceChf NULL, which is what feeds AssetLog.priceChf - the type says number, the column is nullable. Moving the aggregation off the JSON round-trip means that value now reaches the arithmetic directly instead of after a stringify/parse pass, so the case is worth pinning down rather than assuming. Two shapes, because they fail differently: an asset alone on its blockchain must drop out entirely (its group rounds to 0 and is skipped), and one sitting next to a priced asset must leave that group's total and breakdown exactly as if it were absent.
|
Two completed review passes on this branch (correctness and conformance). One finding was acted on: One deliberate non-change: a A further pass over the final commit is still running. Anything it turns up will be handled in a |
GET /v1/dashboard/financial/latestserves the four key figures and the "Liquidity by Provider" chart of the Financial Overview screen. The screen is open around the clock and calls it every minute.The odd part
The endpoint re-reads the newest
FinancialDataLogrow and re-parses its ~43 kB of JSON on every call — a row the job wrote itself sixty seconds earlier, with every number in hand at that moment.Measured in production over six hours, split by status:
Note the gap between median and p95: same document, same aggregation, same asset lookup — a factor of 86. That is not the endpoint being slow, that is the endpoint waiting while the database is busy elsewhere.
What changes
The job hands the finished result over right after writing the snapshot. The endpoint returns it and never touches the database.
The aggregation itself is not reimplemented — the same code path produces the value for both, so the two cannot drift apart. The job uses data it has already loaded, so this adds no database query on the write side either.
No fallback, deliberately
When the store is empty — the minute after a restart, before the job first runs — the endpoint answers exactly as it already does when data is missing today. That is not an edge case here: 308 of 620 calls already end that way.
A fallback to the database would run roughly once per deploy, for about a minute, and would therefore never be exercised in tests. That is precisely the kind of quiet special path that surprises people later.
Failure isolation
The surrounding job arms the safety mode and halts trading when certain blocks throw. Handing over the display value is not one of those: it sits in its own
try/catch, logs loudly, and never rethrows. A problem here must not stop trading — but it must not vanish silently either.Scope
This is one of three changes to the same screen. The other two: chart values moving into their own columns (#4519) and a master switch for the ledger jobs, which currently occupy about a third of production database time.