Skip to content

Added maxBadConnRetries to limit the number of times to obtain broken… #1837

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion internal/pool/export_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
package pool

import "time"
import (
"net"
"time"
)

func (cn *Conn) SetCreatedAt(tm time.Time) {
cn.createdAt = tm
}

func (cn *Conn) NetConn() net.Conn {
return cn.netConn
}

func MaxBadConnRetries() int {
return maxBadConnRetries
}
10 changes: 9 additions & 1 deletion internal/pool/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ import (
"github.com/go-redis/redis/v8/internal"
)

// maxBadConnRetries is the maximum number of attempts to obtain a valid connection from the connection pool.
// When this number is exceeded, a new connection is created directly.
const maxBadConnRetries = 2

var (
// ErrClosed performs any operation on the closed client will return this error.
ErrClosed = errors.New("redis: client is closed")
Expand Down Expand Up @@ -235,7 +239,7 @@ func (p *ConnPool) Get(ctx context.Context) (*Conn, error) {
return nil, err
}

for {
for i := 0; i < maxBadConnRetries; i++ {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we need some limit here after #1824 was merged, but this won't help with #1737.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And I see that you already realize this :)

p.connsMu.Lock()
cn := p.popIdle()
p.connsMu.Unlock()
Expand All @@ -253,6 +257,10 @@ func (p *ConnPool) Get(ctx context.Context) (*Conn, error) {
return cn, nil
}

// After the connection pool is empty or after trying maxBadConnRetries times,
// we still get a broken connection.
// At this time, we directly create a new connection.

atomic.AddUint32(&p.stats.Misses, 1)

newcn, err := p.newConn(ctx, true)
Expand Down
53 changes: 52 additions & 1 deletion internal/pool/pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package pool_test

import (
"context"
"net"
"sync"
"testing"
"time"
Expand All @@ -27,7 +28,7 @@ var _ = Describe("ConnPool", func() {
})

AfterEach(func() {
connPool.Close()
_ = connPool.Close()
})

It("should unblock client when conn is removed", func() {
Expand Down Expand Up @@ -81,6 +82,56 @@ var _ = Describe("ConnPool", func() {
})
})

var _ = Describe("bad conn", func() {
ctx := context.Background()
var opt *pool.Options
var connPool *pool.ConnPool

BeforeEach(func() {
opt = &pool.Options{
Dialer: dummyDialer,
PoolSize: 10,
PoolTimeout: time.Hour,
IdleTimeout: time.Minute,
IdleCheckFrequency: 10 * time.Second,
}
connPool = pool.NewConnPool(opt)
})

AfterEach(func() {
_ = connPool.Close()
})

It("should maxBadConnRetries", func() {
var err error
conns := make([]*pool.Conn, opt.PoolSize)
for i := 0; i < opt.PoolSize; i++ {
conns[i], err = connPool.Get(ctx)
Expect(err).NotTo(HaveOccurred())
}
for i := 0; i < opt.PoolSize; i++ {
// Damage it.
_ = conns[i].Close()
connPool.Put(ctx, conns[i])
}

var newConn *net.TCPConn
opt.Dialer = func(ctx context.Context) (net.Conn, error) {
newConn = &net.TCPConn{}
return newConn, nil
}

conn, err := connPool.Get(ctx)
Expect(err).NotTo(HaveOccurred())
Expect(conn.NetConn()).To(Equal(newConn))

stats := connPool.Stats()
Expect(stats.IdleConns).To(Equal(uint32(opt.PoolSize - pool.MaxBadConnRetries())))
Expect(stats.Misses).To(Equal(uint32(opt.PoolSize + 1)))
Expect(stats.Hits).To(Equal(uint32(0)))
})
})

var _ = Describe("MinIdleConns", func() {
const poolSize = 100
ctx := context.Background()
Expand Down