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

gRPC streaming responses smaller than 4KB are buffered indefinitely #3188

Closed
tanzeeb opened this issue Feb 13, 2019 · 10 comments
Closed

gRPC streaming responses smaller than 4KB are buffered indefinitely #3188

tanzeeb opened this issue Feb 13, 2019 · 10 comments
Assignees
Labels
area/networking kind/bug Categorizes issue or PR as related to a bug.
Milestone

Comments

@tanzeeb
Copy link
Contributor

tanzeeb commented Feb 13, 2019

In what area(s)?

/area networking

What version of Knative?

HEAD

Expected Behavior

Streaming gRPC responses should be returned immediately.

Actual Behavior

Streaming gRPC responses are held until a 4KB buffer is filled.

Steps to Reproduce the Problem

Reported by @fbiville:

@ericbottard and I worked together to investigate the response buffering issue.
As suggested by @dprotaso last Friday, we tried again to deploy our https://github.com/fbiville/hello-grpc Go server in the following setups:

  1. plain Kubernetes (no Istio, no Knative)
  2. plain Kubernetes + Istio (no Knative)
  3. Knative + @tanzeeb's PR knative/serving#2539

You can check the commit history of this project to follow what we exactly did (https://github.com/fbiville/streaming-loudly).

The buffering issue occurred only in situation 3.

After digging in Knative/serving codebase, we found that the queue-proxy transport uses buffered IO and the writer holds a buffer of 4K.

The code paths are approximately like:
cmd/queue/main h2cproxy httputil.ReverseProxy -> ReverseProxy#ServeHTTP transport.RoundTrip -> http2.transport#RoundTrip GetClientConn -> newClientConn -> buf.NewWriter with the default size

@tanzeeb tanzeeb added the kind/bug Categorizes issue or PR as related to a bug. label Feb 13, 2019
@tcnghia tcnghia added this to the Serving 0.5 milestone Feb 13, 2019
@fbiville
Copy link

You can reproduce the issue easily with https://github.com/ericbottard/knative-grpc, by following the steps described in the README file.

@mattmoor
Copy link
Member

From here:

        // FlushInterval specifies the flush interval
        // to flush to the client while copying the
        // response body.
        // If zero, no periodic flushing is done.
        FlushInterval time.Duration

I wonder if this would at least help mitigate the problem?

@mattmoor
Copy link
Member

mattmoor commented Feb 16, 2019

Though setting the payload to 10 bytes, I see the test hang at this for a while (and now it is on to the scale to zero)

2019-02-16T17:31:30.969Z        info    TestGRPC        e2e/grpc_test.go:167    Sending stream 1 of 3

Though, I only tried it in ./cmd/queue/main.go on the h2cProxy.

@mattmoor
Copy link
Member

This issue mentions exactly the issue we are seeing fabiolb/fabio#324

@mattmoor
Copy link
Member

cc @bradfitz in case he has any recommendations that aren't forking this library.

@bradfitz
Copy link

In Go 1.12, an explicit negative interval disables buffering, but it's also disabled for text/event-stream responses (and perhaps more will be added in the future; there's a TODO in the code):

https://tip.golang.org/pkg/net/http/httputil/#ReverseProxy.FlushInterval

    // A negative value means to flush immediately
    // after each write to the client.
    // The FlushInterval is ignored when ReverseProxy
    // recognizes a response as a streaming response;
    // for such responses, writes are flushed to the client
    // immediately.
    FlushInterval time.Duration

Have you tried Go 1.12? Go 1.12rc1 is out.

@mattmoor
Copy link
Member

That's actually perfect, we switched to 1.12 for websocket support yesterday :)

@mattmoor
Copy link
Member

Thanks @bradfitz, I'll try that out when I get back

@mattmoor
Copy link
Member

Hmm, this doesn't seem to do it.

@mattmoor
Copy link
Member

@tcnghia and I got it. queue.timeoutWriter doesn't implement http.Flusher :)

mattmoor added a commit to mattmoor/serving that referenced this issue Feb 17, 2019
In Go 1.12 setting `FlushInterval` to -1 makes it `Flush()` the ResponseWriter on each `Write()` (thanks @bradfitz).

We also need the `ResponseWriter` wrappers we have to implement `http.Flusher` for this to work.

Fixes: knative#3188
knative-prow-robot pushed a commit that referenced this issue Feb 17, 2019
In Go 1.12 setting `FlushInterval` to -1 makes it `Flush()` the ResponseWriter on each `Write()` (thanks @bradfitz).

We also need the `ResponseWriter` wrappers we have to implement `http.Flusher` for this to work.

Fixes: #3188
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/networking kind/bug Categorizes issue or PR as related to a bug.
Projects
None yet
Development

No branches or pull requests

6 participants