Skip to content

Commit 3119a53

Browse files
drpcmanager: end-to-end test for flow-control enablement
The manager already threads drpcstream.Options through newStream, so enabling FlowControl in Options.Stream installs windows on every stream the manager creates (client and server) with no additional wiring. Add an end-to-end test over a real net.Pipe connection between two flow-control-enabled managers: a message larger than the send window completes only if credit grants flow back across the connection, exercising the full stream-level credit loop (gate -> grant-on-dispatch -> apply-grant) through the real read/write paths. Co-Authored-By: roachdev-claude <roachdev-claude-bot@cockroachlabs.com>
1 parent ff4c7f2 commit 3119a53

1 file changed

Lines changed: 72 additions & 0 deletions

File tree

drpcmanager/flow_control_test.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright (C) 2026 Cockroach Labs.
2+
// See LICENSE for copying information.
3+
4+
package drpcmanager
5+
6+
import (
7+
"context"
8+
"errors"
9+
"io"
10+
"net"
11+
"testing"
12+
13+
"github.com/zeebo/assert"
14+
15+
"storj.io/drpc/drpcstream"
16+
"storj.io/drpc/drpctest"
17+
"storj.io/drpc/drpcwire"
18+
)
19+
20+
// Enabling flow control through the manager's Stream options installs windows
21+
// on every stream it creates (client and server), so a message larger than the
22+
// send window only completes if credit grants flow across the real connection.
23+
func TestManager_FlowControlEndToEnd(t *testing.T) {
24+
ctx := drpctest.NewTracker(t)
25+
defer ctx.Close()
26+
27+
cconn, sconn := net.Pipe()
28+
defer func() { _ = cconn.Close() }()
29+
defer func() { _ = sconn.Close() }()
30+
31+
opts := Options{Stream: drpcstream.Options{
32+
SplitSize: 64 << 10,
33+
FlowControl: drpcstream.FlowControl{
34+
Enabled: true,
35+
StreamWindow: 128 << 10, // 2 frames of initial credit
36+
HighWater: 1 << 20,
37+
GrantThreshold: 64 << 10,
38+
},
39+
}}
40+
41+
cman := NewWithOptions(cconn, Client, opts)
42+
defer func() { _ = cman.Close() }()
43+
sman := NewWithOptions(sconn, Server, opts)
44+
defer func() { _ = sman.Close() }()
45+
46+
// 256 KiB is four frames: two fit in the initial window, the rest can only
47+
// be sent as the server dispatches frames and returns credit.
48+
msg := make([]byte, 256<<10)
49+
50+
ctx.Run(func(ctx context.Context) {
51+
stream, err := cman.NewClientStream(ctx, "rpc")
52+
assert.NoError(t, err)
53+
assert.NoError(t, stream.RawWrite(drpcwire.KindInvoke, []byte("rpc")))
54+
assert.NoError(t, stream.RawWrite(drpcwire.KindMessage, msg))
55+
assert.NoError(t, stream.Close())
56+
})
57+
58+
ctx.Run(func(ctx context.Context) {
59+
stream, _, err := sman.NewServerStream(ctx)
60+
assert.NoError(t, err)
61+
defer func() { _ = stream.Close() }()
62+
63+
got, err := stream.RawRecv()
64+
assert.NoError(t, err)
65+
assert.Equal(t, len(got), len(msg))
66+
67+
_, err = stream.RawRecv()
68+
assert.That(t, errors.Is(err, io.EOF))
69+
})
70+
71+
ctx.Wait()
72+
}

0 commit comments

Comments
 (0)