Skip to content

Blockfrost backend rebuilds TimeHandle on every block, tripling API usage #2833

Description

@v0d1ch

The Blockfrost chain layer calls queryTimeHandle from four places, all via the single
getTimeHandle defined in Hydra/Chain/Blockfrost.hs:

  • chainSyncHandler.onRollForward - once per confirmed block
  • chainSyncHandler.onRollBackward - per rollback (does not fire on Blockfrost)
  • mkChain.postTx - per posted L1 transaction (rare)
  • mkChain.draftDepositTx - per deposit draft (rare)

Each queryTimeHandle costs 3 Blockfrost requests: /blocks/latest (tip),
/genesis (system start), /network/eras (era history).

The per-block call in onRollForward dominates: at a 20s block time this is
~13,000 requests/day just to rebuild time handles, versus ~4,300/day for the
actual block polling. Roughly 75% of steady-state Blockfrost usage is time-handle churn.

Why it is unnecessary

  1. systemStart is immutable for the lifetime of the network; it can be queried once at startup.
  2. eraHistory only changes at hard forks, and the interpreter stays valid until its
    safe-zone horizon (hours past the tip). It can be cached and refreshed on a TTL or
    when a conversion fails with a past-horizon error.
  3. The tip query is entirely unused on the sync path: onRollForward/onRollBackward
    only use slotToUTCTime, never currentPointInTime. Only postTx and
    draftDepositTx need a current point in time, and those are rare user actions.

Proposed fix

Cache systemStart at chain-component startup and eraHistory in a TVar
(refresh on TTL or past-horizon failure), then build the TimeHandle locally via
mkTimeHandle. Drop the tip query from the sync path (the block being processed
provides the current slot); keep a fresh tip query only for postTx/draftDepositTx.

This reduces steady-state usage from ~4 requests per block to ~1, with no behavioral
change to the handlers. Caution: era history must not be cached unconditionally
forever, or slotToUTCTime throws TimeConversionException once the tip outruns
the horizon (see #2798 for the analogous L2 issue).

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

Status
In review 👀

Relationships

None yet

Development

No branches or pull requests

Issue actions