Skip to content

Commit 2927e15

Browse files
committed
Retry BadConnError
1 parent 056ad27 commit 2927e15

File tree

9 files changed

+61
-37
lines changed

9 files changed

+61
-37
lines changed

cluster.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -809,7 +809,7 @@ func (c *ClusterClient) process(ctx context.Context, cmd Cmder) error {
809809
continue
810810
}
811811

812-
if isRetryableError(err, true) {
812+
if isRetryableError(err, cmd.readTimeout() == nil) {
813813
// First retry the same node.
814814
if attempt == 0 {
815815
continue

error.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,12 @@ import (
66
"net"
77
"strings"
88

9-
"github.com/go-redis/redis/internal/pool"
109
"github.com/go-redis/redis/internal/proto"
1110
)
1211

1312
func isRetryableError(err error, retryTimeout bool) bool {
1413
switch err {
15-
case nil, context.Canceled, context.DeadlineExceeded, pool.ErrBadConn:
14+
case nil, context.Canceled, context.DeadlineExceeded:
1615
return false
1716
case io.EOF:
1817
return true
@@ -49,8 +48,6 @@ func isBadConn(err error, allowTimeout bool) bool {
4948
switch err {
5049
case nil:
5150
return false
52-
case pool.ErrBadConn:
53-
return true
5451
}
5552
if isRedisError(err) {
5653
return isReadOnlyError(err) // #790

internal/pool/bench_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ func BenchmarkPoolGetRemove(b *testing.B) {
8686
if err != nil {
8787
b.Fatal(err)
8888
}
89-
connPool.Remove(cn)
89+
connPool.Remove(cn, nil)
9090
}
9191
})
9292
})

internal/pool/pool.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ type Pooler interface {
3939

4040
Get(context.Context) (*Conn, error)
4141
Put(*Conn)
42-
Remove(*Conn)
42+
Remove(*Conn, error)
4343

4444
Len() int
4545
IdleLen() int
@@ -311,7 +311,7 @@ func (p *ConnPool) popIdle() *Conn {
311311

312312
func (p *ConnPool) Put(cn *Conn) {
313313
if !cn.pooled {
314-
p.Remove(cn)
314+
p.Remove(cn, nil)
315315
return
316316
}
317317

@@ -322,7 +322,7 @@ func (p *ConnPool) Put(cn *Conn) {
322322
p.freeTurn()
323323
}
324324

325-
func (p *ConnPool) Remove(cn *Conn) {
325+
func (p *ConnPool) Remove(cn *Conn, reason error) {
326326
p.removeConnWithLock(cn)
327327
p.freeTurn()
328328
_ = p.closeConn(cn)

internal/pool/pool_single.go

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,26 @@ const (
1212
stateClosed = 2
1313
)
1414

15-
var ErrBadConn = fmt.Errorf("pg: Conn is in a bad state")
15+
type BadConnError struct {
16+
wrapped error
17+
}
18+
19+
func (e BadConnError) Error() string {
20+
return "pg: Conn is in a bad state"
21+
}
22+
23+
func (e BadConnError) Unwrap() error {
24+
return e.wrapped
25+
}
1626

1727
type SingleConnPool struct {
1828
pool Pooler
1929

2030
state uint32 // atomic
2131
ch chan *Conn
2232

23-
level int32 // atomic
24-
_hasBadConn uint32 // atomic
33+
level int32 // atomic
34+
_badConnError atomic.Value
2535
}
2636

2737
var _ Pooler = (*SingleConnPool)(nil)
@@ -66,10 +76,10 @@ func (p *SingleConnPool) Get(c context.Context) (*Conn, error) {
6676
if atomic.CompareAndSwapUint32(&p.state, stateDefault, stateInited) {
6777
return cn, nil
6878
}
69-
p.pool.Remove(cn)
79+
p.pool.Remove(cn, ErrClosed)
7080
case stateInited:
71-
if p.hasBadConn() {
72-
return nil, ErrBadConn
81+
if err := p.badConnError(); err != nil {
82+
return nil, err
7383
}
7484
cn, ok := <-p.ch
7585
if !ok {
@@ -95,20 +105,20 @@ func (p *SingleConnPool) Put(cn *Conn) {
95105
}
96106

97107
func (p *SingleConnPool) freeConn(cn *Conn) {
98-
if p.hasBadConn() {
99-
p.pool.Remove(cn)
108+
if err := p.badConnError(); err != nil {
109+
p.pool.Remove(cn, err)
100110
} else {
101111
p.pool.Put(cn)
102112
}
103113
}
104114

105-
func (p *SingleConnPool) Remove(cn *Conn) {
115+
func (p *SingleConnPool) Remove(cn *Conn, reason error) {
106116
defer func() {
107117
if recover() != nil {
108-
p.pool.Remove(cn)
118+
p.pool.Remove(cn, ErrClosed)
109119
}
110120
}()
111-
atomic.StoreUint32(&p._hasBadConn, 1)
121+
p._badConnError.Store(BadConnError{wrapped: reason})
112122
p.ch <- cn
113123
}
114124

@@ -158,7 +168,7 @@ func (p *SingleConnPool) Close() error {
158168
}
159169

160170
func (p *SingleConnPool) Reset() error {
161-
if !atomic.CompareAndSwapUint32(&p._hasBadConn, 1, 0) {
171+
if p.badConnError() == nil {
162172
return nil
163173
}
164174

@@ -167,7 +177,8 @@ func (p *SingleConnPool) Reset() error {
167177
if !ok {
168178
return ErrClosed
169179
}
170-
p.pool.Remove(cn)
180+
p.pool.Remove(cn, ErrClosed)
181+
p._badConnError.Store(nil)
171182
default:
172183
return fmt.Errorf("pg: SingleConnPool does not have a Conn")
173184
}
@@ -180,6 +191,9 @@ func (p *SingleConnPool) Reset() error {
180191
return nil
181192
}
182193

183-
func (p *SingleConnPool) hasBadConn() bool {
184-
return atomic.LoadUint32(&p._hasBadConn) == 1
194+
func (p *SingleConnPool) badConnError() error {
195+
if v := p._badConnError.Load(); v != nil {
196+
return v.(BadConnError)
197+
}
198+
return nil
185199
}

internal/pool/pool_sticky.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,13 @@ func (p *StickyConnPool) putUpstream() {
5858

5959
func (p *StickyConnPool) Put(cn *Conn) {}
6060

61-
func (p *StickyConnPool) removeUpstream() {
62-
p.pool.Remove(p.cn)
61+
func (p *StickyConnPool) removeUpstream(reason error) {
62+
p.pool.Remove(p.cn, reason)
6363
p.cn = nil
6464
}
6565

66-
func (p *StickyConnPool) Remove(cn *Conn) {
67-
p.removeUpstream()
66+
func (p *StickyConnPool) Remove(cn *Conn, reason error) {
67+
p.removeUpstream(reason)
6868
}
6969

7070
func (p *StickyConnPool) Len() int {
@@ -104,7 +104,7 @@ func (p *StickyConnPool) Close() error {
104104
if p.reusable {
105105
p.putUpstream()
106106
} else {
107-
p.removeUpstream()
107+
p.removeUpstream(ErrClosed)
108108
}
109109
}
110110

internal/pool/pool_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ var _ = Describe("ConnPool", func() {
6565
// ok
6666
}
6767

68-
connPool.Remove(cn)
68+
connPool.Remove(cn, nil)
6969

7070
// Check that Get is unblocked.
7171
select {
@@ -128,7 +128,7 @@ var _ = Describe("MinIdleConns", func() {
128128

129129
Context("after Remove", func() {
130130
BeforeEach(func() {
131-
connPool.Remove(cn)
131+
connPool.Remove(cn, nil)
132132
})
133133

134134
It("has idle connections", func() {
@@ -205,7 +205,7 @@ var _ = Describe("MinIdleConns", func() {
205205
BeforeEach(func() {
206206
perform(len(cns), func(i int) {
207207
mu.RLock()
208-
connPool.Remove(cns[i])
208+
connPool.Remove(cns[i], nil)
209209
mu.RUnlock()
210210
})
211211

@@ -355,7 +355,7 @@ var _ = Describe("conns reaper", func() {
355355
Expect(connPool.Len()).To(Equal(4))
356356
Expect(connPool.IdleLen()).To(Equal(0))
357357

358-
connPool.Remove(cn)
358+
connPool.Remove(cn, nil)
359359

360360
Expect(connPool.Len()).To(Equal(3))
361361
Expect(connPool.IdleLen()).To(Equal(0))
@@ -413,7 +413,7 @@ var _ = Describe("race", func() {
413413
cn, err := connPool.Get(c)
414414
Expect(err).NotTo(HaveOccurred())
415415
if err == nil {
416-
connPool.Remove(cn)
416+
connPool.Remove(cn, nil)
417417
}
418418
}
419419
})

internal/util.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,13 @@ func isLower(s string) bool {
4444
}
4545
return true
4646
}
47+
48+
func Unwrap(err error) error {
49+
u, ok := err.(interface {
50+
Unwrap() error
51+
})
52+
if !ok {
53+
return nil
54+
}
55+
return u.Unwrap()
56+
}

redis.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,10 @@ func (c *baseClient) _getConn(ctx context.Context) (*pool.Conn, error) {
171171

172172
err = c.initConn(ctx, cn)
173173
if err != nil {
174-
c.connPool.Remove(cn)
174+
c.connPool.Remove(cn, err)
175+
if err := internal.Unwrap(err); err != nil {
176+
return nil, err
177+
}
175178
return nil, err
176179
}
177180

@@ -226,7 +229,7 @@ func (c *baseClient) releaseConn(cn *pool.Conn, err error) {
226229
}
227230

228231
if isBadConn(err, false) {
229-
c.connPool.Remove(cn)
232+
c.connPool.Remove(cn, err)
230233
} else {
231234
c.connPool.Put(cn)
232235
}
@@ -240,7 +243,7 @@ func (c *baseClient) releaseConnStrict(cn *pool.Conn, err error) {
240243
if err == nil || isRedisError(err) {
241244
c.connPool.Put(cn)
242245
} else {
243-
c.connPool.Remove(cn)
246+
c.connPool.Remove(cn, err)
244247
}
245248
}
246249

0 commit comments

Comments
 (0)