Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implements route backend timeout #1566

Merged
merged 1 commit into from
Dec 17, 2020

Conversation

AlexanderYastrebov
Copy link
Member

  • Adds backendTimeout filter to configure route backend timeout
  • Proxy sets up request context with configured timeout and responds
    with 504 status on timeout (note: if response streaming has already
    started it will be terminated, client will receive backend status and
    truncated response body).

See #1041

@AlexanderYastrebov
Copy link
Member Author

If response truncation is not acceptable I see three ways out:

  • declare it a feature for limited usecases :)
  • try to workaround/disable streaming termination
  • attempt another approach - consider timeout as Transport.ResponseHeaderTimeout, keep a pool of transports per configured timeout (cleanup?) and wrap roundtripper around here
    response, err = p.roundTripper.RoundTrip(req)

@AlexanderYastrebov AlexanderYastrebov force-pushed the timeout-filter branch 2 times, most recently from be61996 to 20b44c0 Compare October 23, 2020 23:48
@AlexanderYastrebov
Copy link
Member Author

/cc @otrosien

@AlexanderYastrebov AlexanderYastrebov force-pushed the timeout-filter branch 4 times, most recently from 10795e8 to d3af5cd Compare October 24, 2020 10:57
@AlexanderYastrebov
Copy link
Member Author

AlexanderYastrebov commented Oct 24, 2020

There is a general problem to notify client about backend that has stuck. At the moment copyStream stops writing on backend read error:

skipper/proxy/proxy.go

Lines 411 to 421 in 8f32650

func copyStream(to flushedResponseWriter, from io.Reader, tracing *proxyTracing, span ot.Span) error {
b := make([]byte, proxyBufferSize)
for {
l, rerr := from.Read(b)
tracing.logStreamEvent(span, StreamBodyEvent, fmt.Sprintf("%d", l))
if rerr != nil && rerr != io.EOF {
return rerr
}

which leads to truncated response.

httputil.ReverseProxy uses panic(http.ErrAbortHandler), that closes client connection, see golang/go#23643 (not sure what happens to multistream http/2 connection on this)

@aryszka
Copy link
Contributor

aryszka commented Nov 16, 2020

If response truncation is not acceptable I see three ways out:
...
try to workaround/disable streaming termination

I believe this is possible if we implement our variant of context.Context and signal to it various phases like request headers sent, request stream done, response headers received, response stream done. But in my opinion, the current implementation is a good start, and covers a reasonable majority of the use cases.

@aryszka
Copy link
Contributor

aryszka commented Nov 16, 2020

I would allow multiple filters where the last one would take effect (see my comment for the timeout.Request method. Apart of that, lgtm

@szuecs
Copy link
Member

szuecs commented Dec 1, 2020

Code looks good to me.
I am pretty sure tests will hurt in the future (1ms/2ms sleeps to separate cases will break too often)

@AlexanderYastrebov

This comment has been minimized.

if cerr := req.Context().Err(); cerr != nil {
ctx.proxySpan.LogKV("event", "error", "message", cerr.Error())
return nil, &proxyError{err: cerr, code: 499}
if cerr == stdlibcontext.Canceled {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This now covers client cancellation and backend request timeout

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice!

* Adds `backendTimeout` filter to configure route backend timeout
* Proxy sets up request context with configured timeout and responds
with 504 status on timeout (note: if response streaming has already
started it will be terminated, client will receive backend status and
truncated response body)

See zalando#1041

Signed-off-by: Alexander Yastrebov <[email protected]>
@AlexanderYastrebov
Copy link
Member Author

I have rebased and updated tests as suggested

@szuecs
Copy link
Member

szuecs commented Dec 15, 2020

👍

@aryszka
Copy link
Contributor

aryszka commented Dec 17, 2020

Thanks a lot!

@aryszka
Copy link
Contributor

aryszka commented Dec 17, 2020

👍

@aryszka aryszka merged commit 5801bbb into zalando:master Dec 17, 2020
@AlexanderYastrebov AlexanderYastrebov deleted the timeout-filter branch October 28, 2022 15:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants