Add StreamRetryPolicy.quadratic() with a back-off - #80
Merged
Merged
Conversation
Delays accumulated as step * n(n+1)/2 instead of doubling. Compute the delay from the retry index as step * 2^(n-1), guarding the shift against overflow at high retry counts.
Preserves the triangular curve that exponential() produced before it was corrected, so callers that want step * n(n+1)/2 growth have a named policy for it.
Contributor
PR checklist ✅All required conditions are satisfied:
🎉 Great job! This PR is ready for review. |
aleksandar-apostolov
marked this pull request as ready for review
September 9, 2026 12:32
The branch guard already bounds the shifted value, so the coerceIn could never fire. Add a processor-level test that pins elapsed scheduler time across five attempts, where the accumulation bug actually lived.
Removes the duplicated constructor block flagged by SonarCloud (14 lines, 7.7% of new code). custom() already clamps the lambda result to the same bounds, so behaviour is unchanged.
andremion
approved these changes
Sep 14, 2026
andremion
left a comment
There was a problem hiding this comment.
LGTM. One doc and naming thing inline, plus a test nit.
Quadratic is the steeper curve over retries 1-4 and only loses to doubling at retry 5, which the default maxRetries never reaches. Reword the KDoc, rename the comparison test and assert the crossover, and strengthen the cap test to grow into the cap instead of restating a fixed point.
Base automatically changed from
fix/and-1507-exponential-backoff
to
develop
September 14, 2026 08:14
|
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.



Stacked on #79. Merge after parent.
Goal
Closes AND-1508 — keep the triangular back-off curve available under its own name.
#79 corrects
exponential()to actually double, which retires thestep * n(n+1)/2curve it used to produce. That curve is a reasonable reconnect profile in its own right — it backs off harder early, which suits a caller that would rather wait than hammer a struggling server — and Feeds may want to keep it. This gives it a name instead of forcing callers intocustom().Worth being precise about the comparison, since it is easy to get backwards. Asymptotically doubling is the steeper curve, but over the range a retry loop actually covers
quadratic()is the steeper one: at a 100 ms step it runs100, 300, 600, 1000againstexponential()'s100, 200, 400, 800. Doubling only overtakes at retry 5, andStreamRetryProcessorImplthrows onisLastAttemptbefore computing that delay, so with the defaultmaxRetries = 5the crossover is never reached. Expect longer waits thanexponential(), not shorter.Implementation
StreamRetryPolicy.quadratic()factory, same parameter set and semantics as the other three.nextBackOffDelayFunctionisprev + retryIndex * backoffStepMillis, clamped to[backoffStepMillis, maxBackoffMillis]— the exact lambdaexponential()carried before MakeStreamRetryPolicy.exponential()be actually exponential #79, minus acoerceAtMostthat the followingcoerceInalready subsumed.exponential()no longer works that way.With the default 250 ms step:
250, 750, 1500, 2500, 3750.No consumer is migrated here — Feeds picks its own policy separately. The README retry section is being edited in #74, so
quadratic()gets documented there once that lands.Testing
./gradlew :stream-android-core:testDebugUnitTest— 756 tests, 1 failure inStreamCompositeEventSerializationImplTest, confirmed pre-existing on a cleandevelopworktree at e80f8a4.13 new
quadratictests inStreamRetryPolicyTest: defaults, custom parameters, the triangular sequence across four retries, cap saturation at high retry counts, minimum clamping,giveUpbehavior, and the fiverequireValidrejections mirroring the existinglinearset.One test pins all three curves side by side at a 100 ms step over five retries — linear 500, quadratic 1500, exponential 1600 — so a future change to any of them shows up as a relationship break, not just a number change.