Files
RoboCommander/node_modules/neo4j-driver/lib/v1/internal/connection-providers.js
2026-04-05 16:14:49 -04:00

379 lines
15 KiB
JavaScript

'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.SingleConnectionProvider = exports.LoadBalancer = exports.DirectConnectionProvider = undefined;
var _promise = require('babel-runtime/core-js/promise');
var _promise2 = _interopRequireDefault(_promise);
var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
var _inherits2 = require('babel-runtime/helpers/inherits');
var _inherits3 = _interopRequireDefault(_inherits2);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _error = require('../error');
var _driver = require('../driver');
var _session = require('../session');
var _session2 = _interopRequireDefault(_session);
var _routingTable = require('./routing-table');
var _routingTable2 = _interopRequireDefault(_routingTable);
var _rediscovery = require('./rediscovery');
var _rediscovery2 = _interopRequireDefault(_rediscovery);
var _features = require('./features');
var _features2 = _interopRequireDefault(_features);
var _hostNameResolvers = require('./host-name-resolvers');
var _routingUtil = require('./routing-util');
var _routingUtil2 = _interopRequireDefault(_routingUtil);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Copyright (c) 2002-2018 "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.
*/
var ConnectionProvider = function () {
function ConnectionProvider() {
(0, _classCallCheck3.default)(this, ConnectionProvider);
}
(0, _createClass3.default)(ConnectionProvider, [{
key: 'acquireConnection',
value: function acquireConnection(mode) {
throw new Error('Abstract function');
}
}, {
key: '_withAdditionalOnErrorCallback',
value: function _withAdditionalOnErrorCallback(connectionPromise, driverOnErrorCallback) {
// install error handler from the driver on the connection promise; this callback is installed separately
// so that it does not handle errors, instead it is just an additional error reporting facility.
connectionPromise.catch(function (error) {
driverOnErrorCallback(error);
});
// return the original connection promise
return connectionPromise;
}
}]);
return ConnectionProvider;
}();
var DirectConnectionProvider = exports.DirectConnectionProvider = function (_ConnectionProvider) {
(0, _inherits3.default)(DirectConnectionProvider, _ConnectionProvider);
function DirectConnectionProvider(hostPort, connectionPool, driverOnErrorCallback) {
(0, _classCallCheck3.default)(this, DirectConnectionProvider);
var _this = (0, _possibleConstructorReturn3.default)(this, (DirectConnectionProvider.__proto__ || (0, _getPrototypeOf2.default)(DirectConnectionProvider)).call(this));
_this._hostPort = hostPort;
_this._connectionPool = connectionPool;
_this._driverOnErrorCallback = driverOnErrorCallback;
return _this;
}
(0, _createClass3.default)(DirectConnectionProvider, [{
key: 'acquireConnection',
value: function acquireConnection(mode) {
var connectionPromise = this._connectionPool.acquire(this._hostPort);
return this._withAdditionalOnErrorCallback(connectionPromise, this._driverOnErrorCallback);
}
}]);
return DirectConnectionProvider;
}(ConnectionProvider);
var LoadBalancer = exports.LoadBalancer = function (_ConnectionProvider2) {
(0, _inherits3.default)(LoadBalancer, _ConnectionProvider2);
function LoadBalancer(hostPort, routingContext, connectionPool, loadBalancingStrategy, driverOnErrorCallback) {
(0, _classCallCheck3.default)(this, LoadBalancer);
var _this2 = (0, _possibleConstructorReturn3.default)(this, (LoadBalancer.__proto__ || (0, _getPrototypeOf2.default)(LoadBalancer)).call(this));
_this2._seedRouter = hostPort;
_this2._routingTable = new _routingTable2.default([_this2._seedRouter]);
_this2._rediscovery = new _rediscovery2.default(new _routingUtil2.default(routingContext));
_this2._connectionPool = connectionPool;
_this2._driverOnErrorCallback = driverOnErrorCallback;
_this2._hostNameResolver = LoadBalancer._createHostNameResolver();
_this2._loadBalancingStrategy = loadBalancingStrategy;
_this2._useSeedRouter = false;
return _this2;
}
(0, _createClass3.default)(LoadBalancer, [{
key: 'acquireConnection',
value: function acquireConnection(accessMode) {
var _this3 = this;
var connectionPromise = this._freshRoutingTable(accessMode).then(function (routingTable) {
if (accessMode === _driver.READ) {
var address = _this3._loadBalancingStrategy.selectReader(routingTable.readers);
return _this3._acquireConnectionToServer(address, 'read');
} else if (accessMode === _driver.WRITE) {
var _address = _this3._loadBalancingStrategy.selectWriter(routingTable.writers);
return _this3._acquireConnectionToServer(_address, 'write');
} else {
throw (0, _error.newError)('Illegal mode ' + accessMode);
}
});
return this._withAdditionalOnErrorCallback(connectionPromise, this._driverOnErrorCallback);
}
}, {
key: 'forget',
value: function forget(address) {
this._routingTable.forget(address);
this._connectionPool.purge(address);
}
}, {
key: 'forgetWriter',
value: function forgetWriter(address) {
this._routingTable.forgetWriter(address);
}
}, {
key: '_acquireConnectionToServer',
value: function _acquireConnectionToServer(address, serverName) {
if (!address) {
return _promise2.default.reject((0, _error.newError)('Failed to obtain connection towards ' + serverName + ' server. Known routing table is: ' + this._routingTable, _error.SESSION_EXPIRED));
}
return this._connectionPool.acquire(address);
}
}, {
key: '_freshRoutingTable',
value: function _freshRoutingTable(accessMode) {
var currentRoutingTable = this._routingTable;
if (!currentRoutingTable.isStaleFor(accessMode)) {
return _promise2.default.resolve(currentRoutingTable);
}
return this._refreshRoutingTable(currentRoutingTable);
}
}, {
key: '_refreshRoutingTable',
value: function _refreshRoutingTable(currentRoutingTable) {
var knownRouters = currentRoutingTable.routers;
if (this._useSeedRouter) {
return this._fetchRoutingTableFromSeedRouterFallbackToKnownRouters(knownRouters, currentRoutingTable);
}
return this._fetchRoutingTableFromKnownRoutersFallbackToSeedRouter(knownRouters, currentRoutingTable);
}
}, {
key: '_fetchRoutingTableFromSeedRouterFallbackToKnownRouters',
value: function _fetchRoutingTableFromSeedRouterFallbackToKnownRouters(knownRouters, currentRoutingTable) {
var _this4 = this;
// we start with seed router, no routers were probed before
var seenRouters = [];
return this._fetchRoutingTableUsingSeedRouter(seenRouters, this._seedRouter).then(function (newRoutingTable) {
if (newRoutingTable) {
_this4._useSeedRouter = false;
return newRoutingTable;
}
// seed router did not return a valid routing table - try to use other known routers
return _this4._fetchRoutingTableUsingKnownRouters(knownRouters, currentRoutingTable);
}).then(function (newRoutingTable) {
_this4._applyRoutingTableIfPossible(newRoutingTable);
return newRoutingTable;
});
}
}, {
key: '_fetchRoutingTableFromKnownRoutersFallbackToSeedRouter',
value: function _fetchRoutingTableFromKnownRoutersFallbackToSeedRouter(knownRouters, currentRoutingTable) {
var _this5 = this;
return this._fetchRoutingTableUsingKnownRouters(knownRouters, currentRoutingTable).then(function (newRoutingTable) {
if (newRoutingTable) {
return newRoutingTable;
}
// none of the known routers returned a valid routing table - try to use seed router address for rediscovery
return _this5._fetchRoutingTableUsingSeedRouter(knownRouters, _this5._seedRouter);
}).then(function (newRoutingTable) {
_this5._applyRoutingTableIfPossible(newRoutingTable);
return newRoutingTable;
});
}
}, {
key: '_fetchRoutingTableUsingKnownRouters',
value: function _fetchRoutingTableUsingKnownRouters(knownRouters, currentRoutingTable) {
return this._fetchRoutingTable(knownRouters, currentRoutingTable).then(function (newRoutingTable) {
if (newRoutingTable) {
// one of the known routers returned a valid routing table - use it
return newRoutingTable;
}
// returned routing table was undefined, this means a connection error happened and the last known
// router did not return a valid routing table, so we need to forget it
var lastRouterIndex = knownRouters.length - 1;
LoadBalancer._forgetRouter(currentRoutingTable, knownRouters, lastRouterIndex);
return null;
});
}
}, {
key: '_fetchRoutingTableUsingSeedRouter',
value: function _fetchRoutingTableUsingSeedRouter(seenRouters, seedRouter) {
var _this6 = this;
return this._hostNameResolver.resolve(seedRouter).then(function (resolvedRouterAddresses) {
// filter out all addresses that we've already tried
var newAddresses = resolvedRouterAddresses.filter(function (address) {
return seenRouters.indexOf(address) < 0;
});
return _this6._fetchRoutingTable(newAddresses, null);
});
}
}, {
key: '_fetchRoutingTable',
value: function _fetchRoutingTable(routerAddresses, routingTable) {
var _this7 = this;
return routerAddresses.reduce(function (refreshedTablePromise, currentRouter, currentIndex) {
return refreshedTablePromise.then(function (newRoutingTable) {
if (newRoutingTable) {
// valid routing table was fetched - just return it, try next router otherwise
return newRoutingTable;
} else {
// returned routing table was undefined, this means a connection error happened and we need to forget the
// previous router and try the next one
var previousRouterIndex = currentIndex - 1;
LoadBalancer._forgetRouter(routingTable, routerAddresses, previousRouterIndex);
}
// try next router
return _this7._createSessionForRediscovery(currentRouter).then(function (session) {
return _this7._rediscovery.lookupRoutingTableOnRouter(session, currentRouter);
});
});
}, _promise2.default.resolve(null));
}
}, {
key: '_createSessionForRediscovery',
value: function _createSessionForRediscovery(routerAddress) {
return this._connectionPool.acquire(routerAddress).then(function (connection) {
// initialized connection is required for routing procedure call
// server version needs to be known to decide which routing procedure to use
var initializedConnectionPromise = connection.initializationCompleted();
var connectionProvider = new SingleConnectionProvider(initializedConnectionPromise);
return new _session2.default(_driver.READ, connectionProvider);
});
}
}, {
key: '_applyRoutingTableIfPossible',
value: function _applyRoutingTableIfPossible(newRoutingTable) {
if (!newRoutingTable) {
// none of routing servers returned valid routing table, throw exception
throw (0, _error.newError)('Could not perform discovery. No routing servers available. Known routing table: ' + this._routingTable, _error.SERVICE_UNAVAILABLE);
}
if (newRoutingTable.writers.length === 0) {
// use seed router next time. this is important when cluster is partitioned. it tries to make sure driver
// does not always get routing table without writers because it talks exclusively to a minority partition
this._useSeedRouter = true;
}
this._updateRoutingTable(newRoutingTable);
}
}, {
key: '_updateRoutingTable',
value: function _updateRoutingTable(newRoutingTable) {
var _this8 = this;
var currentRoutingTable = this._routingTable;
// close old connections to servers not present in the new routing table
var staleServers = currentRoutingTable.serversDiff(newRoutingTable);
staleServers.forEach(function (server) {
return _this8._connectionPool.purge(server);
});
// make this driver instance aware of the new table
this._routingTable = newRoutingTable;
}
}], [{
key: '_forgetRouter',
value: function _forgetRouter(routingTable, routersArray, routerIndex) {
var address = routersArray[routerIndex];
if (routingTable && address) {
routingTable.forgetRouter(address);
}
}
}, {
key: '_createHostNameResolver',
value: function _createHostNameResolver() {
if ((0, _features2.default)('dns_lookup')) {
return new _hostNameResolvers.DnsHostNameResolver();
}
return new _hostNameResolvers.DummyHostNameResolver();
}
}]);
return LoadBalancer;
}(ConnectionProvider);
var SingleConnectionProvider = exports.SingleConnectionProvider = function (_ConnectionProvider3) {
(0, _inherits3.default)(SingleConnectionProvider, _ConnectionProvider3);
function SingleConnectionProvider(connectionPromise) {
(0, _classCallCheck3.default)(this, SingleConnectionProvider);
var _this9 = (0, _possibleConstructorReturn3.default)(this, (SingleConnectionProvider.__proto__ || (0, _getPrototypeOf2.default)(SingleConnectionProvider)).call(this));
_this9._connectionPromise = connectionPromise;
return _this9;
}
(0, _createClass3.default)(SingleConnectionProvider, [{
key: 'acquireConnection',
value: function acquireConnection(mode) {
var connectionPromise = this._connectionPromise;
this._connectionPromise = null;
return connectionPromise;
}
}]);
return SingleConnectionProvider;
}(ConnectionProvider);