feat: multi-endpoint fallback provider for the read path#32
Conversation
The live path was a single HTTP endpoint with no failover, so one degraded RPC could stall a liquidator's detection reads. This routes the READ path through eth.zig's FallbackProvider (per-endpoint health tracking, failover after a threshold of consecutive failures, and periodic recovery probing). - EthChainClient.createWithFallback(rpc_urls, private_key, opts): builds a standalone primary (writes + callBatch + callRaw stay here) plus a heap-owned FallbackProvider over owned copies of the endpoint URLs. The read methods (call / getLogs / simulate) route through the fallback when present; the single-endpoint create() path is unchanged (fallback null). - PerpCityContext.initWithFallback(rpc_urls, private_key, opts, deployments). - destroy() tears down the fallback provider before its borrowed URL copies. Construction is network-free (transports connect lazily), so the tests regression-guard the allocation/free discipline: multi- and single-endpoint build leak-clean under the testing allocator, URLs are owned copies, and an empty list errors NoEndpoints. Failover behavior itself is integration. zig build test 499, contract-test 98 (Debug + ReleaseFast).
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthrough
ChangesFallback provider integration
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant PerpCityContext
participant EthChainClient
participant FallbackProvider
participant RPC_Endpoints
PerpCityContext->>EthChainClient: initWithFallback(rpc_urls, opts)
EthChainClient->>FallbackProvider: initialize fallback endpoints
PerpCityContext->>EthChainClient: perform read operation
EthChainClient->>FallbackProvider: route ethCall, ethSimulate, or ethGetLogs
FallbackProvider->>RPC_Endpoints: request read operation
RPC_Endpoints-->>FallbackProvider: return read response
FallbackProvider-->>EthChainClient: return read response
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/chain_client.zig`:
- Around line 379-396: Update the wallet cleanup in the construction path around
Wallet.initLocal so rollback deinitializes the initialized wallet before freeing
its allocation. Replace or augment the existing errdefer
allocator.destroy(wallet) cleanup while preserving normal fallback/provider
cleanup behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 7ffd56d6-4bd8-411c-a2ce-11405faa2ca3
📒 Files selected for processing (3)
src/chain_client.zigsrc/context.zigtests/contract/chain_client_test.zig
CodeRabbit (Minor): in createWithFallback the errdefer only freed the wallet's memory, skipping wallet.deinit() that the normal destroy() runs. If the URL/fallback allocation after wallet init fails, the wallet's resources would leak. Add errdefer wallet.deinit() so rollback mirrors destroy(). (create/createWithKms have no fallible op after wallet init.)
What
Adds
createWithFallback/initWithFallback: route the SDK's read path through eth.zig'sFallbackProvider-- multiple RPC endpoints with per-endpoint health tracking, failover, and recovery probing -- instead of a single HTTP endpoint.Why
The live path was one HTTP endpoint with no failover, so a single degraded RPC could stall a liquidator's high-frequency detection reads (
eth_call/eth_getLogs). Failover keeps detection alive when an endpoint flakes.Changes
EthChainClient.createWithFallback(rpc_urls, private_key, opts): builds a standalone primary (writes,callBatch,callRawstay here) plus a heap-ownedFallbackProviderover owned copies of the endpoint URLs. The read methods (call/getLogs/simulate) route through the fallback when present; the single-endpointcreate()path is unchanged (fallbacknull).PerpCityContext.initWithFallback(rpc_urls, private_key, opts, deployments).destroy()tears down the fallback provider before its borrowed URL copies.opts(FallbackOpts) tunes the failover threshold and recovery-probe interval.Scope note: writes /
callBatch/callRawintentionally stay on the primary endpoint --FallbackProvidermirrors the standard read methods, not the raw-transport or batch paths. The high-frequency detection reads are what get the resilience.Tests
Construction is network-free (transports connect lazily), so the tests regression-guard the allocation/free discipline: multi- and single-endpoint builds are leak-clean under the testing allocator, URLs are verified to be owned copies, and an empty list errors
NoEndpoints. Failover behavior itself is exercised by integration.zig build test: 499/499zig build contract-test(Debug + ReleaseFast): 98/98zig fmt --check: cleanSummary by CodeRabbit
createWithFallbackconstructor.initWithFallbackto initialize context with multiple RPC endpoints.