Skip to content

feat: multi-endpoint fallback provider for the read path#32

Merged
koko1123 merged 2 commits into
mainfrom
koko/zig-sdk-fallback-provider
Jul 14, 2026
Merged

feat: multi-endpoint fallback provider for the read path#32
koko1123 merged 2 commits into
mainfrom
koko/zig-sdk-fallback-provider

Conversation

@koko1123

@koko1123 koko1123 commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

What

Adds createWithFallback / initWithFallback: route the SDK's read path through eth.zig's FallbackProvider -- 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, 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.
  • opts (FallbackOpts) tunes the failover threshold and recovery-probe interval.

Scope note: writes / callBatch / callRaw intentionally stay on the primary endpoint -- FallbackProvider mirrors 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/499
  • zig build contract-test (Debug + ReleaseFast): 98/98
  • zig fmt --check: clean

Summary by CodeRabbit

  • New Features
    • Added optional multi-endpoint fallback support for read operations via a new createWithFallback constructor.
    • Added initWithFallback to initialize context with multiple RPC endpoints.
    • Write operations remain routed through the primary endpoint.
  • Bug Fixes
    • Improved lifecycle management for fallback providers and owned fallback URL storage to prevent leaks and dangling references.
  • Tests
    • Added tests covering multi-endpoint fallback creation, single-endpoint behavior, empty endpoint rejection, and allocator leak cleanliness.

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).
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 93fe78b5-6074-45bc-821a-726a4ddf4920

📥 Commits

Reviewing files that changed from the base of the PR and between e4b1d94 and abba16a.

📒 Files selected for processing (1)
  • src/chain_client.zig
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/chain_client.zig

📝 Walkthrough

Walkthrough

EthChainClient supports optional multi-endpoint fallback reads, owns fallback endpoint copies, and cleans them up. PerpCityContext exposes matching initialization, while contract tests cover endpoint validation, ownership, and destruction.

Changes

Fallback provider integration

Layer / File(s) Summary
Client fallback lifecycle and read routing
src/chain_client.zig, tests/contract/chain_client_test.zig
EthChainClient adds fallback construction and cleanup, routes selected read operations through the fallback provider, and tests endpoint validation and owned URL storage.
Context fallback initialization
src/context.zig
PerpCityContext.initWithFallback creates the fallback-enabled client and initializes existing context fields using the first RPC URL.

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
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding a multi-endpoint fallback provider for read operations.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch koko/zig-sdk-fallback-provider

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

📥 Commits

Reviewing files that changed from the base of the PR and between ad03af2 and e4b1d94.

📒 Files selected for processing (3)
  • src/chain_client.zig
  • src/context.zig
  • tests/contract/chain_client_test.zig

Comment thread src/chain_client.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.)
@koko1123
koko1123 merged commit 6ca51d8 into main Jul 14, 2026
6 checks passed
@koko1123
koko1123 deleted the koko/zig-sdk-fallback-provider branch July 14, 2026 22:11
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.

1 participant