Skip to content

Commit 708152a

Browse files
committed
fix(tests): three CI failures — NagleBuf cap, stress test dedup, timeouts
MaxNagleBuf raised from 32 KB to 256 KB: the 32 KB cap silently dropped 64 KB data-exchange sends (IPC fire-and-forget never surfaced ErrSendBufFull back to the caller), causing TestDataExchangeLargePayload and TestLargeWriteSegmentation to hang until timeout. TestStressConcurrentConnections: 20 concurrent dials to the same (peerNode, dstPort) were all deduplicated to one connection by DialConnectionContext — fixed by giving each goroutine its own port (3000+idx) so dials are independent. Also reduced numConns 20→10 and moved base port away from built-in service ports (1001-1003). TestLargeWriteSegmentation: added 60 s read deadline so a stall fails fast instead of hanging until the global timeout fires. CI workflow: raised test timeout 120 s → 300 s for macOS arm64 runners.
1 parent e2273bd commit 708152a

6 files changed

Lines changed: 44 additions & 32 deletions

File tree

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
run: make build
3030

3131
- name: Unit tests
32-
run: go test -parallel 4 -count=1 -timeout 120s ./tests/ ./pkg/beacon/
32+
run: go test -parallel 4 -count=1 -timeout 300s ./tests/ ./pkg/beacon/
3333

3434
build:
3535
name: Build (${{ matrix.goos }}/${{ matrix.goarch }})

pkg/daemon/nagle_buf_bug_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import (
2929
// memory budget. Multiple writers would amplify this further.
3030
//
3131
// What v1.9.1's NagleBuf cap fix will change:
32-
// - introduce MaxNagleBuf = 8 * MaxSegmentSize (32 KB)
32+
// - introduce MaxNagleBuf = 64 * MaxSegmentSize (256 KB)
3333
// - SendData returns ErrSendBufFull when len(NagleBuf) + len(data) > MaxNagleBuf
3434
// - test assertion flips: NagleBuf plateaus at MaxNagleBuf;
3535
// SendData returns ErrSendBufFull on the oversized write.
@@ -63,7 +63,7 @@ func TestSendDataNagleBufGrowsUnbounded(t *testing.T) {
6363
// FIXED (v1.9.1): SendData now rejects an oversized write up-front
6464
// instead of appending it to NagleBuf. The 5 MiB write should
6565
// return ErrSendBufFull immediately and NagleBuf should remain
66-
// at 0 (not grow at all). MaxNagleBuf = 8 * MaxSegmentSize = 32 KB.
66+
// at 0 (not grow at all). MaxNagleBuf = 64 * MaxSegmentSize = 256 KB.
6767
const payloadSize = 5 * 1024 * 1024
6868
payload := make([]byte, payloadSize)
6969
for i := range payload {

pkg/daemon/ports.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,15 +153,17 @@ const (
153153
AcceptQueueLen = 64 // listener accept channel capacity
154154
SendBufLen = 256 // send buffer channel capacity (segments)
155155

156-
// MaxNagleBuf caps the per-connection NagleBuf at 8 segments
157-
// (32 KB). v1.9.1 fix: SendData previously appended without bound,
156+
// MaxNagleBuf caps the per-connection NagleBuf at 64 segments
157+
// (256 KB). v1.9.1 fix: SendData previously appended without bound,
158158
// so an application writing faster than the network could drain
159159
// (slow peer, full cwnd, packet loss) leaked memory linearly with
160160
// offered-but-undeliverable load. With many connections in that
161161
// state, daemon RSS climbed until OOM. SendData now returns
162162
// ErrSendBufFull when len(NagleBuf) + len(write) would exceed
163163
// this cap; callers must retry with backpressure.
164-
MaxNagleBuf = 8 * MaxSegmentSize
164+
// 256 KB accommodates the largest single data-exchange frame
165+
// (64 KB) with headroom, while still bounding per-connection memory.
166+
MaxNagleBuf = 64 * MaxSegmentSize
165167
)
166168

167169
// RTO parameters (RFC 6298)

tests/dataexchange_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ func TestDataExchangeLargePayload(t *testing.T) {
179179
t.Errorf("large binary mismatch: got %d bytes, expected %d", len(frame.Payload), len(largeData))
180180
}
181181
t.Logf("received large binary: %d bytes", len(frame.Payload))
182-
case <-time.After(10 * time.Second):
182+
case <-time.After(30 * time.Second):
183183
t.Fatal("timeout")
184184
}
185185
}

tests/segmentation_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ func TestLargeWriteSegmentation(t *testing.T) {
7676
}
7777
elapsed := time.Since(start)
7878

79-
// Read hash back
79+
// Read hash back — set a deadline so a stalled transfer fails fast on CI
80+
conn.SetReadDeadline(time.Now().Add(60 * time.Second))
8081
hashBuf := make([]byte, 32)
8182
n, err := conn.Read(hashBuf)
8283
if err != nil {

tests/stress_test.go

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,32 +18,41 @@ func TestStressConcurrentConnections(t *testing.T) {
1818
a := env.AddDaemon()
1919
b := env.AddDaemon()
2020

21-
// Echo server on A
22-
ln, err := a.Driver.Listen(1000)
23-
if err != nil {
24-
t.Fatalf("listen: %v", err)
25-
}
26-
go func() {
27-
for {
28-
conn, err := ln.Accept()
29-
if err != nil {
30-
return
31-
}
32-
go func() {
33-
defer conn.Close()
34-
buf := make([]byte, 65535)
35-
for {
36-
n, err := conn.Read(buf)
37-
if err != nil {
38-
return
39-
}
40-
conn.Write(buf[:n])
21+
// Each goroutine dials its own port so dials aren't deduplicated by
22+
// DialConnectionContext, which keys on (peerNode, dstPort).
23+
const numConns = 10
24+
const basePort = 3000 // avoid ports used by daemon services (1001-1003)
25+
26+
startEchoListener := func(ln *driver.Listener) {
27+
go func() {
28+
for {
29+
conn, err := ln.Accept()
30+
if err != nil {
31+
return
4132
}
42-
}()
33+
go func() {
34+
defer conn.Close()
35+
buf := make([]byte, 65535)
36+
for {
37+
n, err := conn.Read(buf)
38+
if err != nil {
39+
return
40+
}
41+
conn.Write(buf[:n])
42+
}
43+
}()
44+
}
45+
}()
46+
}
47+
48+
for i := 0; i < numConns; i++ {
49+
ln, err := a.Driver.Listen(uint16(basePort + i))
50+
if err != nil {
51+
t.Fatalf("listen port %d: %v", basePort+i, err)
4352
}
44-
}()
53+
startEchoListener(ln)
54+
}
4555

46-
const numConns = 20
4756
var wg sync.WaitGroup
4857
var successes atomic.Int32
4958
var failures atomic.Int32
@@ -61,7 +70,7 @@ func TestStressConcurrentConnections(t *testing.T) {
6170
}
6271
defer d.Close()
6372

64-
conn, err := d.DialAddr(a.Daemon.Addr(), 1000)
73+
conn, err := d.DialAddr(a.Daemon.Addr(), uint16(basePort+idx))
6574
if err != nil {
6675
t.Logf("conn %d: dial failed: %v", idx, err)
6776
failures.Add(1)

0 commit comments

Comments
 (0)