"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