Skip to content

Fix HELLO message RoutingContext and send bookmarks in the ROUTE message #702

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions bolt-connection/src/bolt/bolt-protocol-v4x0.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,14 +149,13 @@ export default class BoltProtocol extends BoltProtocolV3 {
routingContext = {},
databaseName = null,
sessionContext = {},
initialAddress = null,
onError,
onCompleted
}) {
const resultObserver = this.run(
CALL_GET_ROUTING_TABLE_MULTI_DB,
{
[CONTEXT]: { ...routingContext, address: initialAddress },
[CONTEXT]: routingContext,
[DATABASE]: databaseName
},
{ ...sessionContext, txConfig: TxConfig.empty() }
Expand Down
11 changes: 5 additions & 6 deletions bolt-connection/src/bolt/bolt-protocol-v4x3.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { RouteObserver } from './stream-observers'
import { internal } from 'neo4j-driver-core'

const {
bookmark: { Bookmark },
constants: { BOLT_PROTOCOL_V4_3 }
} = internal

Expand All @@ -38,14 +39,15 @@ export default class BoltProtocol extends BoltProtocolV42 {
* @param {object} param.routingContext The routing context used to define the routing table.
* Multi-datacenter deployments is one of its use cases
* @param {string} param.databaseName The database name
* @param {Bookmark} params.sessionContext.bookmark The bookmark used for request the routing table
* @param {function(err: Error)} param.onError
* @param {function(RawRoutingTable)} param.onCompleted
* @returns {RouteObserver} the route observer
*/
requestRoutingInformation ({
routingContext = {},
databaseName = null,
initialAddress = null,
sessionContext = {},
onError,
onCompleted
}) {
Expand All @@ -54,12 +56,9 @@ export default class BoltProtocol extends BoltProtocolV42 {
onError,
onCompleted
})

const bookmark = sessionContext.bookmark || Bookmark.empty()
this.write(
RequestMessage.route(
{ ...routingContext, address: initialAddress },
databaseName
),
RequestMessage.route(routingContext, bookmark.values(), databaseName),
observer,
true
)
Expand Down
12 changes: 8 additions & 4 deletions bolt-connection/src/bolt/request-message.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export default class RequestMessage {
*/
static hello (userAgent, authToken, routing = null) {
const metadata = Object.assign({ user_agent: userAgent }, authToken)
if (routing != null) {
if (routing) {
metadata.routing = routing
}
return new RequestMessage(
Expand Down Expand Up @@ -223,14 +223,18 @@ export default class RequestMessage {
* Generate the ROUTE message, this message is used to fetch the routing table from the server
*
* @param {object} routingContext The routing context used to define the routing table. Multi-datacenter deployments is one of its use cases
* @param {string[]} bookmarks The list of the bookmark should be used
* @param {string} databaseName The name of the database to get the routing table for.
* @return {RequestMessage} the ROUTE message.
*/
static route (routingContext = {}, databaseName = null) {
static route (routingContext = {}, bookmarks = [], databaseName = null) {
return new RequestMessage(
ROUTE,
[routingContext, databaseName],
() => `ROUTE ${json.stringify(routingContext)} ${databaseName}`
[routingContext, bookmarks, databaseName],
() =>
`ROUTE ${json.stringify(routingContext)} ${json.stringify(
bookmarks
)} ${databaseName}`
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,13 @@ export default class RoutingConnectionProvider extends PooledConnectionProvider
this._config,
this._createConnectionErrorHandler(),
this._log,
routingContext || {}
this._routingContext
)
})

this._routingContext = { ...routingContext, address: address.toString() }
this._seedRouter = address
this._rediscovery = new Rediscovery(routingContext, address.toString())
this._rediscovery = new Rediscovery(this._routingContext)
this._loadBalancingStrategy = new LeastConnectedLoadBalancingStrategy(
this._connectionPool
)
Expand Down
5 changes: 1 addition & 4 deletions bolt-connection/src/rediscovery/rediscovery.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,9 @@ export default class Rediscovery {
/**
* @constructor
* @param {object} routingContext
* @param {string} initialAddress
*/
constructor (routingContext, initialAddress) {
constructor (routingContext) {
this._routingContext = routingContext
this._initialAddress = initialAddress
}

/**
Expand Down Expand Up @@ -66,7 +64,6 @@ export default class Rediscovery {
return new Promise((resolve, reject) => {
connection.protocol().requestRoutingInformation({
routingContext: this._routingContext,
initialAddress: this._initialAddress,
databaseName: database,
sessionContext: {
bookmark: session._lastBookmark,
Expand Down
2 changes: 1 addition & 1 deletion bolt-connection/test/bolt/bolt-protocol-v4x0.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ describe('#unit BoltProtocolV4x0', () => {
expect(protocol._run[0]).toEqual([
'CALL dbms.routing.getRoutingTable($context, $database)',
{
context: { ...routingContext, address: initialAddress },
context: routingContext,
database: databaseName
},
{ ...sessionContext, txConfig: TxConfig.empty() }
Expand Down
26 changes: 25 additions & 1 deletion bolt-connection/test/bolt/bolt-protocol-v4x3.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,31 @@ describe('#unit BoltProtocolV4x3', () => {

protocol.verifyMessageCount(1)
expect(protocol.messages[0]).toBeMessage(
RequestMessage.route({ ...routingContext, address: null }, databaseName)
RequestMessage.route(routingContext, [], databaseName)
)
expect(protocol.observers).toEqual([observer])
expect(observer).toEqual(jasmine.any(RouteObserver))
expect(protocol.flushes).toEqual([true])
})

it('should request routing information sending bookmarks', () => {
const recorder = new utils.MessageRecordingConnection()
const protocol = new BoltProtocolV4x3(recorder, null, false)
utils.spyProtocolWrite(protocol)
const routingContext = { someContextParam: 'value' }
const listOfBookmarks = ['a', 'b', 'c']
const bookmark = new Bookmark(listOfBookmarks)
const databaseName = 'name'

const observer = protocol.requestRoutingInformation({
routingContext,
databaseName,
sessionContext: { bookmark }
})

protocol.verifyMessageCount(1)
expect(protocol.messages[0]).toBeMessage(
RequestMessage.route(routingContext, listOfBookmarks, databaseName)
)
expect(protocol.observers).toEqual([observer])
expect(observer).toEqual(jasmine.any(RouteObserver))
Expand Down
15 changes: 10 additions & 5 deletions bolt-connection/test/bolt/request-message.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -242,23 +242,28 @@ describe('#unit RequestMessage', () => {
describe('BoltV4.3', () => {
it('should create ROUTE message', () => {
const requestContext = { someValue: '1234' }
const bookmarks = ['a', 'b']
const database = 'user_db'

const message = RequestMessage.route(requestContext, database)
const message = RequestMessage.route(requestContext, bookmarks, database)

expect(message.signature).toEqual(0x66)
expect(message.fields).toEqual([requestContext, database])
expect(message.fields).toEqual([requestContext, bookmarks, database])
expect(message.toString()).toEqual(
`ROUTE ${json.stringify(requestContext)} ${database}`
`ROUTE ${json.stringify(requestContext)} ${json.stringify(
bookmarks
)} ${database}`
)
})

it('should create ROUTE message with default values', () => {
const message = RequestMessage.route()

expect(message.signature).toEqual(0x66)
expect(message.fields).toEqual([{}, null])
expect(message.toString()).toEqual(`ROUTE ${json.stringify({})} ${null}`)
expect(message.fields).toEqual([{}, [], null])
expect(message.toString()).toEqual(
`ROUTE ${json.stringify({})} ${json.stringify([])} ${null}`
)
})
})
})
143 changes: 143 additions & 0 deletions bolt-connection/test/fake-connection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/**
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import Connection from '../src/connection/connection'

/**
* This class is like a mock of {@link Connection} that tracks invocations count.
* It tries to maintain same "interface" as {@link Connection}.
* It could be replaced with a proper mock by a library like testdouble.
* At the time of writing such libraries require {@link Proxy} support but browser tests execute in
* PhantomJS which does not support proxies.
*/
export default class FakeConnection extends Connection {
constructor () {
super(null)

this._open = true
this._id = 0
this._databaseId = null
this._requestRoutingInformationMock = null
this.creationTimestamp = Date.now()

this.resetInvoked = 0
this.releaseInvoked = 0
this.seenQueries = []
this.seenParameters = []
this.seenProtocolOptions = []
this._server = {}
this.protocolVersion = undefined
this.protocolErrorsHandled = 0
this.seenProtocolErrors = []
this.seenRequestRoutingInformation = []
}

get id () {
return this._id
}

get databaseId () {
return this._databaseId
}

set databaseId (value) {
this._databaseId = value
}

get server () {
return this._server
}

get version () {
return this._server.version
}

set version (value) {
this._server.version = value
}

protocol () {
// return fake protocol object that simply records seen queries and parameters
return {
run: (query, parameters, protocolOptions) => {
this.seenQueries.push(query)
this.seenParameters.push(parameters)
this.seenProtocolOptions.push(protocolOptions)
},
requestRoutingInformation: params => {
this.seenRequestRoutingInformation.push(params)
if (this._requestRoutingInformationMock) {
this._requestRoutingInformationMock(params)
}
},
version: this.protocolVersion
}
}

resetAndFlush () {
this.resetInvoked++
return Promise.resolve()
}

_release () {
this.releaseInvoked++
return Promise.resolve()
}

isOpen () {
return this._open
}

isNeverReleased () {
return this.isReleasedTimes(0)
}

isReleasedOnce () {
return this.isReleasedTimes(1)
}

isReleasedTimes (times) {
return this.resetInvoked === times && this.releaseInvoked === times
}

_handleProtocolError (message) {
this.protocolErrorsHandled++
this.seenProtocolErrors.push(message)
}

withProtocolVersion (version) {
this.protocolVersion = version
return this
}

withCreationTimestamp (value) {
this.creationTimestamp = value
return this
}

withRequestRoutingInformationMock (requestRoutingInformationMock) {
this._requestRoutingInformationMock = requestRoutingInformationMock
return this
}

closed () {
this._open = false
return this
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
* limitations under the License.
*/

import { RawRoutingTable } from '../../bolt-connection/lib/bolt'
import Rediscovery from '../../bolt-connection/lib/rediscovery'
import RoutingTable from '../../bolt-connection/lib/rediscovery/routing-table'
import FakeConnection from './fake-connection'
import { RawRoutingTable } from '../../src/bolt'
import Rediscovery from '../../src/rediscovery'
import RoutingTable from '../../src/rediscovery/routing-table'
import FakeConnection from '../fake-connection'
import lolex from 'lolex'
import { newError, error, int, internal } from 'neo4j-driver-core'

Expand Down Expand Up @@ -106,7 +106,6 @@ describe('#unit Rediscovery', () => {
const requestParams = connection.seenRequestRoutingInformation[0]
expect(requestParams.routingContext).toEqual(routingContext)
expect(requestParams.databaseName).toEqual(database)
expect(requestParams.initialAddress).toEqual(initialAddress)
expect(requestParams.sessionContext).toEqual({
bookmark: session._lastBookmark,
mode: session._mode,
Expand Down
1 change: 0 additions & 1 deletion test/internal/fake-connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
*/

import Connection from '../../bolt-connection/lib/connection/connection'
import { textChangeRangeIsUnchanged } from 'typescript'
import {
ServerVersion,
VERSION_3_4_0,
Expand Down
4 changes: 4 additions & 0 deletions testkit-backend/src/skipped-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ const skippedTests = [
'Routing tests are disabled until the fix on the test scenario be merged',
or(ifStartsWith('stub.routing'), ifStartsWith('stub.retry.TestRetry'))
),
skip(
'The driver no support domain_name_resolver',
ifEndsWith('test_should_successfully_acquire_rt_when_router_ip_changes')
),
skip(
'Driver is failing trying to update the routing table using the original routing server',
ifEndsWith(
Expand Down