fix(portfolio): keep engine balances when an enrichment leg fails - #67
Conversation
FetchEngineTokensWithBalance awaited token metadata and market metrics alongside the balances it had already fetched. Either one throwing after its failover was exhausted hit the outer catch, which returns an empty array — so a metrics outage surfaced identically to a total Hive-Engine outage: no tokens in the wallet at all. Only balances are load-bearing. Tokens supply name/precision/icon and metrics supply the price used for fiat valuation; ConvertEngineToken already null-tolerates both. Degrade them to empty, as the rewards leg already does, so the balance rows still render (unpriced) instead of the layer disappearing.
|
Warning Review limit reached
Next review available in: 10 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
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. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b2f9617c09
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| var tokensTask = Optional(FetchEngineTokens(symbols), "engine tokens"); | ||
| var metricsTask = Optional(FetchEngineMetrics(symbols), "engine metrics"); |
There was a problem hiding this comment.
Bound optional enrichments before the engine deadline
When either enrichment endpoint stalls instead of failing immediately, Optional never reaches its catch before the result is needed: EngineRpcClient.Find can try all eight nodes with a 2-second timeout per attempt, while the inspected PortfolioV2 flow wraps the entire engine operation in a 4.5-second timeout. Consequently, Task.WhenAll remains pending and the outer timeout returns an empty engine array, still discarding successfully fetched balances during the timeout-style outage this change is meant to tolerate. Apply a timeout/fallback to each optional enrichment leg within the engine budget rather than handling exceptions alone.
Useful? React with 👍 / 👎.
Greptile SummaryThe PR preserves successfully fetched Hive-Engine balances when optional token metadata or market metrics fail or stall.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| dotnet/EcencyApi/Handlers/WalletApi.Engine.cs | Adds bounded failure isolation around optional metadata and metrics tasks so their outages no longer erase fetched balances. |
| dotnet/EcencyApi.Tests/EngineOptionalLegTests.cs | Verifies successful pass-through, exception and stall degradation, and balance conversion without enrichment. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Fetch engine balances] --> B{Balances available?}
B -- No --> C[Return empty engine result]
B -- Yes --> D[Fetch token metadata]
B -- Yes --> E[Fetch market metrics]
B -- Yes --> F[Fetch rewards]
D --> G{Success within budget?}
E --> H{Success within budget?}
G -- No --> I[Use empty metadata]
H -- No --> J[Use empty metrics]
G -- Yes --> K[Use metadata]
H -- Yes --> L[Use metrics]
I --> M[Convert balances]
J --> M
K --> M
L --> M
F --> M
M --> N[Return engine balances with available enrichment]
Reviews (2): Last reviewed commit: "review: bound the optional legs, don't o..." | Re-trigger Greptile
Catching exceptions alone left the stalling case unhandled, which is the outage that actually matters: EngineRpcClient.Find walks the whole node pool at 2s per attempt, far outlasting the engine leg's budget. Task.WhenAll stayed pending until PortfolioV2's own timeout fired and returned an empty layer — discarding the balances this change exists to preserve. Optional now bounds each leg as well as catching it, at the same budget the rewards leg already uses. The inner wrapper never faults, so timing a leg out cannot leave an unobserved exception behind.
|
Good catch, and it went to the heart of the change — fixed in the latest commit. Codex, "bound optional enrichments before the engine deadline" (P1). Correct. Catching exceptions only handled the throwing case, while the outage that actually matters here is a stall:
Added a test for the stall path specifically (a Worth noting for the record: Greptile scored this 5/5 "safe to merge" on the same commit and CodeRabbit was rate-limited out entirely, so this one bot was the only thing standing between a no-op fix and production. |
Fixes #65.
Problem
FetchEngineTokensWithBalanceawaits token metadata and market metrics alongside the balances it has already fetched:If either throws after its failover is exhausted, the outer
catchreturns an empty array — so a metrics outage surfaces identically to a total Hive-Engine outage: no tokens in the wallet at all.The three legs are not equally important:
ConvertEngineTokenalready null-tolerates the optional two, and the rewards leg already models this correctly (capped byEngineRewardsTimeoutMs, degrades to empty). Tokens and metrics did not.Change
An
Optionalhelper degrades an enrichment leg to an empty array instead of failing the caller, logged once so a persistent outage stays visible. Balances keep propagating — they are the one leg without which there is nothing to render.Result: a metrics outage now shows balances without a fiat value, rather than an empty wallet.
Tests
dotnet test: 103 passed. NewEngineOptionalLegTestscovers pass-through of a successful leg, degradation of a failed one, and that balances still convert with both enrichment legs lost.Not the cause of the recently-fixed missing balances (that was #64) — this is the remaining path where good data was discarded because an optional enrichment failed.