Skip to content

Commit f071896

Browse files
authored
Merge pull request #544 from 2hdddg/count-pending-connects
Count pending connects when considering max pool size
2 parents d38585d + 49e8906 commit f071896

File tree

2 files changed

+76
-15
lines changed

2 files changed

+76
-15
lines changed

src/internal/pool.js

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class Pool {
5656
this._maxSize = config.maxSize
5757
this._acquisitionTimeout = config.acquisitionTimeout
5858
this._pools = {}
59+
this._pendingCreates = {}
5960
this._acquireRequests = {}
6061
this._activeResourceCounts = {}
6162
this._release = this._release.bind(this)
@@ -73,18 +74,11 @@ class Pool {
7374
const key = address.asKey()
7475

7576
if (resource) {
76-
if (
77-
this._maxSize &&
78-
this.activeResourceCount(address) >= this._maxSize
79-
) {
80-
this._destroy(resource)
81-
} else {
82-
resourceAcquired(key, this._activeResourceCounts)
83-
if (this._log.isDebugEnabled()) {
84-
this._log.debug(`${resource} acquired from the pool ${key}`)
85-
}
86-
return resource
77+
resourceAcquired(key, this._activeResourceCounts)
78+
if (this._log.isDebugEnabled()) {
79+
this._log.debug(`${resource} acquired from the pool ${key}`)
8780
}
81+
return resource
8882
}
8983

9084
// We're out of resources and will try to acquire later on when an existing resource is released.
@@ -185,6 +179,7 @@ class Pool {
185179
if (!pool) {
186180
pool = []
187181
this._pools[key] = pool
182+
this._pendingCreates[key] = 0
188183
}
189184
while (pool.length) {
190185
const resource = pool.pop()
@@ -201,12 +196,29 @@ class Pool {
201196
}
202197
}
203198

204-
if (this._maxSize && this.activeResourceCount(address) >= this._maxSize) {
205-
return null
199+
// Ensure requested max pool size
200+
if (this._maxSize > 0) {
201+
// Include pending creates when checking pool size since these probably will add
202+
// to the number when fulfilled.
203+
const numConnections =
204+
this.activeResourceCount(address) + this._pendingCreates[key]
205+
if (numConnections >= this._maxSize) {
206+
// Will put this request in queue instead since the pool is full
207+
return null
208+
}
206209
}
207210

208211
// there exist no idle valid resources, create a new one for acquisition
209-
return await this._create(address, this._release)
212+
// Keep track of how many pending creates there are to avoid making too many connections.
213+
this._pendingCreates[key] = this._pendingCreates[key] + 1
214+
let resource
215+
try {
216+
// Invoke callback that creates actual connection
217+
resource = await this._create(address, this._release)
218+
} finally {
219+
this._pendingCreates[key] = this._pendingCreates[key] - 1
220+
}
221+
return resource
210222
}
211223

212224
async _release (address, resource) {

test/internal/pool.test.js

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -562,10 +562,59 @@ describe('#unit Pool', async () => {
562562
expectNumberOfAcquisitionRequests(pool, address, 0)
563563
})
564564

565+
const address = ServerAddress.fromUrl('bolt://localhost:7687')
566+
567+
it('should consider pending connects when evaluating max pool size', async () => {
568+
const conns = []
569+
const pool = new Pool({
570+
// Hook into connection creation to track when and what connections that are
571+
// created.
572+
create: (server, release) => {
573+
// Create a fake connection that makes it possible control when it's connected
574+
// and released from the outer scope.
575+
const conn = {
576+
server: server,
577+
release: release
578+
}
579+
const promise = new Promise((resolve, reject) => {
580+
conn.resolve = resolve
581+
conn.reject = reject
582+
})
583+
// Put the connection in a list in outer scope even though there only should be
584+
// one when the test is succeeding.
585+
conns.push(conn)
586+
return promise
587+
},
588+
// Setup pool to only allow one connection
589+
config: new PoolConfig(1, 100000)
590+
})
591+
592+
// Make the first request for a connection, this will be hanging waiting for the
593+
// connect promise to be resolved.
594+
const req1 = pool.acquire(address)
595+
expect(conns.length).toEqual(1)
596+
597+
// Make another request to the same server, this should not try to acquire another
598+
// connection since the pool will be full when the connection for the first request
599+
// is resolved.
600+
const req2 = pool.acquire(address)
601+
expect(conns.length).toEqual(1)
602+
603+
// Let's fulfill the connect promise belonging to the first request.
604+
conns[0].resolve(conns[0])
605+
await expectAsync(req1).toBeResolved()
606+
607+
// Release the connection, it should be picked up by the second request.
608+
conns[0].release(address, conns[0])
609+
await expectAsync(req2).toBeResolved()
610+
611+
// Just to make sure that there hasn't been any new connection.
612+
expect(conns.length).toEqual(1)
613+
})
614+
565615
it('should not time out if max pool size is not set', async () => {
566616
let counter = 0
567617

568-
const address = ServerAddress.fromUrl('bolt://localhost:7687')
569618
const pool = new Pool({
570619
create: (server, release) =>
571620
Promise.resolve(new Resource(server, counter++, release)),

0 commit comments

Comments
 (0)