@@ -24,43 +24,17 @@ import RoutingTable from './routing-table'
24
24
import Rediscovery from './rediscovery'
25
25
import RoutingUtil from './routing-util'
26
26
import { HostNameResolver } from './node'
27
+ import ConnectionProvider from './connection-provider'
28
+ import SingleConnectionProvider from './connection-provider-single'
29
+ import { VERSION_4_0_0 } from './server-version'
27
30
28
31
const UNAUTHORIZED_ERROR_CODE = 'Neo.ClientError.Security.Unauthorized'
32
+ const DATABASE_NOT_FOUND_ERROR_CODE =
33
+ 'Neo.ClientError.Database.DatabaseNotFound'
34
+ const SYSTEM_DB_NAME = 'system'
35
+ const DEFAULT_DB_NAME = ''
29
36
30
- class ConnectionProvider {
31
- acquireConnection ( accessMode , database ) {
32
- throw new Error ( 'Abstract function' )
33
- }
34
-
35
- _withAdditionalOnErrorCallback ( connectionPromise , driverOnErrorCallback ) {
36
- // install error handler from the driver on the connection promise; this callback is installed separately
37
- // so that it does not handle errors, instead it is just an additional error reporting facility.
38
- connectionPromise . catch ( error => {
39
- driverOnErrorCallback ( error )
40
- } )
41
- // return the original connection promise
42
- return connectionPromise
43
- }
44
- }
45
-
46
- export class DirectConnectionProvider extends ConnectionProvider {
47
- constructor ( address , connectionPool , driverOnErrorCallback ) {
48
- super ( )
49
- this . _address = address
50
- this . _connectionPool = connectionPool
51
- this . _driverOnErrorCallback = driverOnErrorCallback
52
- }
53
-
54
- acquireConnection ( accessMode , database ) {
55
- const connectionPromise = this . _connectionPool . acquire ( this . _address )
56
- return this . _withAdditionalOnErrorCallback (
57
- connectionPromise ,
58
- this . _driverOnErrorCallback
59
- )
60
- }
61
- }
62
-
63
- export class LoadBalancer extends ConnectionProvider {
37
+ export default class RoutingConnectionProvider extends ConnectionProvider {
64
38
constructor (
65
39
address ,
66
40
routingContext ,
@@ -72,7 +46,7 @@ export class LoadBalancer extends ConnectionProvider {
72
46
) {
73
47
super ( )
74
48
this . _seedRouter = address
75
- this . _routingTable = new RoutingTable ( )
49
+ this . _routingTables = { }
76
50
this . _rediscovery = new Rediscovery ( new RoutingUtil ( routingContext ) )
77
51
this . _connectionPool = connectionPool
78
52
this . _driverOnErrorCallback = driverOnErrorCallback
@@ -84,60 +58,64 @@ export class LoadBalancer extends ConnectionProvider {
84
58
}
85
59
86
60
acquireConnection ( accessMode , database ) {
87
- const connectionPromise = this . _freshRoutingTable ( accessMode ) . then (
88
- routingTable => {
89
- if ( accessMode === READ ) {
90
- const address = this . _loadBalancingStrategy . selectReader (
91
- routingTable . readers
92
- )
93
- return this . _acquireConnectionToServer ( address , 'read' )
94
- } else if ( accessMode === WRITE ) {
95
- const address = this . _loadBalancingStrategy . selectWriter (
96
- routingTable . writers
97
- )
98
- return this . _acquireConnectionToServer ( address , 'write' )
99
- } else {
100
- throw newError ( 'Illegal mode ' + accessMode )
101
- }
61
+ const connectionPromise = this . _freshRoutingTable (
62
+ accessMode ,
63
+ database || DEFAULT_DB_NAME
64
+ ) . then ( routingTable => {
65
+ if ( accessMode === READ ) {
66
+ const address = this . _loadBalancingStrategy . selectReader (
67
+ routingTable . readers
68
+ )
69
+ return this . _acquireConnectionToServer ( address , 'read' , routingTable )
70
+ } else if ( accessMode === WRITE ) {
71
+ const address = this . _loadBalancingStrategy . selectWriter (
72
+ routingTable . writers
73
+ )
74
+ return this . _acquireConnectionToServer ( address , 'write' , routingTable )
75
+ } else {
76
+ throw newError ( 'Illegal mode ' + accessMode )
102
77
}
103
- )
78
+ } )
104
79
return this . _withAdditionalOnErrorCallback (
105
80
connectionPromise ,
106
81
this . _driverOnErrorCallback
107
82
)
108
83
}
109
84
110
85
forget ( address ) {
111
- this . _routingTable . forget ( address )
86
+ Object . values ( this . _routingTables ) . forEach ( routingTable =>
87
+ routingTable . forget ( address )
88
+ )
112
89
this . _connectionPool . purge ( address )
113
90
}
114
91
115
92
forgetWriter ( address ) {
116
- this . _routingTable . forgetWriter ( address )
93
+ Object . values ( this . _routingTables ) . forEach ( routingTable =>
94
+ routingTable . forgetWriter ( address )
95
+ )
117
96
}
118
97
119
- _acquireConnectionToServer ( address , serverName ) {
98
+ _acquireConnectionToServer ( address , serverName , routingTable ) {
120
99
if ( ! address ) {
121
100
return Promise . reject (
122
101
newError (
123
- `Failed to obtain connection towards ${ serverName } server. Known routing table is: ${
124
- this . _routingTable
125
- } `,
102
+ `Failed to obtain connection towards ${ serverName } server. Known routing table is: ${ routingTable } ` ,
126
103
SESSION_EXPIRED
127
104
)
128
105
)
129
106
}
130
107
return this . _connectionPool . acquire ( address )
131
108
}
132
109
133
- _freshRoutingTable ( accessMode ) {
134
- const currentRoutingTable = this . _routingTable
110
+ _freshRoutingTable ( accessMode , database ) {
111
+ const currentRoutingTable =
112
+ this . _routingTables [ database ] || new RoutingTable ( { database } )
135
113
136
114
if ( ! currentRoutingTable . isStaleFor ( accessMode ) ) {
137
115
return Promise . resolve ( currentRoutingTable )
138
116
}
139
117
this . _log . info (
140
- `Routing table is stale for ${ accessMode } : ${ currentRoutingTable } `
118
+ `Routing table is stale for database: " ${ database } " and access mode: " ${ accessMode } " : ${ currentRoutingTable } `
141
119
)
142
120
return this . _refreshRoutingTable ( currentRoutingTable )
143
121
}
@@ -163,7 +141,11 @@ export class LoadBalancer extends ConnectionProvider {
163
141
) {
164
142
// we start with seed router, no routers were probed before
165
143
const seenRouters = [ ]
166
- return this . _fetchRoutingTableUsingSeedRouter ( seenRouters , this . _seedRouter )
144
+ return this . _fetchRoutingTableUsingSeedRouter (
145
+ seenRouters ,
146
+ this . _seedRouter ,
147
+ currentRoutingTable
148
+ )
167
149
. then ( newRoutingTable => {
168
150
if ( newRoutingTable ) {
169
151
this . _useSeedRouter = false
@@ -177,7 +159,7 @@ export class LoadBalancer extends ConnectionProvider {
177
159
)
178
160
} )
179
161
. then ( newRoutingTable => {
180
- this . _applyRoutingTableIfPossible ( newRoutingTable )
162
+ this . _applyRoutingTableIfPossible ( currentRoutingTable , newRoutingTable )
181
163
return newRoutingTable
182
164
} )
183
165
}
@@ -198,11 +180,12 @@ export class LoadBalancer extends ConnectionProvider {
198
180
// none of the known routers returned a valid routing table - try to use seed router address for rediscovery
199
181
return this . _fetchRoutingTableUsingSeedRouter (
200
182
knownRouters ,
201
- this . _seedRouter
183
+ this . _seedRouter ,
184
+ currentRoutingTable
202
185
)
203
186
} )
204
187
. then ( newRoutingTable => {
205
- this . _applyRoutingTableIfPossible ( newRoutingTable )
188
+ this . _applyRoutingTableIfPossible ( currentRoutingTable , newRoutingTable )
206
189
return newRoutingTable
207
190
} )
208
191
}
@@ -218,7 +201,7 @@ export class LoadBalancer extends ConnectionProvider {
218
201
// returned routing table was undefined, this means a connection error happened and the last known
219
202
// router did not return a valid routing table, so we need to forget it
220
203
const lastRouterIndex = knownRouters . length - 1
221
- LoadBalancer . _forgetRouter (
204
+ RoutingConnectionProvider . _forgetRouter (
222
205
currentRoutingTable ,
223
206
knownRouters ,
224
207
lastRouterIndex
@@ -229,14 +212,14 @@ export class LoadBalancer extends ConnectionProvider {
229
212
)
230
213
}
231
214
232
- _fetchRoutingTableUsingSeedRouter ( seenRouters , seedRouter ) {
233
- const resolvedAddresses = this . _hostNameResolver . resolve ( seedRouter )
215
+ _fetchRoutingTableUsingSeedRouter ( seenRouters , seedRouter , routingTable ) {
216
+ const resolvedAddresses = this . _resolveSeedRouter ( seedRouter )
234
217
return resolvedAddresses . then ( resolvedRouterAddresses => {
235
218
// filter out all addresses that we've already tried
236
219
const newAddresses = resolvedRouterAddresses . filter (
237
220
address => seenRouters . indexOf ( address ) < 0
238
221
)
239
- return this . _fetchRoutingTable ( newAddresses , null )
222
+ return this . _fetchRoutingTable ( newAddresses , routingTable )
240
223
} )
241
224
}
242
225
@@ -265,7 +248,7 @@ export class LoadBalancer extends ConnectionProvider {
265
248
// returned routing table was undefined, this means a connection error happened and we need to forget the
266
249
// previous router and try the next one
267
250
const previousRouterIndex = currentIndex - 1
268
- LoadBalancer . _forgetRouter (
251
+ RoutingConnectionProvider . _forgetRouter (
269
252
routingTable ,
270
253
routerAddresses ,
271
254
previousRouterIndex
@@ -277,8 +260,16 @@ export class LoadBalancer extends ConnectionProvider {
277
260
session => {
278
261
if ( session ) {
279
262
return this . _rediscovery
280
- . lookupRoutingTableOnRouter ( session , currentRouter )
263
+ . lookupRoutingTableOnRouter (
264
+ session ,
265
+ routingTable . database ,
266
+ currentRouter
267
+ )
281
268
. catch ( error => {
269
+ if ( error && error . code === DATABASE_NOT_FOUND_ERROR_CODE ) {
270
+ // not finding the target database is a sign of a configuration issue
271
+ throw error
272
+ }
282
273
this . _log . warn (
283
274
`unable to fetch routing table because of an error ${ error } `
284
275
)
@@ -302,25 +293,37 @@ export class LoadBalancer extends ConnectionProvider {
302
293
. acquire ( routerAddress )
303
294
. then ( connection => {
304
295
const connectionProvider = new SingleConnectionProvider ( connection )
305
- return new Session ( { mode : READ , connectionProvider } )
296
+
297
+ if ( connection . version ( ) . compareTo ( VERSION_4_0_0 ) < 0 ) {
298
+ return new Session ( { mode : READ , connectionProvider } )
299
+ }
300
+
301
+ return new Session ( {
302
+ mode : READ ,
303
+ database : SYSTEM_DB_NAME ,
304
+ connectionProvider
305
+ } )
306
306
} )
307
307
. catch ( error => {
308
308
// unable to acquire connection towards the given router
309
- if ( error && error . code === UNAUTHORIZED_ERROR_CODE ) {
310
- // auth error is a sign of a configuration issue, rediscovery should not proceed
309
+ if (
310
+ error &&
311
+ ( error . code === UNAUTHORIZED_ERROR_CODE ||
312
+ error . code === DATABASE_NOT_FOUND_ERROR_CODE )
313
+ ) {
314
+ // auth error and not finding system database is a sign of a configuration issue
315
+ // discovery should not proceed
311
316
throw error
312
317
}
313
318
return null
314
319
} )
315
320
}
316
321
317
- _applyRoutingTableIfPossible ( newRoutingTable ) {
322
+ _applyRoutingTableIfPossible ( currentRoutingTable , newRoutingTable ) {
318
323
if ( ! newRoutingTable ) {
319
324
// none of routing servers returned valid routing table, throw exception
320
325
throw newError (
321
- `Could not perform discovery. No routing servers available. Known routing table: ${
322
- this . _routingTable
323
- } `,
326
+ `Could not perform discovery. No routing servers available. Known routing table: ${ currentRoutingTable } ` ,
324
327
SERVICE_UNAVAILABLE
325
328
)
326
329
}
@@ -339,7 +342,7 @@ export class LoadBalancer extends ConnectionProvider {
339
342
this . _connectionPool . keepAll ( newRoutingTable . allServers ( ) )
340
343
341
344
// make this driver instance aware of the new table
342
- this . _routingTable = newRoutingTable
345
+ this . _routingTables [ newRoutingTable . database ] = newRoutingTable
343
346
this . _log . info ( `Updated routing table ${ newRoutingTable } ` )
344
347
}
345
348
@@ -350,16 +353,3 @@ export class LoadBalancer extends ConnectionProvider {
350
353
}
351
354
}
352
355
}
353
-
354
- export class SingleConnectionProvider extends ConnectionProvider {
355
- constructor ( connection ) {
356
- super ( )
357
- this . _connection = connection
358
- }
359
-
360
- acquireConnection ( mode , database ) {
361
- const connection = this . _connection
362
- this . _connection = null
363
- return Promise . resolve ( connection )
364
- }
365
- }
0 commit comments