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
 |