Do not retry a provider response that carries no data - #56
Open
werwolfby wants to merge 1 commit into
Open
Conversation
An empty result set was signalled by throwing from inside the retried
delegate, and the policy handled every exception. A permanently unknown
symbol was therefore retried the full retry count before failing, even
though no number of retries could change the outcome. The same unknown
symbol costs one request when another symbol in the batch resolves, and
retryCount+1 requests plus the whole back-off budget when it is alone.
- Add public FinanceNetNoDataException, derived from FinanceNetException so
existing catch blocks keep working, for "the provider answered, there is
no such data".
- Exclude it from the Polly handle predicate, so it fails fast.
- Raise it wherever an empty result set was previously reported as a generic
failure: the four services and the Yahoo HTML parser. Malformed responses
("table is null", "data is invalid", rate limits) stay retryable - only a
well-formed but empty answer is treated as permanent.
- Let it propagate untouched through the error wrappers, via a filter on the
existing catch clause, so callers can tell an invalid symbol from a
transient failure rather than diffing request against response.
- Give the no-data throws the context the wrappers used to supply. They
mostly carried the bare "All fields empty" constant; they now name the
provider and the request, e.g. "Alpha Vantage returned no records for IBM".
Adds NoDataFailFastTests, which asserts both the exception type and that
exactly one HTTP request was made. Existing tests that asserted the exact
type FinanceNetException, or the old wrapper message, assert the new type
and the specific message instead.
This was referenced Aug 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
An empty result set is signalled by throwing from inside the retried delegate, and the policy handles every exception. A permanently unknown symbol is therefore retried the full retry count before failing, even though no number of retries can change the outcome.
The clearest symptom is that the cost depends on the company a symbol keeps:
The same unknown symbol costs one request when another symbol in the batch resolves, and
retryCount + 1requests plus the whole back-off budget when it is alone. The time goes entirely into the retry loop, not into talking to the provider.Cause
src/Services/YahooFinanceService.cs:109,:153,:267and the equivalents in the other three services signal "no data" by throwing, andsrc/Utilities/PollyPolicyFactory.cs:13handles everything:Changes
Add
FinanceNetNoDataExceptionfor "the provider answered, there is no such data". It derives fromFinanceNetException, so existingcatch (FinanceNetException)handlers keep working unchanged.Exclude it from the retry predicate, so it fails fast:
Raise it wherever an empty result set was previously reported as a generic failure — the four services and
YahooHtmlParser, which is where the Yahoo signals actually originate. The line is drawn at well-formed but empty: malformed responses ("table is null","data is invalid") and rate limits stay retryable, because for those a retry genuinely can succeed.Let it propagate untouched through the error wrappers, using a filter on the existing catch rather than a second clause, so the original stack trace survives:
Give the no-data throws the context those wrappers used to supply. They mostly carried the bare
"All fields empty"constant, which is why wrapping looked necessary; they now name the provider and the request:Compatibility
FinanceNetNoDataExceptionderives fromFinanceNetException, socatch (FinanceNetException)is unaffected. Two things do change for callers who look closer:ex.GetType() == typeof(FinanceNetException)) will no longer match on no-data paths. This is the point of the change — it is what lets a caller tell an invalid symbol from a temporarily unavailable one.ex.Messageon no-data paths changes, and there is no longer anInnerExceptioncarrying"All fields empty".Tests
Adds
NoDataFailFastTests, asserting both the exception type and that exactly one HTTP request was made — the retry count is the actual bug, so the request count is the assertion that matters.Ten existing tests asserted the exact type
FinanceNetExceptionon inputs now recognised as no-data, and eight asserted the old wrapper message; they assert the new type and the specific message instead.TestCategory=Unitpasses: 161 tests, 0 failures. No new analyzer warnings.Note
This is independent of #55 (cancellation), but both touch the same
catchblocks in the four services, so whichever merges second will need a rebase — happy to do that. The third part of the cluster,HttpTimeoutdoubling as the retry back-off base, is on another branch and can follow as a separate PR.These changes were generated with Claude Code, and I have reviewed them.
🤖 Generated with Claude Code