Skip to content

Health-aware failover for Hive-Engine nodes#56

Merged
feruzm merged 2 commits into
mainfrom
fix/engine-node-failover
Jul 15, 2026
Merged

Health-aware failover for Hive-Engine nodes#56
feruzm merged 2 commits into
mainfrom
fix/engine-node-failover

Conversation

@feruzm

@feruzm feruzm commented Jul 15, 2026

Copy link
Copy Markdown
Member

Engine token data intermittently blanked in portfolio responses. The engine node selection kept a sticky randomly-picked node with one blind random retry, and the pool contained a node that no longer resolves in DNS plus two that fail intermittently — when the pick landed badly twice, the engine fetchers threw and the portfolio compiled without engine tokens.

Changes

  • Extract HiveRpcClient's per-node health bookkeeping (429 parking, recent-failure deprioritization, latency EWMA ordering) into a shared NodeHealthTracker. No behavior change on the Hive side; the existing HiveRpcFailoverTests cover the refactor.
  • Add EngineRpcClient on the same tracker:
    • Find (portfolio balances/tokens/metrics): these are fixed-shape queries that always yield a result array on a healthy node, so an error payload or non-JSON body is treated as a node failure and rolls over to the next node, walking the pool best-first. Per-attempt timeout is 2s so failover fits inside the portfolioV2 engine leg budget instead of one slow node consuming all of it.
    • ContractsRaw (engine-api passthrough): fails over only on transport errors and 429/5xx — any other response belongs to the caller's query and pipes as-is. Attempts capped at 3.
  • Refresh the node pool from the public node health meter: drop the dead DNS entry and two 0%-uptime nodes, add four healthy nodes; config order reflects observed reliability and remains the cold-start order/tiebreak.

Testing

  • 9 new EngineFailoverTests against stub HTTP nodes: dead node, error-payload node, 429 parking, failed-node deprioritization, all-nodes-failing, passthrough failover/as-is/last-response/attempt-cap semantics. Full suite: 52 passed.
  • All 8 pool nodes probed with a real balances find query — all return result arrays.
  • Local run: portfolio-v2 returns the full engine layer (60 tokens for a token-heavy account) consistently across repeated calls; engine-api passthrough verified.

Summary by CodeRabbit

  • New Features

    • Added health-aware failover across Hive-Engine nodes.
    • Automatically prioritizes healthy, responsive nodes and temporarily avoids failed or rate-limited nodes.
    • Added retry handling for transport failures, server overloads, and rate limits.
    • Engine API requests now preserve applicable upstream responses and enforce retry limits.
  • Bug Fixes

    • Improved reliability of wallet balances, token, metrics, and engine API requests during upstream node failures.
  • Tests

    • Added coverage for node failover, rate limiting, overload responses, transport failures, and retry limits.

Engine data intermittently blanked in portfolio responses: the ported
node selection kept a sticky randomly-picked node with one blind random
retry, and the pool contained a node that no longer resolves plus two
that fail intermittently. When the pick landed badly twice, the engine
fetchers threw and the portfolio compiled without engine tokens.

- Extract HiveRpcClient's per-node health bookkeeping into a shared
  NodeHealthTracker (no behavior change; existing failover tests cover it).
- Add EngineRpcClient on the same tracker: find queries require a result
  array (an error payload from a fixed-shape find is a node failure, not
  an application error) and walk the pool best-first; the engine-api
  passthrough fails over on transport errors and 429/5xx only, capped at
  three attempts.
- Refresh the node pool from the public health meter: drop the dead node
  and two 0% nodes, add four healthy ones; order by observed reliability.
- Per-attempt find timeout of 2s so failover fits inside the portfolioV2
  engine leg budget instead of one slow node consuming all of it.
@greptile-apps

greptile-apps Bot commented Jul 15, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds health-aware failover for Hive-Engine requests. The main changes are:

  • Extracts shared node health tracking from the Hive RPC client.
  • Adds failover clients for Engine RPC and account-history nodes.
  • Rewires portfolio and passthrough routes to use ordered node pools.
  • Refreshes the configured Engine node list.
  • Adds tests for failures, rate limits, retry limits, and response handling.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.
  • The latest fixes cover account-history failover, rate-limit parking, oversized Retry-After values, and bounded passthrough attempts.

Important Files Changed

Filename Overview
dotnet/EcencyApi/Infrastructure/EngineRpcClient.cs Adds health-aware failover for fixed-shape Engine queries and raw passthrough requests.
dotnet/EcencyApi/Infrastructure/NodeHealthTracker.cs Centralizes node health state, ordering, rate-limit parking, and latency tracking.
dotnet/EcencyApi/Infrastructure/HiveRpcClient.cs Moves existing Hive node health bookkeeping to the shared tracker.
dotnet/EcencyApi/Handlers/WalletApi.Engine.cs Routes portfolio, Engine API, and account-history traffic through dedicated failover pools.
dotnet/EcencyApi.Tests/EngineFailoverTests.cs Adds coverage for node failures, overload responses, rate limits, query forwarding, and attempt limits.

Reviews (2): Last reviewed commit: "fix: pool the history nodes behind the s..." | Re-trigger Greptile

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@feruzm, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 47 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 103c490f-7cc7-40c7-abae-483566813aa3

📥 Commits

Reviewing files that changed from the base of the PR and between 79cecf2 and cb0c975.

📒 Files selected for processing (5)
  • CLAUDE.md
  • dotnet/EcencyApi.Tests/EngineFailoverTests.cs
  • dotnet/EcencyApi/Handlers/WalletApi.Engine.cs
  • dotnet/EcencyApi/Infrastructure/EngineRpcClient.cs
  • dotnet/EcencyApi/Infrastructure/NodeHealthTracker.cs
📝 Walkthrough

Walkthrough

The change centralizes upstream node health tracking, refactors Hive RPC to use it, adds health-aware Hive-Engine failover, routes wallet engine requests through the new client, and adds loopback tests and documentation.

Changes

RPC failover

Layer / File(s) Summary
Shared health tracking and Hive integration
dotnet/EcencyApi/Infrastructure/NodeHealthTracker.cs, dotnet/EcencyApi/Infrastructure/HiveRpcClient.cs, CLAUDE.md
NodeHealthTracker manages failure, rate-limit, latency, ordering, and Retry-After state; HiveRpcClient delegates its health logic to it.
Engine RPC failover and wallet wiring
dotnet/EcencyApi/Infrastructure/EngineRpcClient.cs, dotnet/EcencyApi/Handlers/WalletApi.Engine.cs
EngineRpcClient adds health-aware Find and capped ContractsRaw retries, while wallet routes use the client instead of rotating engine URLs directly.
Engine failover validation
dotnet/EcencyApi.Tests/EngineFailoverTests.cs
Loopback stub tests cover transport failures, JSON errors, rate limiting, node deprioritization, overload passthrough, all-node failure, and attempt limits.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant WalletApi
  participant EngineRpcClient
  participant NodeHealthTracker
  participant EngineNode
  WalletApi->>EngineRpcClient: Send Find or ContractsRaw request
  EngineRpcClient->>NodeHealthTracker: Get ordered node indices
  EngineRpcClient->>EngineNode: POST /contracts
  EngineNode-->>EngineRpcClient: Return result or failure response
  EngineRpcClient->>NodeHealthTracker: Record node outcome
  EngineRpcClient-->>WalletApi: Return result or upstream response
Loading

Possibly related PRs

  • ecency/vision-api#51: Adds the documentation later updated to describe the RPC failover architecture.

Poem

A rabbit hops where RPCs run,
Health scores guide each node in turn.
Engine paths now fail over bright,
Stubbed tests guard the route at night.
“No lost requests!” I thump with cheer.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding health-aware failover for Hive-Engine nodes.
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 fix/engine-node-failover

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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: 4

🤖 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 `@dotnet/EcencyApi.Tests/EngineFailoverTests.cs`:
- Around line 187-212: Add a ContractsRaw test covering the separate 429
handling path: configure one rate-limited stub and one healthy stub, make two
sequential ContractsRaw calls, assert both calls succeed, and verify the
rate-limited node has one hit while the healthy node has two. Follow the
existing test setup and assertion patterns in ContractsRaw failover tests.

In `@dotnet/EcencyApi/Handlers/WalletApi.Engine.cs`:
- Around line 28-41: Update the EngineClient initialization to load the ordered
RPC node pool from deployment configuration instead of embedding hostnames in
source. Remove the infrastructure-specific reliability comment and hard-coded
endpoint list, while preserving runtime latency-based reordering and
configuration order as the cold-start tiebreak.

In `@dotnet/EcencyApi/Infrastructure/NodeHealthTracker.cs`:
- Around line 62-69: Remove the RateLimitStreak reset from RecordSuccess(int
nodeIndex, double elapsedMs), while preserving the ConsecutiveFailures reset and
latency recording. Let RecordRateLimited remain responsible for the documented
time-based throttle reset after 120 seconds without a throttle.
- Around line 159-167: Update ParseRetryAfterMs so converting nonnegative
Retry-After seconds to milliseconds cannot overflow int; validate the seconds
value against the maximum representable millisecond duration before multiplying,
and return null for values that exceed that limit while preserving existing
handling of invalid or negative headers.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: eaf55164-8194-4699-bd62-0db56a6b728a

📥 Commits

Reviewing files that changed from the base of the PR and between 94ecf24 and 79cecf2.

📒 Files selected for processing (6)
  • CLAUDE.md
  • dotnet/EcencyApi.Tests/EngineFailoverTests.cs
  • dotnet/EcencyApi/Handlers/WalletApi.Engine.cs
  • dotnet/EcencyApi/Infrastructure/EngineRpcClient.cs
  • dotnet/EcencyApi/Infrastructure/HiveRpcClient.cs
  • dotnet/EcencyApi/Infrastructure/NodeHealthTracker.cs

Comment thread dotnet/EcencyApi.Tests/EngineFailoverTests.cs
Comment thread dotnet/EcencyApi/Handlers/WalletApi.Engine.cs
Comment thread dotnet/EcencyApi/Infrastructure/NodeHealthTracker.cs
Comment thread dotnet/EcencyApi/Infrastructure/NodeHealthTracker.cs
The account-history passthrough proxied a single hardcoded host with no
retry. Generalize EngineRpcClient's passthrough loop to any method+path
and give the history route a two-node pool; per-attempt timeout halved
so the two-attempt envelope matches the original 30s.

Review fixes: clamp huge Retry-After delta-seconds instead of
overflowing into a negative park window; add ContractsRaw 429-parking
coverage.
@feruzm
feruzm merged commit f34ef6a into main Jul 15, 2026
5 checks passed
@feruzm
feruzm deleted the fix/engine-node-failover branch July 15, 2026 07:22
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