CoolFace
Apppublic

opusdev/vector-similarity-api

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes
server_description.ts292 linesDownload Raw Back to sdam
1import { type Document, Long, type ObjectId } from '../bson';2import { type MongoError, MongoRuntimeError } from '../error';3import { arrayStrictEqual, compareObjectId, errorStrictEqual, HostAddress, now } from '../utils';4import { type ClusterTime, ServerType } from './common';5 6const WRITABLE_SERVER_TYPES = new Set<ServerType>([7  ServerType.RSPrimary,8  ServerType.Standalone,9  ServerType.Mongos,10  ServerType.LoadBalancer11]);12 13const DATA_BEARING_SERVER_TYPES = new Set<ServerType>([14  ServerType.RSPrimary,15  ServerType.RSSecondary,16  ServerType.Mongos,17  ServerType.Standalone,18  ServerType.LoadBalancer19]);20 21/** @public */22export interface TopologyVersion {23  processId: ObjectId;24  counter: Long;25}26 27/** @public */28export type TagSet = { [key: string]: string };29 30/** @internal */31export interface ServerDescriptionOptions {32  /** An Error used for better reporting debugging */33  error?: MongoError;34 35  /** The average round trip time to ping this server (in ms) */36  roundTripTime?: number;37  /** The minimum round trip time to ping this server over the past 10 samples(in ms) */38  minRoundTripTime?: number;39 40  /** If the client is in load balancing mode. */41  loadBalanced?: boolean;42}43 44/**45 * The client's view of a single server, based on the most recent hello outcome.46 *47 * Internal type, not meant to be directly instantiated48 * @public49 */50export class ServerDescription {51  address: string;52  type: ServerType;53  hosts: string[];54  passives: string[];55  arbiters: string[];56  tags: TagSet;57  error: MongoError | null;58  topologyVersion: TopologyVersion | null;59  minWireVersion: number;60  maxWireVersion: number;61  roundTripTime: number;62  /** The minimum measurement of the last 10 measurements of roundTripTime that have been collected */63  minRoundTripTime: number;64  lastUpdateTime: number;65  lastWriteDate: number;66  me: string | null;67  primary: string | null;68  setName: string | null;69  setVersion: number | null;70  electionId: ObjectId | null;71  logicalSessionTimeoutMinutes: number | null;72  /** The max message size in bytes for the server. */73  maxMessageSizeBytes: number | null;74  /** The max number of writes in a bulk write command. */75  maxWriteBatchSize: number | null;76  /** The max bson object size. */77  maxBsonObjectSize: number | null;78  /** Indicates server is a mongocryptd instance. */79  iscryptd: boolean;80 81  // NOTE: does this belong here? It seems we should gossip the cluster time at the CMAP level82  $clusterTime?: ClusterTime;83 84  /**85   * Create a ServerDescription86   * @internal87   *88   * @param address - The address of the server89   * @param hello - An optional hello response for this server90   */91  constructor(92    address: HostAddress | string,93    hello?: Document,94    options: ServerDescriptionOptions = {}95  ) {96    if (address == null || address === '') {97      throw new MongoRuntimeError('ServerDescription must be provided with a non-empty address');98    }99 100    this.address =101      typeof address === 'string'102        ? HostAddress.fromString(address).toString() // Use HostAddress to normalize103        : address.toString();104    this.type = parseServerType(hello, options);105    this.hosts = hello?.hosts?.map((host: string) => host.toLowerCase()) ?? [];106    this.passives = hello?.passives?.map((host: string) => host.toLowerCase()) ?? [];107    this.arbiters = hello?.arbiters?.map((host: string) => host.toLowerCase()) ?? [];108    this.tags = hello?.tags ?? {};109    this.minWireVersion = hello?.minWireVersion ?? 0;110    this.maxWireVersion = hello?.maxWireVersion ?? 0;111    this.roundTripTime = options?.roundTripTime ?? -1;112    this.minRoundTripTime = options?.minRoundTripTime ?? 0;113    this.lastUpdateTime = now();114    this.lastWriteDate = hello?.lastWrite?.lastWriteDate ?? 0;115    // NOTE: This actually builds the stack string instead of holding onto the getter and all its116    // associated references. This is done to prevent a memory leak.117    this.error = options.error ?? null;118    this.error?.stack;119    // TODO(NODE-2674): Preserve int64 sent from MongoDB120    this.topologyVersion = this.error?.topologyVersion ?? hello?.topologyVersion ?? null;121    this.setName = hello?.setName ?? null;122    this.setVersion = hello?.setVersion ?? null;123    this.electionId = hello?.electionId ?? null;124    this.logicalSessionTimeoutMinutes = hello?.logicalSessionTimeoutMinutes ?? null;125    this.maxMessageSizeBytes = hello?.maxMessageSizeBytes ?? null;126    this.maxWriteBatchSize = hello?.maxWriteBatchSize ?? null;127    this.maxBsonObjectSize = hello?.maxBsonObjectSize ?? null;128    this.primary = hello?.primary ?? null;129    this.me = hello?.me?.toLowerCase() ?? null;130    this.$clusterTime = hello?.$clusterTime ?? null;131    this.iscryptd = Boolean(hello?.iscryptd);132  }133 134  get hostAddress(): HostAddress {135    return HostAddress.fromString(this.address);136  }137 138  get allHosts(): string[] {139    return this.hosts.concat(this.arbiters).concat(this.passives);140  }141 142  /** Is this server available for reads*/143  get isReadable(): boolean {144    return this.type === ServerType.RSSecondary || this.isWritable;145  }146 147  /** Is this server data bearing */148  get isDataBearing(): boolean {149    return DATA_BEARING_SERVER_TYPES.has(this.type);150  }151 152  /** Is this server available for writes */153  get isWritable(): boolean {154    return WRITABLE_SERVER_TYPES.has(this.type);155  }156 157  get host(): string {158    const chopLength = `:${this.port}`.length;159    return this.address.slice(0, -chopLength);160  }161 162  get port(): number {163    const port = this.address.split(':').pop();164    return port ? Number.parseInt(port, 10) : 27017;165  }166 167  /**168   * Determines if another `ServerDescription` is equal to this one per the rules defined in the SDAM specification.169   * @see https://github.com/mongodb/specifications/blob/master/source/server-discovery-and-monitoring/server-discovery-and-monitoring.md170   */171  equals(other?: ServerDescription | null): boolean {172    // Despite using the comparator that would determine a nullish topologyVersion as greater than173    // for equality we should only always perform direct equality comparison174    const topologyVersionsEqual =175      this.topologyVersion === other?.topologyVersion ||176      compareTopologyVersion(this.topologyVersion, other?.topologyVersion) === 0;177 178    const electionIdsEqual =179      this.electionId != null && other?.electionId != null180        ? compareObjectId(this.electionId, other.electionId) === 0181        : this.electionId === other?.electionId;182 183    return (184      other != null &&185      other.iscryptd === this.iscryptd &&186      errorStrictEqual(this.error, other.error) &&187      this.type === other.type &&188      this.minWireVersion === other.minWireVersion &&189      arrayStrictEqual(this.hosts, other.hosts) &&190      tagsStrictEqual(this.tags, other.tags) &&191      this.setName === other.setName &&192      this.setVersion === other.setVersion &&193      electionIdsEqual &&194      this.primary === other.primary &&195      this.logicalSessionTimeoutMinutes === other.logicalSessionTimeoutMinutes &&196      topologyVersionsEqual197    );198  }199}200 201// Parses a `hello` message and determines the server type202export function parseServerType(hello?: Document, options?: ServerDescriptionOptions): ServerType {203  if (options?.loadBalanced) {204    return ServerType.LoadBalancer;205  }206 207  if (!hello || !hello.ok) {208    return ServerType.Unknown;209  }210 211  if (hello.isreplicaset) {212    return ServerType.RSGhost;213  }214 215  if (hello.msg && hello.msg === 'isdbgrid') {216    return ServerType.Mongos;217  }218 219  if (hello.setName) {220    if (hello.hidden) {221      return ServerType.RSOther;222    } else if (hello.isWritablePrimary) {223      return ServerType.RSPrimary;224    } else if (hello.secondary) {225      return ServerType.RSSecondary;226    } else if (hello.arbiterOnly) {227      return ServerType.RSArbiter;228    } else {229      return ServerType.RSOther;230    }231  }232 233  return ServerType.Standalone;234}235 236function tagsStrictEqual(tags: TagSet, tags2: TagSet): boolean {237  const tagsKeys = Object.keys(tags);238  const tags2Keys = Object.keys(tags2);239 240  return (241    tagsKeys.length === tags2Keys.length &&242    tagsKeys.every((key: string) => tags2[key] === tags[key])243  );244}245 246/**247 * Compares two topology versions.248 *249 * 1. If the response topologyVersion is unset or the ServerDescription's250 *    topologyVersion is null, the client MUST assume the response is more recent.251 * 1. If the response's topologyVersion.processId is not equal to the252 *    ServerDescription's, the client MUST assume the response is more recent.253 * 1. If the response's topologyVersion.processId is equal to the254 *    ServerDescription's, the client MUST use the counter field to determine255 *    which topologyVersion is more recent.256 *257 * ```ts258 * currentTv <   newTv === -1259 * currentTv === newTv === 0260 * currentTv >   newTv === 1261 * ```262 */263export function compareTopologyVersion(264  currentTv?: TopologyVersion | null,265  newTv?: TopologyVersion | null266): 0 | -1 | 1 {267  if (currentTv == null || newTv == null) {268    return -1;269  }270 271  if (!currentTv.processId.equals(newTv.processId)) {272    return -1;273  }274 275  // TODO(NODE-2674): Preserve int64 sent from MongoDB276  const currentCounter =277    typeof currentTv.counter === 'bigint'278      ? Long.fromBigInt(currentTv.counter)279      : Long.isLong(currentTv.counter)280        ? currentTv.counter281        : Long.fromNumber(currentTv.counter);282 283  const newCounter =284    typeof newTv.counter === 'bigint'285      ? Long.fromBigInt(newTv.counter)286      : Long.isLong(newTv.counter)287        ? newTv.counter288        : Long.fromNumber(newTv.counter);289 290  return currentCounter.compare(newCounter);291}292