Skip to content

Commit f55f62e

Browse files
authored
Merge pull request #266 from lutovich/1.4-pool-fixes
Fixed purging of all connections in a pool
2 parents 3387ab4 + 2ae01ca commit f55f62e

File tree

3 files changed

+206
-89
lines changed

3 files changed

+206
-89
lines changed

src/v1/internal/pool.js

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,7 @@ class Pool {
7070
}
7171

7272
purgeAll() {
73-
for (let key in this._pools.keys) {
74-
if (this._pools.hasOwnPropertykey) {
75-
this.purge(key);
76-
}
77-
}
73+
Object.keys(this._pools).forEach(key => this.purge(key));
7874
}
7975

8076
has(key) {
@@ -84,7 +80,8 @@ class Pool {
8480
_release(key, resource) {
8581
let pool = this._pools[key];
8682
if (!pool) {
87-
//key has been purged, don't put it back
83+
// key has been purged, don't put it back, just destroy the resource
84+
this._destroy(resource);
8885
return;
8986
}
9087
if( pool.length >= this._maxIdle || !this._validate(resource) ) {
@@ -96,4 +93,3 @@ class Pool {
9693
}
9794

9895
export default Pool
99-

test/internal/pool.test.js

Lines changed: 191 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -17,145 +17,256 @@
1717
* limitations under the License.
1818
*/
1919

20-
var Pool = require('../../lib/v1/internal/pool').default;
20+
import Pool from '../../src/v1/internal/pool';
2121

22-
describe('Pool', function() {
23-
it('allocates if pool is empty', function() {
22+
describe('Pool', () => {
23+
24+
it('allocates if pool is empty', () => {
2425
// Given
25-
var counter = 0;
26-
var key = "bolt://localhost:7687";
27-
var pool = new Pool( function (url, release) { return new Resource(url, counter++, release) } );
26+
let counter = 0;
27+
const key = 'bolt://localhost:7687';
28+
const pool = new Pool((url, release) => new Resource(url, counter++, release));
2829

2930
// When
30-
var r0 = pool.acquire(key);
31-
var r1 = pool.acquire(key);
31+
const r0 = pool.acquire(key);
32+
const r1 = pool.acquire(key);
3233

3334
// Then
34-
expect( r0.id ).toBe( 0 );
35-
expect( r1.id ).toBe( 1 );
35+
expect(r0.id).toBe(0);
36+
expect(r1.id).toBe(1);
37+
expect(r0).not.toBe(r1);
3638
});
3739

38-
it('pools if resources are returned', function() {
40+
it('pools if resources are returned', () => {
3941
// Given a pool that allocates
40-
var counter = 0;
41-
var key = "bolt://localhost:7687";
42-
var pool = new Pool( function (url, release) { return new Resource(url, counter++, release) } );
42+
let counter = 0;
43+
const key = 'bolt://localhost:7687';
44+
const pool = new Pool((url, release) => new Resource(url, counter++, release));
4345

4446
// When
45-
var r0 = pool.acquire(key);
47+
const r0 = pool.acquire(key);
4648
r0.close();
47-
var r1 = pool.acquire(key);
49+
const r1 = pool.acquire(key);
4850

4951
// Then
50-
expect( r0.id ).toBe( 0 );
51-
expect( r1.id ).toBe( 0 );
52+
expect(r0.id).toBe(0);
53+
expect(r1.id).toBe(0);
54+
expect(r0).toBe(r1);
5255
});
5356

54-
it('handles multiple keys', function() {
57+
it('handles multiple keys', () => {
5558
// Given a pool that allocates
56-
var counter = 0;
57-
var key1 = "bolt://localhost:7687";
58-
var key2 = "bolt://localhost:7688";
59-
var pool = new Pool( function (url, release) { return new Resource(url, counter++, release) } );
59+
let counter = 0;
60+
const key1 = 'bolt://localhost:7687';
61+
const key2 = 'bolt://localhost:7688';
62+
const pool = new Pool((url, release) => new Resource(url, counter++, release));
6063

6164
// When
62-
var r0 = pool.acquire(key1);
63-
var r1 = pool.acquire(key2);
65+
const r0 = pool.acquire(key1);
66+
const r1 = pool.acquire(key2);
6467
r0.close();
65-
var r2 = pool.acquire(key1);
66-
var r3 = pool.acquire(key2);
68+
const r2 = pool.acquire(key1);
69+
const r3 = pool.acquire(key2);
6770

6871
// Then
69-
expect( r0.id ).toBe( 0 );
70-
expect( r1.id ).toBe( 1 );
71-
expect( r2.id ).toBe( 0 );
72-
expect( r3.id ).toBe( 2 );
72+
expect(r0.id).toBe(0);
73+
expect(r1.id).toBe(1);
74+
expect(r2.id).toBe(0);
75+
expect(r3.id).toBe(2);
76+
77+
expect(r0).toBe(r2);
78+
expect(r1).not.toBe(r3);
7379
});
7480

75-
it('frees if pool reaches max size', function() {
81+
it('frees if pool reaches max size', () => {
7682
// Given a pool that tracks destroyed resources
77-
var counter = 0,
78-
destroyed = [];
79-
var key = "bolt://localhost:7687";
80-
var pool = new Pool(
81-
function (url, release) { return new Resource(url, counter++, release) },
82-
function (resource) { destroyed.push(resource); },
83-
function (resource) { return true; },
83+
let counter = 0;
84+
let destroyed = [];
85+
const key = 'bolt://localhost:7687';
86+
const pool = new Pool(
87+
(url, release) => new Resource(url, counter++, release),
88+
resource => {
89+
destroyed.push(resource);
90+
},
91+
resource => true,
8492
2 // maxIdle
8593
);
8694

8795
// When
88-
var r0 = pool.acquire(key);
89-
var r1 = pool.acquire(key);
90-
var r2 = pool.acquire(key);
96+
const r0 = pool.acquire(key);
97+
const r1 = pool.acquire(key);
98+
const r2 = pool.acquire(key);
9199
r0.close();
92100
r1.close();
93101
r2.close();
94102

95103
// Then
96-
expect( destroyed.length ).toBe( 1 );
97-
expect( destroyed[0].id ).toBe( r2.id );
104+
expect(destroyed.length).toBe(1);
105+
expect(destroyed[0].id).toBe(r2.id);
98106
});
99107

100-
it('frees if validate returns false', function() {
108+
it('frees if validate returns false', () => {
101109
// Given a pool that allocates
102-
var counter = 0,
103-
destroyed = [];
104-
var key = "bolt://localhost:7687";
105-
var pool = new Pool(
106-
function (url, release) { return new Resource(url, counter++, release) },
107-
function (resource) { destroyed.push(resource); },
108-
function (resource) { return false; },
110+
let counter = 0;
111+
let destroyed = [];
112+
const key = 'bolt://localhost:7687';
113+
const pool = new Pool(
114+
(url, release) => new Resource(url, counter++, release),
115+
resource => {
116+
destroyed.push(resource);
117+
},
118+
resource => false,
109119
1000 // maxIdle
110120
);
111121

112122
// When
113-
var r0 = pool.acquire(key);
114-
var r1 = pool.acquire(key);
123+
const r0 = pool.acquire(key);
124+
const r1 = pool.acquire(key);
115125
r0.close();
116126
r1.close();
117127

118128
// Then
119-
expect( destroyed.length ).toBe( 2 );
120-
expect( destroyed[0].id ).toBe( r0.id );
121-
expect( destroyed[1].id ).toBe( r1.id );
129+
expect(destroyed.length).toBe(2);
130+
expect(destroyed[0].id).toBe(r0.id);
131+
expect(destroyed[1].id).toBe(r1.id);
122132
});
123133

124134

125-
it('purges keys', function() {
135+
it('purges keys', () => {
126136
// Given a pool that allocates
127-
var counter = 0;
128-
var key1 = "bolt://localhost:7687";
129-
var key2 = "bolt://localhost:7688";
130-
var pool = new Pool( function (url, release) { return new Resource(url, counter++, release) },
131-
function (res) {res.destroyed = true; return true}
137+
let counter = 0;
138+
const key1 = 'bolt://localhost:7687';
139+
const key2 = 'bolt://localhost:7688';
140+
const pool = new Pool((url, release) => new Resource(url, counter++, release),
141+
res => {
142+
res.destroyed = true;
143+
return true;
144+
}
132145
);
133146

134147
// When
135-
var r0 = pool.acquire(key1);
136-
var r1 = pool.acquire(key2);
148+
const r0 = pool.acquire(key1);
149+
const r1 = pool.acquire(key2);
137150
r0.close();
138151
r1.close();
139-
expect(pool.has(key1)).toBe(true);
140-
expect(pool.has(key2)).toBe(true);
152+
expect(pool.has(key1)).toBeTruthy();
153+
expect(pool.has(key2)).toBeTruthy();
141154
pool.purge(key1);
142-
expect(pool.has(key1)).toBe(false);
143-
expect(pool.has(key2)).toBe(true);
155+
expect(pool.has(key1)).toBeFalsy();
156+
expect(pool.has(key2)).toBeTruthy();
144157

145-
var r2 = pool.acquire(key1);
146-
var r3 = pool.acquire(key2);
158+
const r2 = pool.acquire(key1);
159+
const r3 = pool.acquire(key2);
147160

148161
// Then
149-
expect( r0.id ).toBe( 0 );
150-
expect( r0.destroyed ).toBe( true );
151-
expect( r1.id ).toBe( 1 );
152-
expect( r2.id ).toBe( 2 );
153-
expect( r3.id ).toBe( 1 );
162+
expect(r0.id).toBe(0);
163+
expect(r0.destroyed).toBeTruthy();
164+
expect(r1.id).toBe(1);
165+
expect(r2.id).toBe(2);
166+
expect(r3.id).toBe(1);
167+
});
168+
169+
it('destroys resource when key was purged', () => {
170+
let counter = 0;
171+
const key = 'bolt://localhost:7687';
172+
const pool = new Pool((url, release) => new Resource(url, counter++, release),
173+
res => {
174+
res.destroyed = true;
175+
return true;
176+
}
177+
);
178+
179+
const r0 = pool.acquire(key);
180+
expect(pool.has(key)).toBeTruthy();
181+
expect(r0.id).toEqual(0);
182+
183+
pool.purge(key);
184+
expect(pool.has(key)).toBeFalsy();
185+
expect(r0.destroyed).toBeFalsy();
186+
187+
r0.close();
188+
expect(pool.has(key)).toBeFalsy();
189+
expect(r0.destroyed).toBeTruthy();
190+
});
191+
192+
it('purges all keys', () => {
193+
let counter = 0;
194+
195+
const key1 = 'bolt://localhost:7687';
196+
const key2 = 'bolt://localhost:7688';
197+
const key3 = 'bolt://localhost:7689';
198+
199+
const pool = new Pool((url, release) => new Resource(url, counter++, release),
200+
res => {
201+
res.destroyed = true;
202+
return true;
203+
}
204+
);
205+
206+
const acquiredResources = [
207+
pool.acquire(key1),
208+
pool.acquire(key2),
209+
pool.acquire(key3),
210+
pool.acquire(key1),
211+
pool.acquire(key2),
212+
pool.acquire(key3)
213+
];
214+
acquiredResources.forEach(resource => resource.close());
215+
216+
pool.purgeAll();
217+
218+
acquiredResources.forEach(resource => expect(resource.destroyed).toBeTruthy());
219+
});
220+
221+
it('skips broken connections during acquire', () => {
222+
let validated = false;
223+
let counter = 0;
224+
const key = 'bolt://localhost:7687';
225+
const pool = new Pool((url, release) => new Resource(url, counter++, release),
226+
res => {
227+
res.destroyed = true;
228+
return true;
229+
},
230+
() => {
231+
if (validated) {
232+
return false;
233+
}
234+
validated = true;
235+
return true;
236+
}
237+
);
238+
239+
const r0 = pool.acquire(key);
240+
r0.close();
241+
242+
const r1 = pool.acquire(key);
243+
expect(r1).not.toBe(r0);
244+
});
245+
246+
it('reports presence of the key', () => {
247+
const existingKey = 'bolt://localhost:7687';
248+
const absentKey = 'bolt://localhost:7688';
249+
250+
const pool = new Pool((url, release) => new Resource(url, 42, release));
251+
252+
pool.acquire(existingKey);
253+
pool.acquire(existingKey);
254+
255+
expect(pool.has(existingKey)).toBeTruthy();
256+
expect(pool.has(absentKey)).toBeFalsy();
154257
});
155258
});
156259

157-
function Resource( key, id, release) {
158-
var self = this;
159-
this.id = id;
160-
this.close = function() { release(key, self); };
260+
class Resource {
261+
262+
constructor(key, id, release) {
263+
this.id = id;
264+
this.key = key;
265+
this.release = release;
266+
this.destroyed = false;
267+
}
268+
269+
close() {
270+
this.release(this.key, this);
271+
}
161272
}

test/v1/routing.driver.boltkit.it.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -747,13 +747,19 @@ describe('routing driver', () => {
747747
const session1 = driver.session(neo4j.session.WRITE);
748748
session1.run("CREATE (n {name:'Bob'})").then(() => {
749749
session1.close(() => {
750-
const connections = Object.keys(driver._openSessions).length;
750+
const openConnectionsCount = numberOfOpenConnections(driver);
751751
const session2 = driver.session(neo4j.session.WRITE);
752752
session2.run("CREATE ()").then(() => {
753+
// driver should have same amount of open connections at this point;
754+
// no new connections should be created, existing connections should be reused
755+
expect(numberOfOpenConnections(driver)).toEqual(openConnectionsCount);
753756
driver.close();
757+
758+
// all connections should be closed when driver is closed
759+
expect(numberOfOpenConnections(driver)).toEqual(0);
760+
754761
seedServer.exit(code1 => {
755762
writeServer.exit(code2 => {
756-
expect(connections).toEqual(Object.keys(driver._openSessions).length);
757763
expect(code1).toEqual(0);
758764
expect(code2).toEqual(0);
759765
done();
@@ -2064,6 +2070,10 @@ describe('routing driver', () => {
20642070
return '[' + array.map(s => '"' + s + '"').join(',') + ']';
20652071
}
20662072

2073+
function numberOfOpenConnections(driver) {
2074+
return Object.keys(driver._openSessions).length;
2075+
}
2076+
20672077
class MemorizingRoutingTable extends RoutingTable {
20682078

20692079
constructor(initialTable) {

0 commit comments

Comments
 (0)