Skip to content

Commit 66068f4

Browse files
committed
test: add syntest around connection open and close
1 parent 746e196 commit 66068f4

File tree

1 file changed

+79
-0
lines changed

1 file changed

+79
-0
lines changed

clickhouse_test.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//go:build go1.25
2+
3+
package clickhouse
4+
5+
import (
6+
"fmt"
7+
"sync"
8+
"testing"
9+
"testing/synctest"
10+
)
11+
12+
// TestIdleConnectionCleanupLeak demonstrates that startAutoCloseIdleConnections
13+
// goroutines are leaked when connections are opened and closed.
14+
//
15+
// This test uses Go's synctest package (stably available in Go 1.25+) to deterministically
16+
// detect the leaked goroutine without relying on timing or sleep calls.
17+
func TestIdleConnectionCleanupLeak(t *testing.T) {
18+
synctest.Test(t, func(t *testing.T) {
19+
conn, err := Open(&Options{
20+
Addr: []string{"localhost:9000"},
21+
})
22+
if err != nil {
23+
t.Fatalf("failed to open connection: %v", err)
24+
}
25+
26+
// Close the connection - this should stop the goroutine
27+
if err := conn.Close(); err != nil {
28+
t.Fatalf("failed to close connection: %v", err)
29+
}
30+
31+
// Wait for all goroutines in this synctest bubble to exit
32+
// This will hang if background goroutines are left behind.
33+
synctest.Wait()
34+
})
35+
}
36+
37+
// TestIdleConnectionCleanupLeak demonstrates that startAutoCloseIdleConnections
38+
// goroutines are leaked when connections are opened and closed.
39+
//
40+
// This test uses Go's synctest package (stably available in Go 1.25+) to deterministically
41+
// detect the leaked goroutine without relying on timing or sleep calls.
42+
func TestIdleConnectionCleanupLeakConcurrent(t *testing.T) {
43+
synctest.Test(t, func(t *testing.T) {
44+
errs := make(chan error)
45+
46+
var wg sync.WaitGroup
47+
for range 100 {
48+
wg.Add(1)
49+
go func() {
50+
defer wg.Done()
51+
conn, err := Open(&Options{
52+
Addr: []string{"localhost:9000"},
53+
})
54+
if err != nil {
55+
errs <- fmt.Errorf("failed to open connection: %w", err)
56+
}
57+
58+
// Close the connection - this should stop the goroutine
59+
if err := conn.Close(); err != nil {
60+
errs <- fmt.Errorf("failed to close connection: %w", err)
61+
}
62+
}()
63+
}
64+
65+
go func() {
66+
wg.Wait()
67+
close(errs)
68+
}()
69+
70+
for err := range errs {
71+
// any error attempting to open or close should be fatal
72+
t.Fatal(err)
73+
}
74+
75+
// Wait for all goroutines in this synctest bubble to exit
76+
// This will hang if background goroutines are left behind.
77+
synctest.Wait()
78+
})
79+
}

0 commit comments

Comments
 (0)