Skip to content

tp: keep polling until the caller's deadline - #46

Merged
dangra merged 1 commit into
mainfrom
discharge-poll-deadline
Aug 24, 2026
Merged

dangra merged 1 commit into
mainfrom
discharge-poll-deadline

Conversation

@dangra

@dangra dangra commented Aug 24, 2026 •

Copy link
Copy Markdown
Member

The third party poll loop backs off without an upper bound, so the interval
routinely grows past what is left of the caller's context. When it does, the
client sleeps through the rest of its own budget without asking the third party
again, and then reports context deadline exceeded for a discharge that may
have been ready for most of that window. doPoll now clamps the wait when the
next backoff would sleep past the deadline, so a final poll always lands before
the context expires.

Context

With the default backoff under a 30 second deadline, polls land at
t=0,1,3,7,15 and the next one is scheduled for t=31. Nothing is polled for the
second half of the deadline: the client is blind for as long as it spent
looking. The gap grows with the backoff, so the longer the discharge takes, the
longer it sits ready and uncollected.

That is most visible in the user-interactive flow, where the discharge only
exists once a person finishes a login at the third party and the client's whole
job in between is to notice when they do. A caller can work around it by
passing a capped WithPollingBackoff, but the deadline is the real bound on
how long the client may wait, and nothing except the caller was consulting it.
Deriving the last wait from the deadline fixes the flow for every consumer,
whatever backoff they chose.

Details

boBeforeDeadline is applied to the sleep, not to the backoff state, so the
schedule keeps growing from its unclamped value and the clamp can only ever
shorten a single wait. It leaves a second of room before the deadline (or half
of what remains, when that is shorter) for the final request to complete, and
declines to clamp below 100ms so a nearly expired context expires rather than
turning into a spin on the third party. A context with no deadline is
unaffected.

Also drops the copy of defaultBackoff inlined in nextBO. NewClient always
assigns pollBackoffNext, so that branch is unreachable and a change made
there does nothing, silently.

tp_test.go gains a poll case whose backoff outgrows the context after the
first poll and whose discharge arrives in the window that used to be blind. It
fails with context deadline exceeded before this change.

Related PRs

The poll backoff doubles without bound, so it regularly grows past what
is left of the caller's context. The client then sleeps through the rest
of its own budget without asking the third party again and returns
context.DeadlineExceeded for a discharge that may have been ready for
most of that window. With the default backoff under a 30 second
deadline, polls land at t=0,1,3,7,15 and the next one is scheduled for
t=31: nothing is polled for the second half of the budget.

Clamp the wait when the next backoff would sleep past the deadline, so a
final poll still lands before the context expires. The clamp only ever
shortens a wait, and the backoff keeps growing from its unclamped value,
so a caller's schedule is unchanged except for that last poll.

Also drop the copy of defaultBackoff inlined in nextBO. It is
unreachable, since NewClient always assigns pollBackoffNext, and a
change made there silently does nothing.
Copilot AI lite review requested due to automatic review settings August 24, 2026 22:07

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@dangra
dangra merged commit 89ff4a7 into main Aug 24, 2026
1 check passed
@dangra
dangra deleted the discharge-poll-deadline branch August 24, 2026 22:21
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.

2 participants