Skip to content

Read multiplexer#387

Open
dimriou wants to merge 12 commits intodevelopfrom
read_multiplexer
Open

Read multiplexer#387
dimriou wants to merge 12 commits intodevelopfrom
read_multiplexer

Conversation

@dimriou
Copy link
Contributor

@dimriou dimriou commented Mar 11, 2026

This PR allows v2 client wrappers to perform multiple read calls for the pending and the latest nonce. Every time a call is required, the client will cycle through the available alive RPCs and make a request until it receives a successful response. If it does it returns with the value. If it doesn't, it moves on to the next RPC. This change is important because it makes sure the confirmation logic will resume, even if the MultiNode has selected a bad RPC.

@dimriou dimriou force-pushed the read_multiplexer branch 3 times, most recently from bd6d02d to 260b5f5 Compare March 11, 2026 12:27
defer cancel()

startedAt := time.Now()
raw, err := c.CallContextAllSequential(ctx, method, args...)
Copy link
Contributor

Choose a reason for hiding this comment

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

This means the total of the sequential read calls have max 1.5s in total to respond, isn't that too low? We can start with this, but do we have an idea of the avg/p99 read call latency?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a bit of unknown, but here's my justification:
Across the Core node, our absolute minimum interval for each component is 2 seconds. Anything lower than that and you start having 100% utilization. For this case in particular, we need to come up with a number that is high enough but not too high so it adds additional latency for the components on chains with fast block time. Given how fast eth_transactionCount RPC calls are, I think 1.5s is decent enough to cycle through 3 RPCs (or even more) and not add potential latency. We have to take in consideration that if MultiNode is "locked" in a bad RPC, it means the max timeout we use might be consumed for an extended period of time, so we have to be careful with how high that value is.
Finally my thought process was this: I'd rather fail occasionally if this context is not enough than add latency that the node can't handle, the node would have failed without this anyway. I do agree that we should monitor this after we ship this change and see if this value is reasonable enough and revisit it if needed.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, that makes sense. Good to know about the 2s minimum interval for each core node component.

It's probably worthwhile to add some otel metric(s) to the code so that we can monitor how this change behaves after shipping it.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 20, 2026

⚠️ API Diff Results - github.com/smartcontractkit/chainlink-evm

⚠️ Breaking Changes (6)

pkg/client.Client (1)
  • CallContextAllSequential — ➕ Added
pkg/config.TransactionManagerV2 (1)
  • ReadRequestsToMultipleNodes — ➕ Added
pkg/txm/clientwrappers (1)
  • NewChainClient — Type changed:
func(
  - github.com/smartcontractkit/chainlink-evm/pkg/client.Client
  + github.com/smartcontractkit/chainlink-common/pkg/logger.Logger, 
  + github.com/smartcontractkit/chainlink-evm/pkg/client.Client, 
  + bool
)
- *ChainClient
+ (*ChainClient, error)
pkg/txm/clientwrappers/dualbroadcast (1)
  • SelectClient — Type changed:
func(
  github.com/smartcontractkit/chainlink-common/pkg/logger.Logger, 
  github.com/smartcontractkit/chainlink-evm/pkg/client.Client, 
  github.com/smartcontractkit/chainlink-evm/pkg/keys.ChainStore, 
  *net/url.URL, 
  *math/big.Int, 
  github.com/smartcontractkit/chainlink-evm/pkg/txm.TxStore, 
  + bool, 
  *bool, 
  *time.Duration
)
(github.com/smartcontractkit/chainlink-evm/pkg/txm.Client, github.com/smartcontractkit/chainlink-evm/pkg/txm.ErrorHandler, error)
pkg/txm/clientwrappers/dualbroadcast.FlashbotsClientRPC (1)
  • SendTransaction — Type changed:
func(
  context.Context, 
  - *github.com/ethereum/go-ethereum/core/types.Transaction
  + *github.com/smartcontractkit/chainlink-evm/pkg/txm/types.Transaction, 
  + *github.com/smartcontractkit/chainlink-evm/pkg/txm/types.Attempt
)
error
pkg/txm/clientwrappers/dualbroadcast.MetaClientRPC (1)
  • SendTransaction — Type changed:
func(
  context.Context, 
  - *github.com/ethereum/go-ethereum/core/types.Transaction
  + *github.com/smartcontractkit/chainlink-evm/pkg/txm/types.Transaction, 
  + *github.com/smartcontractkit/chainlink-evm/pkg/txm/types.Attempt
)
error

✅ Compatible Changes (9)

pkg/client.(*NullClient) (1)
  • CallContextAllSequential — ➕ Added
pkg/client.(*SimulatedBackendClient) (1)
  • CallContextAllSequential — ➕ Added
pkg/client.Client (1)
  • CallContextAllSequential — ➕ Added
pkg/client/clienttest (1)
  • Client_CallContextAllSequential_Call — ➕ Added
pkg/client/clienttest.(*Client) (1)
  • CallContextAllSequential — ➕ Added
pkg/client/clienttest.(*Client_Expecter) (1)
  • CallContextAllSequential — ➕ Added
pkg/config/toml.TransactionManagerV2Config (1)
  • ReadRequestsToMultipleNodes — ➕ Added
pkg/txm/clientwrappers (1)
  • MultiCallMaxTimeout — ➕ Added
pkg/txm/clientwrappers.(*ChainClient) (1)
  • BlockByNumber — ➕ Added

📄 View full apidiff report

@dimriou dimriou marked this pull request as ready for review March 20, 2026 11:06
@dimriou dimriou requested review from a team as code owners March 20, 2026 11:06
Copilot AI review requested due to automatic review settings March 20, 2026 11:06
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds an optional “read multiplexer” for txm v2 nonce reads so the system can try multiple RPC nodes sequentially when fetching latest/pending nonces, improving resiliency when the currently-selected node is unhealthy.

Changes:

  • Introduces Client.CallContextAllSequential and wires it through txm’s ChainClient wrapper to multiplex NonceAt / PendingNonceAt reads.
  • Threads a new ReadRequestsToMultipleNodes config flag through TOML/config interfaces into txm v2 client construction (including dualbroadcast selector).
  • Adds/updates unit tests for the new sequential call path and txm wrapper multiplexing.

Reviewed changes

Copilot reviewed 20 out of 20 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
pkg/txmgr/builder.go Wires new config flag into txm v2 client wrapper construction and dualbroadcast selector.
pkg/txm/clientwrappers/dualbroadcast/selector.go Wraps the base client with the txm ChainClient wrapper (optionally multiplexing reads) before constructing dualbroadcast clients.
pkg/txm/clientwrappers/dualbroadcast/meta_client.go Updates SendTransaction RPC interface and call sites to pass txm attempt context.
pkg/txm/clientwrappers/dualbroadcast/flashbots_client.go Updates SendTransaction RPC interface and call sites to pass txm attempt context.
pkg/txm/clientwrappers/dualbroadcast/flashbots_client_test.go Updates test RPC mock to match new SendTransaction signature.
pkg/txm/clientwrappers/chain_client.go Adds multiplexed nonce reads (via raw JSON-RPC + decoding), timeout, and metrics wiring.
pkg/txm/clientwrappers/chain_client_metrics.go Adds OTel histogram recording for multicall duration and outcome attributes.
pkg/txm/clientwrappers/chain_client_test.go Adds tests for multicall sequencing and nonce decoding behavior.
pkg/config/toml/testdata/config-full.toml Adds ReadRequestsToMultipleNodes to full config fixture.
pkg/config/toml/docs.toml Documents new ReadRequestsToMultipleNodes option.
pkg/config/toml/config_test.go Updates defaults/docs tests and full config struct to include new option.
pkg/config/toml/config.go Adds TOML field and merge behavior for ReadRequestsToMultipleNodes.
pkg/config/config.go Extends TransactionManagerV2 interface with ReadRequestsToMultipleNodes().
pkg/config/chain_scoped_transactions.go Implements new config accessor in chain-scoped config wrapper.
pkg/client/chain_client.go Adds CallContextAllSequential to client interface and implements it using multinode iteration.
pkg/client/chain_client_test.go Adds tests validating CallContextAllSequential behavior (success, cancellation, arg handling).
pkg/client/simulated_backend_client.go Implements CallContextAllSequential shim for simulated backend client.
pkg/client/null_client.go Implements CallContextAllSequential stub for null client.
pkg/client/clienttest/client.go Extends autogenerated mock client with CallContextAllSequential.
CONFIG.md Documents new ReadRequestsToMultipleNodes option in generated config docs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@smartcontractkit smartcontractkit deleted a comment from github-actions bot Mar 20, 2026
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.

3 participants