Skip to content

baa7748e - Hand the latest balance over at write time instead of re-parsing it - #4520

Merged
TaprootFreak merged 2 commits into
developfrom
perf/latest-balance-write-through
Jul 30, 2026
Merged

baa7748e - Hand the latest balance over at write time instead of re-parsing it#4520
TaprootFreak merged 2 commits into
developfrom
perf/latest-balance-write-through

Conversation

@TaprootFreak

Copy link
Copy Markdown
Collaborator

GET /v1/dashboard/financial/latest serves 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.

Draft — in progress. Verification and the double review follow.

The odd part

The endpoint re-reads the newest FinancialDataLog row 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:

status n median p95 max
200 (real work) 256 23 ms 1,989 ms 6,802 ms
304 52 20 ms 416 ms 1,050 ms
204 (no data) 308 0.1 ms 0 ms 4 ms

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.

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.
@TaprootFreak

Copy link
Copy Markdown
Collaborator Author

Two completed review passes on this branch (correctness and conformance). One finding was acted on:
the case that actually occurs in production — 144 of 430 assets carry no price — was not covered by
any test, so a regression guard was added for it.

One deliberate non-change: a Number.isFinite guard on the price was considered and rejected. It
would book a broken price as zero, which is the silent masking this codebase avoids; a non-finite
value now surfaces as an absent number in the response instead of a plausible wrong one. Checked
against production: of 430 asset rows, 0 hold NaN or Infinity, 144 hold NULL — and NULL multiplies
to zero, which is what the previous JSON round-trip produced as well.

A further pass over the final commit is still running. Anything it turns up will be handled in a
follow-up PR rather than held against this one.

@TaprootFreak
TaprootFreak marked this pull request as ready for review July 30, 2026 19:59
@TaprootFreak
TaprootFreak merged commit fc60ca3 into develop Jul 30, 2026
12 checks passed
@TaprootFreak
TaprootFreak deleted the perf/latest-balance-write-through branch July 30, 2026 20:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant