Skip to content

Commit 2701975

Browse files
hardeyheryker-uptycs
authored andcommitted
hotfix/v8_conn_pool_check_fd (redis#2431)
1 parent a8e56a2 commit 2701975

File tree

7 files changed

+121
-20
lines changed

7 files changed

+121
-20
lines changed

internal/pool/conncheck.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//go:build linux || darwin || dragonfly || freebsd || netbsd || openbsd || solaris || illumos
2+
// +build linux darwin dragonfly freebsd netbsd openbsd solaris illumos
3+
4+
package pool
5+
6+
import (
7+
"errors"
8+
"io"
9+
"net"
10+
"syscall"
11+
)
12+
13+
var errUnexpectedRead = errors.New("unexpected read from socket")
14+
15+
func connCheck(conn net.Conn) error {
16+
sysConn, ok := conn.(syscall.Conn)
17+
if !ok {
18+
return nil
19+
}
20+
rawConn, err := sysConn.SyscallConn()
21+
if err != nil {
22+
return err
23+
}
24+
25+
var sysErr error
26+
err = rawConn.Read(func(fd uintptr) bool {
27+
var buf [1]byte
28+
n, err := syscall.Read(int(fd), buf[:])
29+
switch {
30+
case n == 0 && err == nil:
31+
sysErr = io.EOF
32+
case n > 0:
33+
sysErr = errUnexpectedRead
34+
case err == syscall.EAGAIN || err == syscall.EWOULDBLOCK:
35+
sysErr = nil
36+
default:
37+
sysErr = err
38+
}
39+
return true
40+
})
41+
if err != nil {
42+
return err
43+
}
44+
45+
return sysErr
46+
}

internal/pool/conncheck_dummy.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//go:build !linux && !darwin && !dragonfly && !freebsd && !netbsd && !openbsd && !solaris && !illumos
2+
// +build !linux,!darwin,!dragonfly,!freebsd,!netbsd,!openbsd,!solaris,!illumos
3+
4+
package pool
5+
6+
import "net"
7+
8+
func connCheck(conn net.Conn) error {
9+
return nil
10+
}

internal/pool/conncheck_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//go:build linux || darwin || dragonfly || freebsd || netbsd || openbsd || solaris || illumos
2+
// +build linux darwin dragonfly freebsd netbsd openbsd solaris illumos
3+
4+
package pool
5+
6+
import (
7+
"net"
8+
"net/http/httptest"
9+
"testing"
10+
"time"
11+
)
12+
13+
func Test_connCheck(t *testing.T) {
14+
// tests with real conns
15+
ts := httptest.NewServer(nil)
16+
defer ts.Close()
17+
18+
t.Run("good conn", func(t *testing.T) {
19+
conn, err := net.DialTimeout(ts.Listener.Addr().Network(), ts.Listener.Addr().String(), time.Second)
20+
if err != nil {
21+
t.Fatalf(err.Error())
22+
}
23+
defer conn.Close()
24+
if err = connCheck(conn); err != nil {
25+
t.Fatalf(err.Error())
26+
}
27+
conn.Close()
28+
29+
if err = connCheck(conn); err == nil {
30+
t.Fatalf("expect has error")
31+
}
32+
})
33+
34+
t.Run("bad conn 2", func(t *testing.T) {
35+
conn, err := net.DialTimeout(ts.Listener.Addr().Network(), ts.Listener.Addr().String(), time.Second)
36+
if err != nil {
37+
t.Fatalf(err.Error())
38+
}
39+
defer conn.Close()
40+
41+
ts.Close()
42+
43+
if err = connCheck(conn); err == nil {
44+
t.Fatalf("expect has err")
45+
}
46+
})
47+
}

internal/pool/main_test.go

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,18 @@ func perform(n int, cbs ...func(int)) {
3535
}
3636

3737
func dummyDialer(context.Context) (net.Conn, error) {
38+
// return &net.TCPConn{}, nil
3839
return newDummyConn(), nil
3940
}
4041

4142
func newDummyConn() net.Conn {
4243
return &dummyConn{
43-
rawConn: new(dummyRawConn),
44+
rawConn: &dummyRawConn{},
4445
}
4546
}
4647

47-
var (
48-
_ net.Conn = (*dummyConn)(nil)
49-
_ syscall.Conn = (*dummyConn)(nil)
50-
)
48+
var _ net.Conn = (*dummyConn)(nil)
49+
var _ syscall.Conn = (*dummyConn)(nil)
5150

5251
type dummyConn struct {
5352
rawConn *dummyRawConn
@@ -95,17 +94,17 @@ func (d *dummyConn) SetWriteDeadline(t time.Time) error {
9594
var _ syscall.RawConn = (*dummyRawConn)(nil)
9695

9796
type dummyRawConn struct {
98-
mu sync.Mutex
9997
closed bool
98+
mux sync.Mutex
10099
}
101100

102101
func (d *dummyRawConn) Control(f func(fd uintptr)) error {
103102
return nil
104103
}
105104

106105
func (d *dummyRawConn) Read(f func(fd uintptr) (done bool)) error {
107-
d.mu.Lock()
108-
defer d.mu.Unlock()
106+
d.mux.Lock()
107+
defer d.mux.Unlock()
109108
if d.closed {
110109
return fmt.Errorf("dummyRawConn closed")
111110
}
@@ -115,9 +114,8 @@ func (d *dummyRawConn) Read(f func(fd uintptr) (done bool)) error {
115114
func (d *dummyRawConn) Write(f func(fd uintptr) (done bool)) error {
116115
return nil
117116
}
118-
119117
func (d *dummyRawConn) Close() {
120-
d.mu.Lock()
118+
d.mux.Lock()
121119
d.closed = true
122-
d.mu.Unlock()
120+
d.mux.Unlock()
123121
}

internal/pool/pool_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -323,8 +323,8 @@ var _ = Describe("conns reaper", func() {
323323
cn.SetUsedAt(time.Now().Add(-2 * idleTimeout))
324324
case "aged":
325325
cn.SetCreatedAt(time.Now().Add(-2 * maxAge))
326-
case "connCheck":
327-
_ = cn.Close()
326+
case "conncheck":
327+
cn.Close()
328328
}
329329
conns = append(conns, cn)
330330
staleConns = append(staleConns, cn)
@@ -411,7 +411,7 @@ var _ = Describe("conns reaper", func() {
411411

412412
assert("idle")
413413
assert("aged")
414-
assert("connCheck")
414+
assert("conncheck")
415415
})
416416

417417
var _ = Describe("race", func() {

pool_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ var _ = Describe("pool", func() {
7272
Expect(cmds).To(HaveLen(1))
7373
Expect(ping.Err()).NotTo(HaveOccurred())
7474
Expect(ping.Val()).To(Equal("PONG"))
75+
Expect(pipe.Close()).NotTo(HaveOccurred())
7576
})
7677

7778
pool := client.Pool()
@@ -86,11 +87,10 @@ var _ = Describe("pool", func() {
8687
cn.SetNetConn(&badConn{})
8788
client.Pool().Put(ctx, cn)
8889

89-
val, err := client.Ping(ctx).Result()
90+
err = client.Ping(ctx).Err()
9091
Expect(err).NotTo(HaveOccurred())
91-
Expect(val).To(Equal("PONG"))
9292

93-
val, err = client.Ping(ctx).Result()
93+
val, err := client.Ping(ctx).Result()
9494
Expect(err).NotTo(HaveOccurred())
9595
Expect(val).To(Equal("PONG"))
9696

tx_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ var _ = Describe("Tx", func() {
123123
Expect(num).To(Equal(int64(N)))
124124
})
125125

126-
It("should recover from bad connection", func() {
126+
It("should remove from bad connection", func() {
127127
// Put bad connection in the pool.
128128
cn, err := client.Pool().Get(context.Background())
129129
Expect(err).NotTo(HaveOccurred())
@@ -134,14 +134,14 @@ var _ = Describe("Tx", func() {
134134
do := func() error {
135135
err := client.Watch(ctx, func(tx *redis.Tx) error {
136136
_, err := tx.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
137-
pipe.Ping(ctx)
138-
return nil
137+
return pipe.Ping(ctx).Err()
139138
})
140139
return err
141140
})
142141
return err
143142
}
144143

144+
// connCheck will automatically remove damaged connections.
145145
err = do()
146146
Expect(err).NotTo(HaveOccurred())
147147
})

0 commit comments

Comments
 (0)