Skip to content

Commit 8737df8

Browse files
committed
Don't send RESET message when the connection is not open
Send RESET when the connection is not optimal since it was time waiting for the message send times out. It could be really fast in the nodejs environment, but it could take some minutes in browser. Skip send RESET when the connection is not open avoid this issue.
1 parent 02dd421 commit 8737df8

File tree

7 files changed

+235
-7
lines changed

7 files changed

+235
-7
lines changed

bolt-connection/src/connection/connection-channel.js

+8-1
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,10 @@ export default class ChannelConnection extends Connection {
270270
*/
271271
_handleFatalError (error) {
272272
this._isBroken = true
273-
this._error = this.handleAndTransformError(this._protocol.currentFailure || error, this._address)
273+
this._error = this.handleAndTransformError(
274+
this._protocol.currentFailure || error,
275+
this._address
276+
)
274277

275278
if (this._log.isErrorEnabled()) {
276279
this._log.error(
@@ -320,6 +323,10 @@ export default class ChannelConnection extends Connection {
320323
}
321324

322325
_resetOnFailure () {
326+
if (!this.isOpen()) {
327+
return
328+
}
329+
323330
this._protocol.reset({
324331
onError: () => {
325332
this._protocol.resetFailure()

bolt-connection/test/channel/browser/browser-channel.test.js

+32
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,38 @@ describe('WebSocketChannel', () => {
294294
}
295295
})
296296

297+
describe('.close()', () => {
298+
it('should set _open to false before resolve the promise', async () => {
299+
const fakeSetTimeout = setTimeoutMock.install()
300+
try {
301+
// do not execute setTimeout callbacks
302+
fakeSetTimeout.pause()
303+
const address = ServerAddress.fromUrl('bolt://localhost:8989')
304+
const driverConfig = { connectionTimeout: 4242 }
305+
const channelConfig = new ChannelConfig(
306+
address,
307+
driverConfig,
308+
SERVICE_UNAVAILABLE
309+
)
310+
webSocketChannel = new WebSocketChannel(
311+
channelConfig,
312+
undefined,
313+
createWebSocketFactory(WS_OPEN)
314+
)
315+
316+
expect(webSocketChannel._open).toBe(true)
317+
318+
const promise = webSocketChannel.close()
319+
320+
expect(webSocketChannel._open).toBe(false)
321+
322+
await promise
323+
} finally {
324+
fakeSetTimeout.uninstall()
325+
}
326+
})
327+
})
328+
297329
describe('.setupReceiveTimeout()', () => {
298330
beforeEach(() => {
299331
const address = ServerAddress.fromUrl('http://localhost:8989')

bolt-connection/test/channel/node/node-channel.test.js

+14
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,20 @@ describe('NodeChannel', () => {
4444
return expect(channel.close()).resolves.not.toThrow()
4545
})
4646

47+
describe('.close()', () => {
48+
it('should set _open to false before resolve the promise', async () => {
49+
const channel = createMockedChannel(true)
50+
51+
expect(channel._open).toBe(true)
52+
53+
const promise = channel.close()
54+
55+
expect(channel._open).toBe(false)
56+
57+
await promise
58+
})
59+
})
60+
4761
describe('.setupReceiveTimeout()', () => {
4862
it('should call socket.setTimeout(receiveTimeout)', () => {
4963
const receiveTimeout = 42

bolt-connection/test/connection/connection-channel.test.js

+63-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919

2020
import ChannelConnection from '../../src/connection/connection-channel'
2121
import { int, internal } from 'neo4j-driver-core'
22-
import { add } from 'lodash'
2322

2423
const {
2524
serverAddress: { ServerAddress },
@@ -127,6 +126,69 @@ describe('ChannelConnection', () => {
127126
)
128127
})
129128

129+
describe('._resetOnFailure()', () => {
130+
describe('when connection isOpen', () => {
131+
it('should call protocol.reset() and then protocol.resetFailure() onComplete', () => {
132+
const channel = {
133+
_open: true
134+
}
135+
136+
const protocol = {
137+
reset: jest.fn(observer => observer.onComplete()),
138+
resetFailure: jest.fn()
139+
}
140+
const protocolSupplier = () => protocol
141+
const connection = spyOnConnectionChannel({ channel, protocolSupplier })
142+
143+
connection._resetOnFailure()
144+
145+
expect(protocol.reset).toHaveBeenCalled()
146+
expect(protocol.resetFailure).toHaveBeenCalled()
147+
})
148+
149+
it('should call protocol.reset() and then protocol.resetFailure() onError', () => {
150+
const channel = {
151+
_open: true
152+
}
153+
154+
const protocol = {
155+
reset: jest.fn(observer => observer.onError()),
156+
resetFailure: jest.fn()
157+
}
158+
const protocolSupplier = () => protocol
159+
const connection = spyOnConnectionChannel({ channel, protocolSupplier })
160+
161+
connection._resetOnFailure()
162+
163+
expect(protocol.reset).toHaveBeenCalled()
164+
expect(protocol.resetFailure).toHaveBeenCalled()
165+
})
166+
})
167+
168+
describe('when connection is not open', () => {
169+
it('should not call protocol.reset() and protocol.resetFailure()', () => {
170+
const channel = {
171+
_open: false
172+
}
173+
174+
const protocol = {
175+
reset: jest.fn(observer => {
176+
observer.onComplete()
177+
observer.onError()
178+
}),
179+
resetFailure: jest.fn()
180+
}
181+
const protocolSupplier = () => protocol
182+
const connection = spyOnConnectionChannel({ channel, protocolSupplier })
183+
184+
connection._resetOnFailure()
185+
186+
expect(protocol.reset).not.toHaveBeenCalled()
187+
expect(protocol.resetFailure).not.toHaveBeenCalled()
188+
})
189+
})
190+
})
191+
130192
function spyOnConnectionChannel ({
131193
channel,
132194
errorHandler,

core/src/internal/connection-holder.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -179,10 +179,13 @@ class ConnectionHolder implements ConnectionHolderInterface {
179179
this._connectionPromise = this._connectionPromise
180180
.then((connection?: Connection) => {
181181
if (connection) {
182-
return connection
183-
.resetAndFlush()
184-
.catch(ignoreError)
185-
.then(() => connection._release())
182+
if (connection.isOpen()) {
183+
return connection
184+
.resetAndFlush()
185+
.catch(ignoreError)
186+
.then(() => connection._release())
187+
}
188+
return connection._release()
186189
} else {
187190
return Promise.resolve()
188191
}

test/internal/connection-holder.test.js

+110
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,116 @@ describe('#unit ConnectionHolder', () => {
292292

293293
expect(connectionHolder.database()).toBe('testdb')
294294
})
295+
296+
describe('.releaseConnection()', () => {
297+
describe('when the connection is initialized', () => {
298+
describe('and connection is open', () => {
299+
let connection
300+
301+
beforeEach(async () => {
302+
connection = new FakeConnection()
303+
const connectionProvider = newSingleConnectionProvider(connection)
304+
const connectionHolder = new ConnectionHolder({
305+
mode: READ,
306+
connectionProvider
307+
})
308+
309+
connectionHolder.initializeConnection()
310+
311+
await connectionHolder.releaseConnection()
312+
})
313+
314+
it('should call connection.resetAndFlush', () => {
315+
expect(connection.resetInvoked).toBe(1)
316+
})
317+
318+
it('should call connection._release()', () => {
319+
expect(connection.releaseInvoked).toBe(1)
320+
})
321+
})
322+
323+
describe('and connection is not open', () => {
324+
let connection
325+
326+
beforeEach(async () => {
327+
connection = new FakeConnection()
328+
connection._open = false
329+
const connectionProvider = newSingleConnectionProvider(connection)
330+
const connectionHolder = new ConnectionHolder({
331+
mode: READ,
332+
connectionProvider
333+
})
334+
335+
connectionHolder.initializeConnection()
336+
337+
await connectionHolder.releaseConnection()
338+
})
339+
340+
it('should not call connection.resetAndFlush', () => {
341+
expect(connection.resetInvoked).toBe(0)
342+
})
343+
344+
it('should call connection._release()', () => {
345+
expect(connection.releaseInvoked).toBe(1)
346+
})
347+
})
348+
})
349+
})
350+
351+
describe('.close()', () => {
352+
describe('when the connection is initialized', () => {
353+
describe('and connection is open', () => {
354+
let connection
355+
356+
beforeEach(async () => {
357+
connection = new FakeConnection()
358+
const connectionProvider = newSingleConnectionProvider(connection)
359+
const connectionHolder = new ConnectionHolder({
360+
mode: READ,
361+
connectionProvider
362+
})
363+
364+
connectionHolder.initializeConnection()
365+
366+
await connectionHolder.close()
367+
})
368+
369+
it('should call connection.resetAndFlush', () => {
370+
expect(connection.resetInvoked).toBe(1)
371+
})
372+
373+
it('should call connection._release()', () => {
374+
expect(connection.releaseInvoked).toBe(1)
375+
})
376+
})
377+
378+
describe('and connection is not open', () => {
379+
let connection
380+
381+
beforeEach(async () => {
382+
connection = new FakeConnection()
383+
connection._open = false
384+
const connectionProvider = newSingleConnectionProvider(connection)
385+
const connectionHolder = new ConnectionHolder({
386+
mode: READ,
387+
connectionProvider
388+
})
389+
390+
connectionHolder.initializeConnection()
391+
392+
await connectionHolder.close()
393+
})
394+
395+
it('should not call connection.resetAndFlush', () => {
396+
expect(connection.resetInvoked).toBe(0)
397+
})
398+
399+
it('should call connection._release()', () => {
400+
expect(connection.releaseInvoked).toBe(1)
401+
})
402+
})
403+
})
404+
})
295405
})
296406

297407
class RecordingConnectionProvider extends SingleConnectionProvider {

test/result.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ describe('#integration result stream', () => {
7373
done()
7474
},
7575
onError: error => {
76-
console.log(error)
76+
done.fail(error)
7777
}
7878
})
7979
})

0 commit comments

Comments
 (0)