Better context timeout error messages#1277
Open
brandur wants to merge 1 commit into
Open
Conversation
This one's aimed at producing somewhat better ergonomics when it comes
to working with context timeouts throughout the project. A sizable
problem that we have right now is that in case of timeout, all that
comes back is the generic error from `context.DeadlineExceeded`,
"context deadline exceeded". You don't know where it came from exactly
as it might've bubble up through many layers of the stack. If multiple
timeouts were in use simultaneously, either one might've caused the
timeout.
Here, introduce a timeout helper that aims to let us easily trace the
origin of a timeout. Traditionally, use of a timeout looked like this:
func (p *StandardPilot) JobGetAvailable(ctx context.Context, exec riverdriver.Executor, state ProducerState, params *riverdriver.JobGetAvailableParams) ([]*rivertype.JobRow, error) {
ctx, cancel := context.WithTimeout(ctx, rivercommon.HotOperationTimeout)
defer cancel()
return exec.JobGetAvailable(ctx, params)
Here, we add `WithTimeout`/`WithTimeoutV` which are used like this:
func (p *StandardPilot) JobGetAvailable(ctx context.Context, exec riverdriver.Executor, state ProducerState, params *riverdriver.JobGetAvailableParams) ([]*rivertype.JobRow, error) {
return timeoututil.WithTimeoutV(ctx, rivercommon.HotOperationTimeout, "StandardPilot.JobGetAvailable", func(ctx context.Context) ([]*rivertype.JobRow, error) {
return exec.JobGetAvailable(ctx, params)
})
The helpers apply a timeout with `context.WithTimeout` the same way the
first block works, but when a return timeout error is detected, they
wrap it in a more contextual form of the error. So previously, you'd get
this:
context deadline exceeded
With the helpers, you get this, containing the name of the operation and
the timeout that'd been applied:
StandardPilot.JobGetAvailable timed out after 1ns: context deadline exceeded
Unfortunately it has to be a helper function with inner callback because
even using `context.WithTimeoutCause`, `ctx.Err` still returns only a
naked `context.DeadlineExceeded`. You need to call `context.Cause(ctx)`
to get the error that was set as cause.
That said, everything else works. A `context.DeadlineExceeded` that was
wrapped on the way out is still recognized and maintains its wrapper.
Multiple levels of `WithTimeout` can be nested and the right error
message still gets returned, depending on which level timed out.
Contributor
Author
|
@bgentry Thoughts on this? I changed only a single instance of context timeout to show what usage would roughly look like at first, but if we went through with it, it shouldn't be too difficult to have an LLM canvas the whole codebase to update everything. Or we could do it over time. |
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.
This one's aimed at producing somewhat better ergonomics when it comes
to working with context timeouts throughout the project. A sizable
problem that we have right now is that in case of timeout, all that
comes back is the generic error from
context.DeadlineExceeded,"context deadline exceeded". You don't know where it came from exactly
as it might've bubble up through many layers of the stack. If multiple
timeouts were in use simultaneously, either one might've caused the
timeout.
Here, introduce a timeout helper that aims to let us easily trace the
origin of a timeout. Traditionally, use of a timeout looked like this:
Here, we add
WithTimeout/WithTimeoutVwhich are used like this:The helpers apply a timeout with
context.WithTimeoutthe same way thefirst block works, but when a return timeout error is detected, they
wrap it in a more contextual form of the error. So previously, you'd get
this:
With the helpers, you get this, containing the name of the operation and
the timeout that'd been applied:
Unfortunately it has to be a helper function with inner callback because
even using
context.WithTimeoutCause,ctx.Errstill returns only anaked
context.DeadlineExceeded. You need to callcontext.Cause(ctx)to get the error that was set as cause.
That said, everything else works. A
context.DeadlineExceededthat waswrapped on the way out is still recognized and maintains its wrapper.
Multiple levels of
WithTimeoutcan be nested and the right errormessage still gets returned, depending on which level timed out.