Skip to content

Commit 1c936ae

Browse files
vttest: fix race from randomPort() (#18899)
Signed-off-by: Tim Vaillancourt <[email protected]>
1 parent 73afe9b commit 1c936ae

File tree

2 files changed

+35
-3
lines changed

2 files changed

+35
-3
lines changed

go/vt/vttest/environment.go

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,25 @@ import (
2121
"net"
2222
"os"
2323
"path"
24+
"slices"
2425
"strconv"
2526
"strings"
27+
"sync"
2628

2729
"vitess.io/vitess/go/vt/proto/vttest"
2830

2931
// we use gRPC everywhere, so import the vtgate client.
3032
_ "vitess.io/vitess/go/vt/vtgate/grpcvtgateconn"
3133
)
3234

35+
var (
36+
// randomPortsMu provides synchronization for randomPorts().
37+
randomPortsMu sync.Mutex
38+
39+
// usedRandomPorts stores ports that have been used by remotePort().
40+
usedRandomPorts []int
41+
)
42+
3343
// Environment is the interface that customizes the global settings for
3444
// the test cluster. Usually the same environment settings are shared by
3545
// all the LocalCluster instances in a given test suite, with each instance
@@ -235,21 +245,31 @@ func tmpdir(dataroot string) (dir string, err error) {
235245
// randomPort gets a random port that is available for a TCP connection.
236246
// After we generate a random port, we try to establish tcp connections on it and the next 5 values.
237247
// If any of them fail, then we try a different port.
238-
func randomPort() int {
248+
func randomPort() (port int) {
249+
randomPortsMu.Lock()
250+
defer randomPortsMu.Unlock()
239251
for {
240-
port := int(rand.Int32N(20000) + 10000)
252+
portBase := int(rand.Int32N(20000) + 10000)
241253
portInUse := false
254+
portRange := make([]int, 0, 6)
242255
for i := 0; i < 6; i++ {
243-
ln, err := net.Listen("tcp", net.JoinHostPort("127.0.0.1", strconv.Itoa(port+i)))
256+
port = portBase + i
257+
if slices.Contains(usedRandomPorts, port) {
258+
portInUse = true
259+
break
260+
}
261+
ln, err := net.Listen("tcp", net.JoinHostPort("127.0.0.1", strconv.Itoa(port)))
244262
if err != nil {
245263
portInUse = true
246264
break
247265
}
266+
portRange = append(portRange, port)
248267
ln.Close()
249268
}
250269
if portInUse {
251270
continue
252271
}
272+
usedRandomPorts = append(usedRandomPorts, portRange...)
253273
return port
254274
}
255275
}

go/vt/vttest/environment_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,15 @@ func TestVtcomboArguments(t *testing.T) {
4444
assert.ElementsMatch(t, expectedServiceList, serviceMapList, "--service-map list does not contain expected vtcombo services")
4545
})
4646
}
47+
48+
func TestVtcomboRandomPort(t *testing.T) {
49+
require.Empty(t, usedRandomPorts)
50+
port := randomPort()
51+
// 10000-30000 is the range the rand call in randomPorts() can return
52+
require.GreaterOrEqual(t, port, 10000)
53+
require.LessOrEqual(t, port, 30000)
54+
require.Len(t, usedRandomPorts, 6)
55+
require.Contains(t, usedRandomPorts, port)
56+
require.NotEqual(t, port, randomPort())
57+
require.Len(t, usedRandomPorts, 12)
58+
}

0 commit comments

Comments
 (0)