You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					189 lines
				
				7.6 KiB
			
		
		
			
		
	
	
					189 lines
				
				7.6 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								"use strict";
							 | 
						||
| 
								 | 
							
								Object.defineProperty(exports, "__esModule", { value: true });
							 | 
						||
| 
								 | 
							
								exports.compareTopologyVersion = exports.parseServerType = exports.ServerDescription = void 0;
							 | 
						||
| 
								 | 
							
								const bson_1 = require("../bson");
							 | 
						||
| 
								 | 
							
								const error_1 = require("../error");
							 | 
						||
| 
								 | 
							
								const utils_1 = require("../utils");
							 | 
						||
| 
								 | 
							
								const common_1 = require("./common");
							 | 
						||
| 
								 | 
							
								const WRITABLE_SERVER_TYPES = new Set([
							 | 
						||
| 
								 | 
							
								    common_1.ServerType.RSPrimary,
							 | 
						||
| 
								 | 
							
								    common_1.ServerType.Standalone,
							 | 
						||
| 
								 | 
							
								    common_1.ServerType.Mongos,
							 | 
						||
| 
								 | 
							
								    common_1.ServerType.LoadBalancer
							 | 
						||
| 
								 | 
							
								]);
							 | 
						||
| 
								 | 
							
								const DATA_BEARING_SERVER_TYPES = new Set([
							 | 
						||
| 
								 | 
							
								    common_1.ServerType.RSPrimary,
							 | 
						||
| 
								 | 
							
								    common_1.ServerType.RSSecondary,
							 | 
						||
| 
								 | 
							
								    common_1.ServerType.Mongos,
							 | 
						||
| 
								 | 
							
								    common_1.ServerType.Standalone,
							 | 
						||
| 
								 | 
							
								    common_1.ServerType.LoadBalancer
							 | 
						||
| 
								 | 
							
								]);
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * The client's view of a single server, based on the most recent hello outcome.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * Internal type, not meant to be directly instantiated
							 | 
						||
| 
								 | 
							
								 * @public
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								class ServerDescription {
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Create a ServerDescription
							 | 
						||
| 
								 | 
							
								     * @internal
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @param address - The address of the server
							 | 
						||
| 
								 | 
							
								     * @param hello - An optional hello response for this server
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    constructor(address, hello, options = {}) {
							 | 
						||
| 
								 | 
							
								        if (address == null || address === '') {
							 | 
						||
| 
								 | 
							
								            throw new error_1.MongoRuntimeError('ServerDescription must be provided with a non-empty address');
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        this.address =
							 | 
						||
| 
								 | 
							
								            typeof address === 'string'
							 | 
						||
| 
								 | 
							
								                ? utils_1.HostAddress.fromString(address).toString() // Use HostAddress to normalize
							 | 
						||
| 
								 | 
							
								                : address.toString();
							 | 
						||
| 
								 | 
							
								        this.type = parseServerType(hello, options);
							 | 
						||
| 
								 | 
							
								        this.hosts = hello?.hosts?.map((host) => host.toLowerCase()) ?? [];
							 | 
						||
| 
								 | 
							
								        this.passives = hello?.passives?.map((host) => host.toLowerCase()) ?? [];
							 | 
						||
| 
								 | 
							
								        this.arbiters = hello?.arbiters?.map((host) => host.toLowerCase()) ?? [];
							 | 
						||
| 
								 | 
							
								        this.tags = hello?.tags ?? {};
							 | 
						||
| 
								 | 
							
								        this.minWireVersion = hello?.minWireVersion ?? 0;
							 | 
						||
| 
								 | 
							
								        this.maxWireVersion = hello?.maxWireVersion ?? 0;
							 | 
						||
| 
								 | 
							
								        this.roundTripTime = options?.roundTripTime ?? -1;
							 | 
						||
| 
								 | 
							
								        this.lastUpdateTime = (0, utils_1.now)();
							 | 
						||
| 
								 | 
							
								        this.lastWriteDate = hello?.lastWrite?.lastWriteDate ?? 0;
							 | 
						||
| 
								 | 
							
								        this.error = options.error ?? null;
							 | 
						||
| 
								 | 
							
								        // TODO(NODE-2674): Preserve int64 sent from MongoDB
							 | 
						||
| 
								 | 
							
								        this.topologyVersion = this.error?.topologyVersion ?? hello?.topologyVersion ?? null;
							 | 
						||
| 
								 | 
							
								        this.setName = hello?.setName ?? null;
							 | 
						||
| 
								 | 
							
								        this.setVersion = hello?.setVersion ?? null;
							 | 
						||
| 
								 | 
							
								        this.electionId = hello?.electionId ?? null;
							 | 
						||
| 
								 | 
							
								        this.logicalSessionTimeoutMinutes = hello?.logicalSessionTimeoutMinutes ?? null;
							 | 
						||
| 
								 | 
							
								        this.primary = hello?.primary ?? null;
							 | 
						||
| 
								 | 
							
								        this.me = hello?.me?.toLowerCase() ?? null;
							 | 
						||
| 
								 | 
							
								        this.$clusterTime = hello?.$clusterTime ?? null;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    get hostAddress() {
							 | 
						||
| 
								 | 
							
								        return utils_1.HostAddress.fromString(this.address);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    get allHosts() {
							 | 
						||
| 
								 | 
							
								        return this.hosts.concat(this.arbiters).concat(this.passives);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    /** Is this server available for reads*/
							 | 
						||
| 
								 | 
							
								    get isReadable() {
							 | 
						||
| 
								 | 
							
								        return this.type === common_1.ServerType.RSSecondary || this.isWritable;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    /** Is this server data bearing */
							 | 
						||
| 
								 | 
							
								    get isDataBearing() {
							 | 
						||
| 
								 | 
							
								        return DATA_BEARING_SERVER_TYPES.has(this.type);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    /** Is this server available for writes */
							 | 
						||
| 
								 | 
							
								    get isWritable() {
							 | 
						||
| 
								 | 
							
								        return WRITABLE_SERVER_TYPES.has(this.type);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    get host() {
							 | 
						||
| 
								 | 
							
								        const chopLength = `:${this.port}`.length;
							 | 
						||
| 
								 | 
							
								        return this.address.slice(0, -chopLength);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    get port() {
							 | 
						||
| 
								 | 
							
								        const port = this.address.split(':').pop();
							 | 
						||
| 
								 | 
							
								        return port ? Number.parseInt(port, 10) : 27017;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Determines if another `ServerDescription` is equal to this one per the rules defined
							 | 
						||
| 
								 | 
							
								     * in the {@link https://github.com/mongodb/specifications/blob/master/source/server-discovery-and-monitoring/server-discovery-and-monitoring.rst#serverdescription|SDAM spec}
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    equals(other) {
							 | 
						||
| 
								 | 
							
								        // Despite using the comparator that would determine a nullish topologyVersion as greater than
							 | 
						||
| 
								 | 
							
								        // for equality we should only always perform direct equality comparison
							 | 
						||
| 
								 | 
							
								        const topologyVersionsEqual = this.topologyVersion === other?.topologyVersion ||
							 | 
						||
| 
								 | 
							
								            compareTopologyVersion(this.topologyVersion, other?.topologyVersion) === 0;
							 | 
						||
| 
								 | 
							
								        const electionIdsEqual = this.electionId != null && other?.electionId != null
							 | 
						||
| 
								 | 
							
								            ? (0, utils_1.compareObjectId)(this.electionId, other.electionId) === 0
							 | 
						||
| 
								 | 
							
								            : this.electionId === other?.electionId;
							 | 
						||
| 
								 | 
							
								        return (other != null &&
							 | 
						||
| 
								 | 
							
								            (0, utils_1.errorStrictEqual)(this.error, other.error) &&
							 | 
						||
| 
								 | 
							
								            this.type === other.type &&
							 | 
						||
| 
								 | 
							
								            this.minWireVersion === other.minWireVersion &&
							 | 
						||
| 
								 | 
							
								            (0, utils_1.arrayStrictEqual)(this.hosts, other.hosts) &&
							 | 
						||
| 
								 | 
							
								            tagsStrictEqual(this.tags, other.tags) &&
							 | 
						||
| 
								 | 
							
								            this.setName === other.setName &&
							 | 
						||
| 
								 | 
							
								            this.setVersion === other.setVersion &&
							 | 
						||
| 
								 | 
							
								            electionIdsEqual &&
							 | 
						||
| 
								 | 
							
								            this.primary === other.primary &&
							 | 
						||
| 
								 | 
							
								            this.logicalSessionTimeoutMinutes === other.logicalSessionTimeoutMinutes &&
							 | 
						||
| 
								 | 
							
								            topologyVersionsEqual);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								exports.ServerDescription = ServerDescription;
							 | 
						||
| 
								 | 
							
								// Parses a `hello` message and determines the server type
							 | 
						||
| 
								 | 
							
								function parseServerType(hello, options) {
							 | 
						||
| 
								 | 
							
								    if (options?.loadBalanced) {
							 | 
						||
| 
								 | 
							
								        return common_1.ServerType.LoadBalancer;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (!hello || !hello.ok) {
							 | 
						||
| 
								 | 
							
								        return common_1.ServerType.Unknown;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (hello.isreplicaset) {
							 | 
						||
| 
								 | 
							
								        return common_1.ServerType.RSGhost;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (hello.msg && hello.msg === 'isdbgrid') {
							 | 
						||
| 
								 | 
							
								        return common_1.ServerType.Mongos;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (hello.setName) {
							 | 
						||
| 
								 | 
							
								        if (hello.hidden) {
							 | 
						||
| 
								 | 
							
								            return common_1.ServerType.RSOther;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else if (hello.isWritablePrimary) {
							 | 
						||
| 
								 | 
							
								            return common_1.ServerType.RSPrimary;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else if (hello.secondary) {
							 | 
						||
| 
								 | 
							
								            return common_1.ServerType.RSSecondary;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else if (hello.arbiterOnly) {
							 | 
						||
| 
								 | 
							
								            return common_1.ServerType.RSArbiter;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else {
							 | 
						||
| 
								 | 
							
								            return common_1.ServerType.RSOther;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return common_1.ServerType.Standalone;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								exports.parseServerType = parseServerType;
							 | 
						||
| 
								 | 
							
								function tagsStrictEqual(tags, tags2) {
							 | 
						||
| 
								 | 
							
								    const tagsKeys = Object.keys(tags);
							 | 
						||
| 
								 | 
							
								    const tags2Keys = Object.keys(tags2);
							 | 
						||
| 
								 | 
							
								    return (tagsKeys.length === tags2Keys.length &&
							 | 
						||
| 
								 | 
							
								        tagsKeys.every((key) => tags2[key] === tags[key]));
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Compares two topology versions.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * 1. If the response topologyVersion is unset or the ServerDescription's
							 | 
						||
| 
								 | 
							
								 *    topologyVersion is null, the client MUST assume the response is more recent.
							 | 
						||
| 
								 | 
							
								 * 1. If the response's topologyVersion.processId is not equal to the
							 | 
						||
| 
								 | 
							
								 *    ServerDescription's, the client MUST assume the response is more recent.
							 | 
						||
| 
								 | 
							
								 * 1. If the response's topologyVersion.processId is equal to the
							 | 
						||
| 
								 | 
							
								 *    ServerDescription's, the client MUST use the counter field to determine
							 | 
						||
| 
								 | 
							
								 *    which topologyVersion is more recent.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * ```ts
							 | 
						||
| 
								 | 
							
								 * currentTv <   newTv === -1
							 | 
						||
| 
								 | 
							
								 * currentTv === newTv === 0
							 | 
						||
| 
								 | 
							
								 * currentTv >   newTv === 1
							 | 
						||
| 
								 | 
							
								 * ```
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function compareTopologyVersion(currentTv, newTv) {
							 | 
						||
| 
								 | 
							
								    if (currentTv == null || newTv == null) {
							 | 
						||
| 
								 | 
							
								        return -1;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (!currentTv.processId.equals(newTv.processId)) {
							 | 
						||
| 
								 | 
							
								        return -1;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    // TODO(NODE-2674): Preserve int64 sent from MongoDB
							 | 
						||
| 
								 | 
							
								    const currentCounter = bson_1.Long.isLong(currentTv.counter)
							 | 
						||
| 
								 | 
							
								        ? currentTv.counter
							 | 
						||
| 
								 | 
							
								        : bson_1.Long.fromNumber(currentTv.counter);
							 | 
						||
| 
								 | 
							
								    const newCounter = bson_1.Long.isLong(newTv.counter) ? newTv.counter : bson_1.Long.fromNumber(newTv.counter);
							 | 
						||
| 
								 | 
							
								    return currentCounter.compare(newCounter);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								exports.compareTopologyVersion = compareTopologyVersion;
							 | 
						||
| 
								 | 
							
								//# sourceMappingURL=server_description.js.map
							 |