Skip to content

Commit c65e954

Browse files
authored
fix(connpool): adjust globalIdle based on the number of connections decreased during the Get. (#698)
* fix(connpool): adjust globalIdle based on the number of connections decreased during the Get
1 parent 1ba2a48 commit c65e954

2 files changed

Lines changed: 61 additions & 9 deletions

File tree

pkg/remote/connpool/long_pool.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -104,17 +104,18 @@ type pool struct {
104104
maxIdleTimeout time.Duration // the idle connection will be cleaned if the idle time exceeds maxIdleTimeout.
105105
}
106106

107-
// Get gets the first active connection from the idleList.
108-
func (p *pool) Get() (*longConn, bool) {
107+
// Get gets the first active connection from the idleList. Return the number of connections decreased during the Get.
108+
func (p *pool) Get() (*longConn, bool, int) {
109109
p.mu.Lock()
110110
// Get the first active one
111-
i := len(p.idleList) - 1
111+
n := len(p.idleList)
112+
i := n - 1
112113
for ; i >= 0; i-- {
113114
o := p.idleList[i]
114115
if o.IsActive() {
115116
p.idleList = p.idleList[:i]
116117
p.mu.Unlock()
117-
return o, true
118+
return o, true, n - i
118119
}
119120
// inactive object
120121
o.Close()
@@ -125,7 +126,7 @@ func (p *pool) Get() (*longConn, bool) {
125126
}
126127
p.idleList = p.idleList[:i]
127128
p.mu.Unlock()
128-
return nil, false
129+
return nil, false, n - i
129130
}
130131

131132
// Put puts back a connection to the pool.
@@ -223,9 +224,9 @@ type peer struct {
223224
// Get gets a connection with dialer and timeout. Dial a new connection if no idle connection in pool is available.
224225
func (p *peer) Get(d remote.Dialer, timeout time.Duration, reporter Reporter, addr string) (net.Conn, error) {
225226
var c net.Conn
226-
c, reused := p.pool.Get()
227+
c, reused, decNum := p.pool.Get()
228+
p.globalIdle.DecN(int64(decNum))
227229
if reused {
228-
p.globalIdle.Dec()
229230
return c, nil
230231
}
231232
// dial a new connection

pkg/remote/connpool/long_pool_test.go

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,15 @@ func TestPoolReuse(t *testing.T) {
7070
test.Assert(t, recycled == true)
7171
test.Assert(t, p.Len() == 1)
7272

73-
c, reused := p.Get()
73+
c, reused, decN := p.Get()
7474
test.Assert(t, c != nil)
7575
test.Assert(t, reused == true)
76+
test.Assert(t, decN == 1)
7677
count[c] = true
7778
test.Assert(t, len(count) == 1)
7879
}
7980

81+
// TestPoolGetInactiveConn tests the pool with only inactive connection. Get() should return nil.
8082
func TestPoolGetInactiveConn(t *testing.T) {
8183
ctrl := gomock.NewController(t)
8284
defer ctrl.Finish()
@@ -105,12 +107,61 @@ func TestPoolGetInactiveConn(t *testing.T) {
105107
recycled := p.Put(conn)
106108
test.Assert(t, recycled == true)
107109
// inactive
108-
conn, reused := p.Get()
110+
conn, reused, decNum := p.Get()
111+
test.Assert(t, decNum == 1)
109112
test.Assert(t, conn == nil)
110113
test.Assert(t, reused == false)
111114
test.Assert(t, closed == true)
112115
}
113116

117+
// TestPoolGetWithInactiveConn tests the pool with inactive connection. Get() should return the first active one.
118+
func TestPoolGetWithInactiveConn(t *testing.T) {
119+
ctrl := gomock.NewController(t)
120+
defer ctrl.Finish()
121+
122+
var (
123+
minIdle = 0
124+
maxIdle = 10
125+
maxIdleTimeout = time.Millisecond
126+
inactiveNum = 5
127+
)
128+
129+
p := newPool(minIdle, maxIdle, maxIdleTimeout)
130+
// put active conn
131+
activeConn := newLongConnForTest(ctrl, mockAddr0)
132+
recycled := p.Put(activeConn)
133+
test.Assert(t, recycled == true)
134+
135+
// put inactive conn
136+
closed := make([]bool, inactiveNum)
137+
for i := 0; i < inactiveNum; i++ {
138+
idx := i
139+
c := mocksnetpoll.NewMockConnection(ctrl)
140+
c.EXPECT().IsActive().Return(false).AnyTimes()
141+
c.EXPECT().Close().DoAndReturn(func() error {
142+
closed[idx] = true
143+
return nil
144+
}).AnyTimes()
145+
conn := &longConn{
146+
Conn: c,
147+
address: mockAddr0,
148+
}
149+
recycled := p.Put(conn)
150+
test.Assert(t, recycled == true)
151+
}
152+
test.Assert(t, p.Len() == inactiveNum+1)
153+
154+
// decNum should be inactiveNum + 1
155+
conn, reused, decNum := p.Get()
156+
test.Assert(t, conn != nil)
157+
test.Assert(t, reused == true)
158+
test.Assert(t, decNum == inactiveNum+1)
159+
// check if all inactive conns have been closed
160+
for i := 0; i < inactiveNum; i++ {
161+
test.Assert(t, closed[i] == true)
162+
}
163+
}
164+
114165
func TestPoolMaxIdle(t *testing.T) {
115166
ctrl := gomock.NewController(t)
116167
defer ctrl.Finish()

0 commit comments

Comments
 (0)