Skip to content

Follow-ups from the 2026-07-30 performance work #4531

Description

@TaprootFreak

Leftovers from the performance work merged on 2026-07-30 (#4519, #4520, #4521, #4527). None are urgent; all were found by review after the respective PR was already merged, or measured afterwards in production.

1. Is the new log index worth its cost?

IDX_b7eda1156aca7b2a1302cdf88f was added by #4519 to serve the overview chart query. Production, ~2 h after deploy:

Index Size idx_scan
IDX_b7eda1156aca7b2a1302cdf88f (new) 42 MB 20
IDX_log_financial_query (existing) 46 MB 16,672

The planner overwhelmingly picks the existing index. The measured win of #4519 came from no longer reading the ~43 kB message column (465 buffers instead of ~89,000), not from the new index — the chart query runs in 6.0 ms via IDX_log_financial_query.

Worth deciding whether 42 MB plus write cost on every log insert is justified for the remaining share, or whether the index should be dropped again.

2. Ledger master switch: test gaps (#4521)

  • crypto-input-cutover.integration.spec.ts:55 sets Config.ledger.enabled = true and never resets it. Its sibling staleness-cutover.integration.spec.ts does (afterEach). Not currently exploitable — each beforeEach builds a fresh ConfigService — but inconsistent.
  • ledger-master-switch.spec.ts discovers cron entry points via @DfxCron metadata only. A ledger job written with Nest's native @Cron would not be discovered and would silently escape the completeness check. transaction-request.service.ts:52 shows native @Cron is in use elsewhere in the codebase.
  • The same discovery uses getOwnPropertyNames(prototype), while the production scanner walks the prototype chain. A cron on an inherited method would be missed. No ledger service uses extends today.

3. Non-finite prices pass through the dashboard aggregation silently (#4520)

buildLatestBalance multiplies plusBalance.total by priceChf. Moving off the JSON round-trip means a non-finite price now reaches the arithmetic directly instead of being normalised to null on the way.

Production check at the time: of 430 asset rows, 0 hold NaN or Infinity, 144 hold NULL — and NULL multiplies to zero, same as before. So this is not reachable today.

A Number.isFinite guard was deliberately rejected: booking a broken price as zero is exactly the masking this codebase avoids. Logging loudly when a non-finite price enters the aggregation would close the gap without inventing a value.

4. AsyncCache.invalidate() can be outlived by an in-flight refresh

async-cache.ts:42-56: a refresh started before invalidate() writes its result back afterwards, with a fresh timestamp. In a narrow window an invalidation can therefore be undone for up to a full TTL.

Pre-existing, and it applies to every CachedRepository consumer, not to one call site. Relevant because FiatService.updatePrice() relies on invalidation taking effect immediately. A generation counter incremented on invalidate(), checked before the write-back, would fix it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions