Skip to content

feat(pxe): anchor-scoped node read cache#24969

Draft
nchamo wants to merge 2 commits into
merge-train/fairiesfrom
nchamo/rpc-optimizations
Draft

feat(pxe): anchor-scoped node read cache#24969
nchamo wants to merge 2 commits into
merge-train/fairiesfrom
nchamo/rpc-optimizations

Conversation

@nchamo

@nchamo nchamo commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Why we are doing this

Client flows repeat identical node reads within a single anchor block window: simulation and witness generation fetch the same membership witnesses, leaf indexes, receipts and public storage, and separate services re-fetch what a simulator just read. Every duplicate is a serial RPC round trip on the critical path of proving.

Our fix

  • AztecNodeReadCache: a PXE-wide store of node read promises keyed by method and arguments. In-flight promises are shared (concurrent identical reads collapse into one request) and rejected reads are evicted so callers can retry.
  • withReadCache(node, cache): a transparent AztecNode wrapper that serves repeated immutable reads (witnesses, leaf indexes, public storage, blocks, tx receipts) from the store and passes everything else through. findLeavesIndexes is cached per leaf: only the leaves without a cached result are fetched, in a single batched node call.
  • Consumers with an anchor view of the chain (contract function simulators, private kernel proving, ContractSyncService, TxResolverService) get the wrapped node. Chain observers (BlockSynchronizer, tx submission, isValidTx) keep the raw node, since they must see live chain state.
  • BlockSynchronizer wipes the store whenever the anchor block advances, so entries whose results can change as the chain grows (receipt statuses, block-number-keyed lookups) never outlive the anchor they were read at.

Node calls per method across all client-flow benchmarks (whole run):

RPC call before after reduction
findLeavesIndexes 132 90 −31.8%
getTxReceipt 69 51 −26.1%
getPublicStorageAt 15 11 −26.7%
getPublicDataWitness 154 141 −8.4%
getNullifierMembershipWitness 47 33 −29.8%
getNoteHashMembershipWitness 40 28 −30.0%
everything else 726 726
Total 1183 1080 −8.7%

Profiled flows: RPC calls and node round trips. RPC calls count every individual node request (server load). Round trips count how many times the flow blocks waiting on the network: requests issued concurrently count as many RPC calls but a single round trip, so client-perceived latency scales with round trips, not calls.

Scenario RPC before RPC after reduction round trips before round trips after reduction
transfer_0_recursions+private_fpc 39 29 −25.6% 27 19 −29.6%
transfer_0_recursions+sponsored_fpc 20 19 −5.0% 15 14 −6.7%
transfer_1_recursions+private_fpc 38 27 −28.9% 24 16 −33.3%
transfer_1_recursions+sponsored_fpc 23 20 −13.0% 17 14 −17.6%
amm_add_liquidity_1_recursions 39 35 −10.3% 27 23 −14.8%
token_bridge_claim_private 26 25 −3.8% 14 13 −7.1%
storage_proof_7_layers 11 11 7 7
deployments (4 scenarios) 30 30 24 24
Total 226 196 −13.3% 155 130 −16.1%

The benchmarks advance the anchor on every interaction, wiping the cache at the fastest possible cadence, so these numbers are a lower bound: production sessions doing repeated operations against one anchor reuse strictly more.

@nchamo nchamo self-assigned this Jul 24, 2026
@nchamo nchamo added ci-draft Run CI on draft PRs. ci-no-fail-fast Sets NO_FAIL_FAST in the CI so the run is not aborted on the first failure labels Jul 24, 2026
@nchamo
nchamo marked this pull request as ready for review July 24, 2026 17:21
@nchamo
nchamo marked this pull request as draft July 24, 2026 17:23
keyStore: this.keyStore,
addressStore: this.addressStore,
aztecNode: BenchmarkedNodeFactory.create(this.node),
aztecNode: withReadCache(BenchmarkedNodeFactory.create(this.node), this.nodeReadCache),

@nchamo nchamo Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI: cache hits are not counted for benchmarks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-draft Run CI on draft PRs. ci-no-fail-fast Sets NO_FAIL_FAST in the CI so the run is not aborted on the first failure

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant