What
A concrete type in internal/rpcv2/adapters satisfying store.TransactionReader (one method, GetTransaction), backed by the query router's by-hash probe surface (#865, implemented in PR #894; design in PR #843): probe the hot transaction-hash indexes first, then each window's frozen index, verify candidates against the full hash, and gate the result to the read view's [floor, latest] range.
The probe algorithm already exists in stores/txhash/read_assembly.go — this ticket wires it up and gates it.
What the read view provides
| Call |
File |
What it does for us |
view.HotTxHashIndexes() |
query/txlookup.go |
[]txhash.HashIndex for every published hot chunk, newest first. Ready to pass straight into NewTxReader. Registry-owned — do not close. |
view.ColdTxHashIndexCoverages() |
query/txlookup.go |
Frozen coverage keys, newest Hi first, read through the snapshot so a concurrent index rebuild can't change the probe set mid-request. |
What this ticket owns
- Open the cold
.idx files. Coverages are keys, not readers. Per coverage: txhash.OpenColdReader(layout.TxHashIndexFilePath(cov)). These are not view-owned — we close them. (Unless we add ColdTxIndexes() — see the open decision below.)
txhash.LedgerSource — one method, GetLedgerRaw(seq). Plain per-candidate resolution: view.Ledgers(chunk.IDFromLedger(seq)), one open per candidate. No caching layer: TxReader.scan takes one candidate per index, index ranges are disjoint, and the probe stops at the first verified match, so no chunk is ever probed twice.
- The floor/latest gate.
HotTxHashIndexes is deliberately unfiltered (its doc comment says so): a hot handle can predate the view's floor, and a cold index hit is a fingerprint, not an exact match. A resolved ledger outside [OldestLedger(), LatestLedger()] must become store.ErrNoTransaction, not an error and not a served result.
ingest.LedgerTransactionView → store.Transaction. Near 1:1 field copy: Hash, Result, Meta, Envelope, DiagnosticEvents, TransactionEvents, ContractEvents, FeeBump, ApplicationOrder, Successful, LedgerSequence, LedgerCloseTime all land directly. Only the hash needs hex encoding and the ledger fields need wrapping in store.LedgerInfo.
- Preserve the soft-error contract.
TxReader.GetTransaction deliberately returns an error rather than a clean not-found when a candidate could not be verified. Do not collapse that into ErrNoTransaction — a false not-found is indistinguishable from the transaction genuinely not existing.
Open decision for this ticket
The #894 reviewer proposed a ColdTxIndexes() ([]txhash.HashIndex, error) on ReadView that opens each frozen coverage newest-first with view-owned closers, feeding NewTxReader's slice directly and letting ColdTxHashIndexCoverages go internal. Deferred to this ticket, where it gains its consumer. Recommendation: add it. Without it this adapter is the only cold-reader consumer holding its own path composition and closer discipline.
Sizing
~120–180 lines in 1 file, ~12 tests.
Depends on
#876, #865 (implemented in PR #894). #885 in build order only — nothing is code-shared; the LedgerSource is a plain per-candidate view.Ledgers read.
What
A concrete type in
internal/rpcv2/adapterssatisfyingstore.TransactionReader(one method,GetTransaction), backed by the query router's by-hash probe surface (#865, implemented in PR #894; design in PR #843): probe the hot transaction-hash indexes first, then each window's frozen index, verify candidates against the full hash, and gate the result to the read view's[floor, latest]range.The probe algorithm already exists in
stores/txhash/read_assembly.go— this ticket wires it up and gates it.What the read view provides
view.HotTxHashIndexes()query/txlookup.go[]txhash.HashIndexfor every published hot chunk, newest first. Ready to pass straight intoNewTxReader. Registry-owned — do not close.view.ColdTxHashIndexCoverages()query/txlookup.goHifirst, read through the snapshot so a concurrent index rebuild can't change the probe set mid-request.What this ticket owns
.idxfiles. Coverages are keys, not readers. Per coverage:txhash.OpenColdReader(layout.TxHashIndexFilePath(cov)). These are not view-owned — we close them. (Unless we addColdTxIndexes()— see the open decision below.)txhash.LedgerSource— one method,GetLedgerRaw(seq). Plain per-candidate resolution:view.Ledgers(chunk.IDFromLedger(seq)), one open per candidate. No caching layer:TxReader.scantakes one candidate per index, index ranges are disjoint, and the probe stops at the first verified match, so no chunk is ever probed twice.HotTxHashIndexesis deliberately unfiltered (its doc comment says so): a hot handle can predate the view's floor, and a cold index hit is a fingerprint, not an exact match. A resolved ledger outside[OldestLedger(), LatestLedger()]must becomestore.ErrNoTransaction, not an error and not a served result.ingest.LedgerTransactionView→store.Transaction. Near 1:1 field copy:Hash,Result,Meta,Envelope,DiagnosticEvents,TransactionEvents,ContractEvents,FeeBump,ApplicationOrder,Successful,LedgerSequence,LedgerCloseTimeall land directly. Only the hash needs hex encoding and the ledger fields need wrapping instore.LedgerInfo.TxReader.GetTransactiondeliberately returns an error rather than a clean not-found when a candidate could not be verified. Do not collapse that intoErrNoTransaction— a false not-found is indistinguishable from the transaction genuinely not existing.Open decision for this ticket
The #894 reviewer proposed a
ColdTxIndexes() ([]txhash.HashIndex, error)onReadViewthat opens each frozen coverage newest-first with view-owned closers, feedingNewTxReader's slice directly and lettingColdTxHashIndexCoveragesgo internal. Deferred to this ticket, where it gains its consumer. Recommendation: add it. Without it this adapter is the only cold-reader consumer holding its own path composition and closer discipline.Sizing
~120–180 lines in 1 file, ~12 tests.
Depends on
#876, #865 (implemented in PR #894). #885 in build order only — nothing is code-shared; the
LedgerSourceis a plain per-candidateview.Ledgersread.