Skip to content

Commit fd001b5

Browse files
committed
Made pool expose active resource count
It will now track count of active (checked out of the pool) resources per key. Where key is a server address in the driver. Counter is incremented when resource is acquired from the pool and decremented when resource is released back to the pool. Also added JSDocs in the `Pool` class.
1 parent 963aeae commit fd001b5

File tree

3 files changed

+220
-13
lines changed

3 files changed

+220
-13
lines changed

src/v1/internal/pool.js

Lines changed: 71 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -36,59 +36,117 @@ class Pool {
3636
this._validate = validate;
3737
this._maxIdle = maxIdle;
3838
this._pools = {};
39+
this._activeResourceCounts = {};
3940
this._release = this._release.bind(this);
4041
}
4142

43+
/**
44+
* Acquire and idle resource fom the pool or create a new one.
45+
* @param {string} key the resource key.
46+
* @return {object} resource that is ready to use.
47+
*/
4248
acquire(key) {
43-
let resource;
4449
let pool = this._pools[key];
4550
if (!pool) {
4651
pool = [];
4752
this._pools[key] = pool;
4853
}
4954
while (pool.length) {
50-
resource = pool.pop();
55+
const resource = pool.pop();
5156

5257
if (this._validate(resource)) {
58+
// idle resource is valid and can be acquired
59+
resourceAcquired(key, this._activeResourceCounts);
5360
return resource;
5461
} else {
5562
this._destroy(resource);
5663
}
5764
}
5865

66+
// there exist no idle valid resources, create a new one for acquisition
67+
resourceAcquired(key, this._activeResourceCounts);
5968
return this._create(key, this._release);
6069
}
6170

71+
/**
72+
* Destroy all idle resources for the given key.
73+
* @param {string} key the resource key to purge.
74+
*/
6275
purge(key) {
63-
let resource;
64-
let pool = this._pools[key] || [];
76+
const pool = this._pools[key] || [];
6577
while (pool.length) {
66-
resource = pool.pop();
78+
const resource = pool.pop();
6779
this._destroy(resource)
6880
}
6981
delete this._pools[key]
7082
}
7183

84+
/**
85+
* Destroy all idle resources in this pool.
86+
*/
7287
purgeAll() {
7388
Object.keys(this._pools).forEach(key => this.purge(key));
7489
}
7590

91+
/**
92+
* Check if this pool contains resources for the given key.
93+
* @param {string} key the resource key to check.
94+
* @return {boolean} <code>true</code> when pool contains entries for the given key, <code>false</code> otherwise.
95+
*/
7696
has(key) {
7797
return (key in this._pools);
7898
}
7999

100+
/**
101+
* Get count of active (checked out of the pool) resources for the given key.
102+
* @param {string} key the resource key to check.
103+
* @return {number} count of resources acquired by clients.
104+
*/
105+
activeResourceCount(key) {
106+
return this._activeResourceCounts[key] || 0;
107+
}
108+
80109
_release(key, resource) {
81-
let pool = this._pools[key];
82-
if (!pool) {
110+
const pool = this._pools[key];
111+
112+
if (pool) {
113+
// there exist idle connections for the given key
114+
if (pool.length >= this._maxIdle || !this._validate(resource)) {
115+
this._destroy(resource);
116+
} else {
117+
pool.push(resource);
118+
}
119+
} else {
83120
// key has been purged, don't put it back, just destroy the resource
84121
this._destroy(resource);
85-
return;
86-
}
87-
if( pool.length >= this._maxIdle || !this._validate(resource) ) {
88-
this._destroy(resource);
89-
} else {
90-
pool.push(resource);
91122
}
123+
124+
resourceReleased(key, this._activeResourceCounts);
125+
}
126+
}
127+
128+
/**
129+
* Increment active (checked out of the pool) resource counter.
130+
* @param {string} key the resource group identifier (server address for connections).
131+
* @param {Object.<string, number>} activeResourceCounts the object holding active counts per key.
132+
*/
133+
function resourceAcquired(key, activeResourceCounts) {
134+
const currentCount = activeResourceCounts[key] || 0;
135+
activeResourceCounts[key] = currentCount + 1;
136+
}
137+
138+
/**
139+
* Decrement active (checked out of the pool) resource counter.
140+
* @param {string} key the resource group identifier (server address for connections).
141+
* @param {Object.<string, number>} activeResourceCounts the object holding active counts per key.
142+
*/
143+
function resourceReleased(key, activeResourceCounts) {
144+
const currentCount = activeResourceCounts[key] || 0;
145+
const nextCount = currentCount - 1;
146+
if (nextCount > 0) {
147+
activeResourceCounts[key] = nextCount;
148+
} else {
149+
delete activeResourceCounts[key];
92150
}
93151
}
94152

test/internal/pool.test.js

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,86 @@ describe('Pool', () => {
255255
expect(pool.has(existingKey)).toBeTruthy();
256256
expect(pool.has(absentKey)).toBeFalsy();
257257
});
258+
259+
it('reports zero active resources when empty', () => {
260+
const pool = new Pool((url, release) => new Resource(url, 42, release));
261+
262+
expect(pool.activeResourceCount('bolt://localhost:1')).toEqual(0);
263+
expect(pool.activeResourceCount('bolt://localhost:2')).toEqual(0);
264+
expect(pool.activeResourceCount('bolt://localhost:3')).toEqual(0);
265+
});
266+
267+
it('reports active resources', () => {
268+
const key = 'bolt://localhost:7687';
269+
const pool = new Pool((url, release) => new Resource(url, 42, release));
270+
271+
expect(pool.acquire(key)).toBeDefined();
272+
expect(pool.acquire(key)).toBeDefined();
273+
expect(pool.acquire(key)).toBeDefined();
274+
275+
expect(pool.activeResourceCount(key)).toEqual(3);
276+
});
277+
278+
it('reports active resources when they are created', () => {
279+
const key = 'bolt://localhost:7687';
280+
const pool = new Pool((url, release) => new Resource(url, 42, release));
281+
282+
// three new resources are created
283+
expect(pool.acquire(key)).toBeDefined();
284+
expect(pool.acquire(key)).toBeDefined();
285+
expect(pool.acquire(key)).toBeDefined();
286+
287+
expect(pool.activeResourceCount(key)).toEqual(3);
288+
});
289+
290+
it('reports active resources when they are acquired', () => {
291+
const key = 'bolt://localhost:7687';
292+
const pool = new Pool((url, release) => new Resource(url, 42, release));
293+
294+
// three new resources are created and returned to the pool
295+
const r0 = pool.acquire(key);
296+
const r1 = pool.acquire(key);
297+
const r2 = pool.acquire(key);
298+
r0.close();
299+
r1.close();
300+
r2.close();
301+
302+
// three idle resources are acquired from the pool
303+
const r3 = pool.acquire(key);
304+
const r4 = pool.acquire(key);
305+
const r5 = pool.acquire(key);
306+
expect(r3).toBe(r2);
307+
expect(r4).toBe(r1);
308+
expect(r5).toBe(r0);
309+
310+
expect(pool.activeResourceCount(key)).toEqual(3);
311+
});
312+
313+
it('does not report resources that are returned to the pool', () => {
314+
const key = 'bolt://localhost:7687';
315+
const pool = new Pool((url, release) => new Resource(url, 42, release));
316+
317+
const r0 = pool.acquire(key);
318+
const r1 = pool.acquire(key);
319+
const r2 = pool.acquire(key);
320+
expect(pool.activeResourceCount(key)).toEqual(3);
321+
322+
r0.close();
323+
expect(pool.activeResourceCount(key)).toEqual(2);
324+
325+
r1.close();
326+
expect(pool.activeResourceCount(key)).toEqual(1);
327+
328+
r2.close();
329+
expect(pool.activeResourceCount(key)).toEqual(0);
330+
331+
const r3 = pool.acquire(key);
332+
expect(pool.activeResourceCount(key)).toEqual(1);
333+
334+
r3.close();
335+
expect(pool.activeResourceCount(key)).toEqual(0);
336+
});
337+
258338
});
259339

260340
class Resource {

test/v1/session.test.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -965,6 +965,70 @@ describe('session', () => {
965965
});
966966
});
967967

968+
it('should acquire connection for transaction', done => {
969+
expect(session.beginTransaction()).toBeDefined();
970+
971+
const otherSession1 = driver.session();
972+
expect(otherSession1.beginTransaction()).toBeDefined();
973+
974+
const otherSession2 = driver.session();
975+
expect(otherSession2.beginTransaction()).toBeDefined();
976+
977+
const otherSession3 = driver.session();
978+
expect(otherSession3.beginTransaction()).toBeDefined();
979+
980+
expect(numberOfAcquiredConnectionsFromPool()).toEqual(4);
981+
982+
session.close(() => {
983+
otherSession1.close(() => {
984+
otherSession2.close(() => {
985+
otherSession3.close(() => {
986+
done();
987+
});
988+
});
989+
});
990+
});
991+
});
992+
993+
it('should acquire connection for query execution', done => {
994+
session.run('RETURN 42 AS answer').subscribe({
995+
onNext: record => {
996+
expect(record.get('answer').toInt()).toEqual(42);
997+
expect(numberOfAcquiredConnectionsFromPool()).toEqual(1);
998+
},
999+
onCompleted: () => {
1000+
session.close(() => {
1001+
done();
1002+
});
1003+
},
1004+
onError: error => {
1005+
console.log(error);
1006+
}
1007+
});
1008+
});
1009+
1010+
it('should acquire separate connections for transaction and query execution in different sessions', done => {
1011+
const otherSession = driver.session();
1012+
expect(otherSession.beginTransaction()).toBeDefined();
1013+
1014+
session.run('RETURN 42 AS answer').subscribe({
1015+
onNext: record => {
1016+
expect(record.get('answer').toInt()).toEqual(42);
1017+
expect(numberOfAcquiredConnectionsFromPool()).toEqual(2);
1018+
},
1019+
onCompleted: () => {
1020+
otherSession.close(() => {
1021+
session.close(() => {
1022+
done();
1023+
});
1024+
});
1025+
},
1026+
onError: error => {
1027+
console.log(error);
1028+
}
1029+
});
1030+
});
1031+
9681032
function serverIs31OrLater(done) {
9691033
// lazy way of checking the version number
9701034
// if server has been set we know it is at least 3.1
@@ -1043,4 +1107,9 @@ describe('session', () => {
10431107
});
10441108
}
10451109

1110+
function numberOfAcquiredConnectionsFromPool() {
1111+
const pool = driver._pool;
1112+
return pool.activeResourceCount('localhost');
1113+
}
1114+
10461115
});

0 commit comments

Comments
 (0)