- Category
- Operations
- Reading time
- 11 minutes
- Published
- Updated
EVM RPC failover: how to prevent stale or inconsistent data
RPC failover is safe only when the replacement can answer the same request with acceptable freshness, history, method support, and block consistency. HTTP 200 is not enough: a secondary endpoint can be reachable while lagging, missing old state, rejecting the required tracer, or observing a different canonical block.
A production application therefore needs two layers of protection. The RPC service must meet the advertised customer-visible capability and availability requirements. The application must pin related reads, bound retries, persist block identity, recover from reorgs, and treat an ambiguous transaction submission as unknown.
This guide gives a failover contract, a test plan, and an implementation checklist. Teams using SolidRPC can apply those checks to one managed integration instead of building a customer-managed provider pool.
Who should own RPC failover?
Start with the boundary your team wants to own. A self-managed provider pool makes the application team responsible for selecting endpoints, testing capability, switching traffic, observing service quality, and handling incidents. Adding another endpoint adds another system to qualify and maintain.
With SolidRPC, the customer-facing boundary is one managed integration across the supported chains and methods. Your team qualifies that service against its requirements instead of maintaining its own pool. The public network, pricing, transport, and SLA documentation define the capabilities and commercial terms to test.
Your team still specifies the required data, historical depth, throughput, freshness, and consistency. It also owns how an RPC result becomes an application decision or a durable database record.
| Work | With SolidRPC |
|---|---|
| Advertised service availability and capability | SolidRPC is accountable for the managed service. |
| Published networks, methods, transport, quotas, and SLA terms | SolidRPC publishes the customer-visible service boundary. |
| Required methods, history, throughput, and freshness | Your team defines requirements and qualifies the service against them. |
| Block pinning, reorg rollback, cursors, and transaction safety | Your application owns data correctness and recovery. |
| Application deadlines, request pacing, and accepted limits | Your team controls demand within the agreed service capacity. |
What must RPC failover preserve besides availability?
A useful failover contract has four parts:
- Reachability: a request completes before its deadline.
- Capability: the selected upstream supports the method, tracer, block range, and response size you need.
- Freshness: the node is close enough to the chain head for this workload.
- Consistency: all reads in one logical operation describe the same block or an explicitly accepted finality level.
These requirements differ by workload. A dashboard may accept a slightly older finalized balance. A mempool monitor needs the newest view it can get. An indexer can process unfinalized blocks only if it stores enough information to roll them back. A payout service may require an explicit independent-verification policy before acting; ordinary failover should not be mistaken for a consensus guarantee.
Your application team should write these requirements down per request class and qualify the RPC service against them. Without it, a router can improve HTTP success rate while silently weakening the application’s data guarantees.
Which RPC failures should trigger another upstream?
A timeout, connection reset, service throttling, or transient 5xx may justify another attempt within a bounded deadline. A customer account or API-key limit instead requires request pacing or a capacity change; changing endpoints does not increase the customer’s purchased allowance.
A valid JSON-RPC result is different. null, 0x0, 0x, and an empty array can all be correct answers for particular methods. Treat an empty result as wrong only when the method’s semantics and surrounding evidence prove it.
The same distinction applies to errors:
-32601identifies an unavailable method; check the advertised method family and exact call.-32602indicates invalid parameters; the application must fix the request.- an execution revert is an application result, not a transport outage.
- pruned-history errors mean the requested history is unavailable on that service path.
A SolidRPC customer should retain the response for diagnosis, handle deterministic errors, and keep application retries bounded against the same managed integration. A persistent failure of an advertised capability is a service issue to raise with SolidRPC, not a reason to silently substitute a different block or result.
How should you qualify a backup RPC endpoint?
If your team operates its own pool, build a capability matrix per chain and endpoint covering:
- standard methods used by the application
- retained block and state history
eth_getLogsrange and response limits- exact
debug_*ortrace_*methods and tracer options - required transport and batch behavior
- rate, concurrency, and timeout limits
Method names alone are not enough. Different EVM implementations can expose different tracing behavior, and pruned services cannot answer every historical query. A useful secondary must satisfy the required workload and reduce shared failure risk.
A team buying managed RPC qualifies the customer-facing service instead. Run the same method, history, freshness, block-identity, and response-shape tests during a controlled service disruption. Retain that acceptance suite and rerun it when the published service capabilities or the application requirements change.
How do you stop failover from serving stale data?
The RPC operator must distinguish a responsive process from fresh blockchain data. A liveness probe such as eth_chainId proves only that an endpoint speaks JSON-RPC for the expected chain. An operator should compare block numbers, timestamps, and hashes across qualified references, exclude stale capacity, and verify recovery before returning it to service.
Block height alone is insufficient: during a reorganization, nodes can report the same height with different hashes. The application should specify its permitted freshness and consistency envelope and test the service against it, rather than maintaining a second production router to compensate for stale serving.
Finality remains an application choice. Ethereum’s JSON-RPC block parameter includes latest, safe, and finalized; other EVM networks can have different models or tag support. Test the exact chain and choose the block reference that matches the product’s requirements.
Apply the same intent to application caches. Cache finalized historical data where safe, but keep head-sensitive responses short-lived or uncached. Monitor the age of data your product actually uses; escalate a persistent service freshness failure with the chain, block, time, and affected request.
How do you keep a multi-call operation consistent?
Resolve an explicit block before a workflow that makes several related reads. Record its number and hash, then issue every compatible state query against that same block. If the endpoint supports EIP-1898, a block-hash parameter with requireCanonical: true can make a non-canonical result an explicit error instead of allowing the workflow to mix forks.
This matters for workflows such as reading several balances, walking contract state with repeated eth_call, or combining a block, receipts, logs, and traces. Using latest on every call lets the target move between requests even when no failover happens. Switching upstreams makes that ambiguity harder to notice.
For logs, the Ethereum execution API allows an eth_getLogs filter by blockHash. For range backfills, store the hash of every processed block and validate parent continuity before advancing. If the service cannot serve the pinned block within the operation’s deadline, stop or retry the logical operation according to its recovery policy. Keep the block reference fixed; do not quietly continue at a different block or add application-level upstream switching.
How should retries and hedges differ by method?
Retry policies can exist in both the RPC service and the application. The application owns its total deadline, bounded retries to the service, and concurrency. Multiple unbounded layers can multiply one request into a traffic surge.
A hedge sends another request when the first has not completed after a delay. It can reduce tail latency for idempotent reads, but duplicates work. That trade-off differs sharply between a small current-state read and a multi-second trace or wide eth_getLogs query.
Retry only unresolved, retryable work within the application's deadline. Use backoff and jitter for throttling, reserve capacity for live traffic, and keep the selected block fixed across attempts. With SolidRPC, send those bounded retries to the same integration. The 429 guide explains how to separate purchased demand limits from transient service failures.
How should an indexer fail over without gaps or duplicates?
Managed RPC does not maintain the indexer’s database. The indexer’s durable cursor should contain a block number and block hash, not just the number. Before committing block N, verify that its parent hash matches the committed hash for N - 1. Store indexed rows with block identity so a fork can be reversed deterministically.
A robust loop is:
- Read a bounded block range ending behind your chosen confirmation boundary.
- Fetch and verify the block hashes that anchor the range.
- Process logs, receipts, and traces against those explicit blocks.
- Commit application data and the block cursor atomically.
- On a parent-hash mismatch, find the common ancestor, remove orphaned data, and replay the canonical range.
WebSocket failover needs the same reconciliation path. A replacement subscription starts from “now”; it does not prove that nothing was missed while the connection was down. Reconnect, resubscribe, and backfill from the last committed block before returning to live events. Ethereum’s WebSocket guide explains that a reorganization can resend old-chain logs with removed: true. A transaction may also appear in a replacement block, so consumers must preserve block identity, undo orphaned records, and deduplicate replayed work.
SolidRPC also supports ordinary RPC and native newHeads over WebSocket on activated networks for eligible paid accounts. Each head written shares the existing unit allowance and rate ceiling with RPC calls. Recovery is live-only and all WebSocket traffic is excluded from monetary SLA credits. Check the WebSocket guide. Use explicit ranges to recover missed blocks. Confirm support for the required subscription type before migration.
What changes for transaction submission?
Separate the write path from ordinary read failover. After a timeout from eth_sendRawTransaction, the transaction may have reached the first node even though the response did not reach you. Treat that outcome as unknown, not as a confirmed failure.
Sign locally, persist the raw transaction and its hash before broadcasting, and query by that hash after an ambiguous result. If rebroadcast is required, send the identical signed bytes; do not create a different transaction with the same nonce as an automatic retry. A “known transaction” response can be evidence that the bytes were already accepted, not an outage that needs further fan-out.
Read operations used to make a write decision—nonce, fee data, balances, and contract calls—deserve stricter consistency than cosmetic UI reads. Pin them to an intentional block or require agreement where a wrong answer could create financial loss.
What should you monitor during and after failover?
Monitor the customer-visible outcome:
- method, chain, request time, HTTP status, and JSON-RPC error
- end-to-end latency and application retry count
- returned block number, hash, timestamp, and data age
- indexer backlog and oldest uncommitted block
- useful results committed within the product's deadline
- affected users or application operations
These checks establish impact without exposing implementation details. If an advertised SolidRPC request fails persistently, report the method, parameters without credentials, timestamp, returned error, expected block or result, and observed impact. The application team should preserve unfinished work and recover it according to its own cursor, retry, and idempotency rules.
How do you test an RPC failover plan?
Use a controlled test environment and agree on failure scenarios with the RPC operator. A self-managed team can exercise its own upstreams; a managed-service customer should ask the provider to demonstrate failure and recovery through the same customer endpoint.
Include timeouts, throttling, stale data, unavailable required history, unsupported methods, and slow heavy requests. Verify deadlines, the exact historical block and method, response shape, throughput, and recovery. Record any capability that is not covered rather than inferring universal continuity from an archive label.
Separately, test application behavior for reorgs, duplicate delivery, process restarts, malformed responses, and ambiguous transaction-submit timeouts. Confirm cursor continuity, rollback, and safe replay. These application tests remain necessary even when upstream failover works.
The operator owns internal requalification after routing or node changes. Keep customer acceptance probes for the methods and outcomes the product depends on, and rerun them when those requirements or the published service capabilities change.
RPC failover implementation checklist
RPC service requirements
- Publish supported networks, methods, history, transport, quotas, and service terms.
- Preserve the advertised customer-visible capability during ordinary service disruption.
- Return clear errors when a request cannot be served.
- Support controlled acceptance testing for the required workload.
Application responsibilities
- Define method, history, throughput, freshness, finality, and consistency requirements.
- Test the customer endpoint against those requirements.
- Pin related reads to an explicit block where supported.
- Handle valid empty results and deterministic errors correctly.
- Keep retries, deadlines, and concurrency bounded.
- Persist block-number-and-hash cursors and implement reorg rollback.
- Treat transaction-submit timeouts as unknown outcomes and rebroadcast identical signed bytes only when appropriate.
- Monitor user-visible results and recover unfinished application work.
With SolidRPC, qualify the first list against the published managed service. Your team maintains the application controls in the second list without operating its own provider pool.
How SolidRPC applies these boundaries
SolidRPC's customer-facing outcome is one managed integration for supported EVM RPC traffic. Your engineers do not need to maintain a separate production provider pool for ordinary service continuity.
Confirm the required chains, method families, history, and transport in the network catalog, WebSocket guide, and current plan documentation. Then qualify correctness, throughput, and behavior during a controlled disruption with a representative workload. Use a staged migration and a time-bounded rollback period; retire the old customer-managed pool only after the acceptance gates pass.
Your application still owns consistent multi-call reads, reorg handling, durable storage, request pacing, and transaction safety. Bring those requirements to a workload review and test the service boundary your product will actually use.