Description
An internal discard limit is applied to limit discarding bytes from a connection on close. It's currently at 4MiB:
Line 43 in 734ea94
However when connect receives a large message payload it discards the entire message to read the max size and report an error :
Lines 253 to 257 in 734ea94
connect-go/protocol_connect.go
Lines 1087 to 1091 in 734ea94
This avoids the discard limit and will read up to the max message size (max uint32, and potentially more in connect unary?). This also can read much more than the connect.WithReadMaxBytes
limited, although it won't keep the total payload in memory.
To Reproduce
To showcase I wrote a quick test to time a large payload with a server that has a 2MiB limit. Each iteration takes around 4.5s locally. Altering the bounds to error and not discard reduce this to less than 1s (presumably the time to marshal and start sending the large payload).
func TestShowDiscard(t *testing.T) {
mux := http.NewServeMux()
mux.Handle(
pingv1connect.NewPingServiceHandler(
&ExamplePingServer{},
connect.WithReadMaxBytes(2*1024*1024), // 2 MiB
),
)
server := httptest.NewServer(mux)
t.Cleanup(server.Close)
httpClient := server.Client()
httpTransport, ok := httpClient.Transport.(*http.Transport)
assert.True(t, ok)
httpTransport.DisableCompression = true
clients := []struct {
name string
opts []connect.ClientOption
}{{
name: "connect",
opts: []connect.ClientOption{},
}, {
name: "grpc",
opts: []connect.ClientOption{
connect.WithGRPC(),
},
}, {
name: "grpcweb",
opts: []connect.ClientOption{
connect.WithGRPCWeb(),
},
}}
bigPayload := strings.Repeat("a", math.MaxUint32-10)
msg := &pingv1.PingRequest{Text: bigPayload}
size := proto.Size(msg)
t.Log("size", size)
if size > math.MaxUint32 {
t.Fatal("message too big")
}
for _, client := range clients {
now := time.Now()
t.Log("start", client.name, now)
client := pingv1connect.NewPingServiceClient(
httpClient,
server.URL,
connect.WithClientOptions(client.opts...),
)
if _, err := client.Ping(
context.Background(), connect.NewRequest(&pingv1.PingRequest{Text: bigPayload}),
); err != nil {
t.Log(err) // resource_exhausted: message size 4294967291 is larger than configured max 2097152
}
t.Log("duration", time.Since(now))
}
}
Open questions
- Should the discard limits be applied to message discarding?
- Does the number of bytes read affect the discard limit?
Additional Info
Opened #606 to explore a solution, but needs clarification on how discard limits should be calculated.
Activity