Skip to content

Commit d56026b

Browse files
committed
Merge branch 1.7 into 2.0
2 parents f63caf3 + 8ac9bbb commit d56026b

File tree

6 files changed

+323
-142
lines changed

6 files changed

+323
-142
lines changed

src/driver.js

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,15 @@ class Driver {
8080
this._authToken = authToken
8181
this._config = config
8282
this._log = Logger.create(config)
83-
this._pool = new Pool(
84-
this._createConnection.bind(this),
85-
this._destroyConnection.bind(this),
86-
this._validateConnection.bind(this),
87-
PoolConfig.fromDriverConfig(config),
88-
this._log
89-
)
83+
this._pool = new Pool({
84+
create: this._createConnection.bind(this),
85+
destroy: this._destroyConnection.bind(this),
86+
validate: this._validateConnection.bind(this),
87+
installIdleObserver: this._installIdleObserverOnConnection.bind(this),
88+
removeIdleObserver: this._removeIdleObserverOnConnection.bind(this),
89+
config: PoolConfig.fromDriverConfig(config),
90+
log: this._log
91+
})
9092

9193
/**
9294
* Reference to the connection provider. Initialized lazily by {@link _getOrCreateConnectionProvider}.
@@ -160,6 +162,14 @@ class Driver {
160162
return lifetime <= maxConnectionLifetime
161163
}
162164

165+
_installIdleObserverOnConnection (conn, observer) {
166+
conn._queueObserver(observer)
167+
}
168+
169+
_removeIdleObserverOnConnection (conn) {
170+
conn._updateCurrentObserver()
171+
}
172+
163173
/**
164174
* Dispose of a connection.
165175
* @return {Connection} the connection to dispose.

src/internal/pool.js

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,25 @@ class Pool {
3131
* @param {function} validate called at various times (like when an instance is acquired and
3232
* when it is returned). If this returns false, the resource will
3333
* be evicted
34+
* @param {function} installIdleObserver called when the resource is released back to pool
35+
* @param {function} removeIdleObserver called when the resource is acquired from the pool
3436
* @param {PoolConfig} config configuration for the new driver.
3537
* @param {Logger} log the driver logger.
3638
*/
37-
constructor (
38-
create,
39-
destroy = () => true,
40-
validate = () => true,
39+
constructor ({
40+
create = (address, release) => {},
41+
destroy = conn => true,
42+
validate = conn => true,
43+
installIdleObserver = (conn, observer) => {},
44+
removeIdleObserver = conn => {},
4145
config = PoolConfig.defaultConfig(),
4246
log = Logger.noOp()
43-
) {
47+
} = {}) {
4448
this._create = create
4549
this._destroy = destroy
4650
this._validate = validate
51+
this._installIdleObserver = installIdleObserver
52+
this._removeIdleObserver = removeIdleObserver
4753
this._maxSize = config.maxSize
4854
this._acquisitionTimeout = config.acquisitionTimeout
4955
this._pools = {}
@@ -165,6 +171,10 @@ class Pool {
165171
const resource = pool.pop()
166172

167173
if (this._validate(resource)) {
174+
if (this._removeIdleObserver) {
175+
this._removeIdleObserver(resource)
176+
}
177+
168178
// idle resource is valid and can be acquired
169179
return Promise.resolve(resource)
170180
} else {
@@ -197,6 +207,14 @@ class Pool {
197207
if (this._log.isDebugEnabled()) {
198208
this._log.debug(`${resource} released to the pool ${key}`)
199209
}
210+
if (this._installIdleObserver) {
211+
this._installIdleObserver(resource, {
212+
onError: () => {
213+
this._pools[key] = this._pools[key].filter(r => r !== resource)
214+
this._destroy(resource)
215+
}
216+
})
217+
}
200218
pool.push(resource)
201219
}
202220
} else {

test/internal/connection-providers.test.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1210,9 +1210,10 @@ function setupLoadBalancerToRememberRouters (loadBalancer, routersArray) {
12101210
}
12111211

12121212
function newPool () {
1213-
return new Pool((address, release) =>
1214-
Promise.resolve(new FakeConnection(address, release))
1215-
)
1213+
return new Pool({
1214+
create: (address, release) =>
1215+
Promise.resolve(new FakeConnection(address, release))
1216+
})
12161217
}
12171218

12181219
function expectRoutingTable (loadBalancer, routers, readers, writers) {

test/internal/least-connected-load-balancing-strategy.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ describe('LeastConnectedLoadBalancingStrategy', () => {
140140

141141
class DummyPool extends Pool {
142142
constructor (activeConnections) {
143-
super(() => 42)
143+
super({ create: () => 42 })
144144
this._activeConnections = activeConnections
145145
}
146146

test/internal/node/direct.driver.boltkit.test.js

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import neo4j from '../../../src'
2121
import { READ, WRITE } from '../../../src/driver'
2222
import boltStub from '../bolt-stub'
23-
import { SERVICE_UNAVAILABLE } from '../../../src/error'
23+
import { newError, SERVICE_UNAVAILABLE } from '../../../src/v1/error'
2424

2525
describe('direct driver with stub server', () => {
2626
let originalTimeout
@@ -438,6 +438,58 @@ describe('direct driver with stub server', () => {
438438
})
439439
})
440440

441+
it('should close connection if it dies sitting idle in connection pool', done => {
442+
if (!boltStub.supported) {
443+
done()
444+
return
445+
}
446+
447+
const server = boltStub.start(
448+
'./test/resources/boltstub/read_server_v3_read.script',
449+
9001
450+
)
451+
452+
boltStub.run(() => {
453+
const driver = boltStub.newDriver('bolt://127.0.0.1:9001')
454+
const session = driver.session({ defaultAccessMode: READ })
455+
456+
session
457+
.run('MATCH (n) RETURN n.name')
458+
.then(result => {
459+
const records = result.records
460+
expect(records.length).toEqual(3)
461+
expect(records[0].get(0)).toBe('Bob')
462+
expect(records[1].get(0)).toBe('Alice')
463+
expect(records[2].get(0)).toBe('Tina')
464+
465+
const connectionKey = Object.keys(driver._openConnections)[0]
466+
expect(connectionKey).toBeTruthy()
467+
468+
const connection = driver._openConnections[connectionKey]
469+
session.close(() => {
470+
// generate a fake fatal error
471+
connection._handleFatalError(
472+
newError('connection reset', SERVICE_UNAVAILABLE)
473+
)
474+
475+
// expect that the connection to be removed from the pool
476+
expect(driver._pool._pools['127.0.0.1:9001'].length).toEqual(0)
477+
expect(
478+
driver._pool._activeResourceCounts['127.0.0.1:9001']
479+
).toBeFalsy()
480+
// expect that the connection to be unregistered from the open connections registry
481+
expect(driver._openConnections[connectionKey]).toBeFalsy()
482+
driver.close()
483+
server.exit(code => {
484+
expect(code).toEqual(0)
485+
done()
486+
})
487+
})
488+
})
489+
.catch(error => done.fail(error))
490+
})
491+
})
492+
441493
describe('should fail if commit fails due to broken connection', () => {
442494
it('v1', done => {
443495
verifyFailureOnConnectionFailureWhenExplicitTransactionIsCommitted(

0 commit comments

Comments
 (0)