CoolFace
Apppublic

opusdev/vector-similarity-api

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes
server_selection.ts324 linesDownload Raw Back to sdam
1import { MongoInvalidArgumentError } from '../error';2import { ReadPreference } from '../read_preference';3import { ServerType, TopologyType } from './common';4import type { ServerDescription, TagSet } from './server_description';5import type { TopologyDescription } from './topology_description';6 7// max staleness constants8const IDLE_WRITE_PERIOD = 10000;9const SMALLEST_MAX_STALENESS_SECONDS = 90;10 11//  Minimum version to try writes on secondaries.12export const MIN_SECONDARY_WRITE_WIRE_VERSION = 13;13 14/** @internal */15export type ServerSelector = (16  topologyDescription: TopologyDescription,17  servers: ServerDescription[],18  deprioritized?: ServerDescription[]19) => ServerDescription[];20 21/**22 * Returns a server selector that selects for writable servers23 */24export function writableServerSelector(): ServerSelector {25  return function writableServer(26    topologyDescription: TopologyDescription,27    servers: ServerDescription[]28  ): ServerDescription[] {29    return latencyWindowReducer(30      topologyDescription,31      servers.filter((s: ServerDescription) => s.isWritable)32    );33  };34}35 36/**37 * The purpose of this selector is to select the same server, only38 * if it is in a state that it can have commands sent to it.39 */40export function sameServerSelector(description?: ServerDescription): ServerSelector {41  return function sameServerSelector(42    topologyDescription: TopologyDescription,43    servers: ServerDescription[]44  ): ServerDescription[] {45    if (!description) return [];46    // Filter the servers to match the provided description only if47    // the type is not unknown.48    return servers.filter(sd => {49      return sd.address === description.address && sd.type !== ServerType.Unknown;50    });51  };52}53 54/**55 * Returns a server selector that uses a read preference to select a56 * server potentially for a write on a secondary.57 */58export function secondaryWritableServerSelector(59  wireVersion?: number,60  readPreference?: ReadPreference61): ServerSelector {62  // If server version < 5.0, read preference always primary.63  // If server version >= 5.0...64  // - If read preference is supplied, use that.65  // - If no read preference is supplied, use primary.66  if (67    !readPreference ||68    !wireVersion ||69    (wireVersion && wireVersion < MIN_SECONDARY_WRITE_WIRE_VERSION)70  ) {71    return readPreferenceServerSelector(ReadPreference.primary);72  }73  return readPreferenceServerSelector(readPreference);74}75 76/**77 * Reduces the passed in array of servers by the rules of the "Max Staleness" specification78 * found here:79 *80 * @see https://github.com/mongodb/specifications/blob/master/source/max-staleness/max-staleness.md81 *82 * @param readPreference - The read preference providing max staleness guidance83 * @param topologyDescription - The topology description84 * @param servers - The list of server descriptions to be reduced85 * @returns The list of servers that satisfy the requirements of max staleness86 */87function maxStalenessReducer(88  readPreference: ReadPreference,89  topologyDescription: TopologyDescription,90  servers: ServerDescription[]91): ServerDescription[] {92  if (readPreference.maxStalenessSeconds == null || readPreference.maxStalenessSeconds < 0) {93    return servers;94  }95 96  const maxStaleness = readPreference.maxStalenessSeconds;97  const maxStalenessVariance =98    (topologyDescription.heartbeatFrequencyMS + IDLE_WRITE_PERIOD) / 1000;99  if (maxStaleness < maxStalenessVariance) {100    throw new MongoInvalidArgumentError(101      `Option "maxStalenessSeconds" must be at least ${maxStalenessVariance} seconds`102    );103  }104 105  if (maxStaleness < SMALLEST_MAX_STALENESS_SECONDS) {106    throw new MongoInvalidArgumentError(107      `Option "maxStalenessSeconds" must be at least ${SMALLEST_MAX_STALENESS_SECONDS} seconds`108    );109  }110 111  if (topologyDescription.type === TopologyType.ReplicaSetWithPrimary) {112    const primary: ServerDescription = Array.from(topologyDescription.servers.values()).filter(113      primaryFilter114    )[0];115 116    return servers.reduce((result: ServerDescription[], server: ServerDescription) => {117      const stalenessMS =118        server.lastUpdateTime -119        server.lastWriteDate -120        (primary.lastUpdateTime - primary.lastWriteDate) +121        topologyDescription.heartbeatFrequencyMS;122 123      const staleness = stalenessMS / 1000;124      const maxStalenessSeconds = readPreference.maxStalenessSeconds ?? 0;125      if (staleness <= maxStalenessSeconds) {126        result.push(server);127      }128 129      return result;130    }, []);131  }132 133  if (topologyDescription.type === TopologyType.ReplicaSetNoPrimary) {134    if (servers.length === 0) {135      return servers;136    }137 138    const sMax = servers.reduce((max: ServerDescription, s: ServerDescription) =>139      s.lastWriteDate > max.lastWriteDate ? s : max140    );141 142    return servers.reduce((result: ServerDescription[], server: ServerDescription) => {143      const stalenessMS =144        sMax.lastWriteDate - server.lastWriteDate + topologyDescription.heartbeatFrequencyMS;145 146      const staleness = stalenessMS / 1000;147      const maxStalenessSeconds = readPreference.maxStalenessSeconds ?? 0;148      if (staleness <= maxStalenessSeconds) {149        result.push(server);150      }151 152      return result;153    }, []);154  }155 156  return servers;157}158 159/**160 * Determines whether a server's tags match a given set of tags161 *162 * @param tagSet - The requested tag set to match163 * @param serverTags - The server's tags164 */165function tagSetMatch(tagSet: TagSet, serverTags: TagSet) {166  const keys = Object.keys(tagSet);167  const serverTagKeys = Object.keys(serverTags);168  for (let i = 0; i < keys.length; ++i) {169    const key = keys[i];170    if (serverTagKeys.indexOf(key) === -1 || serverTags[key] !== tagSet[key]) {171      return false;172    }173  }174 175  return true;176}177 178/**179 * Reduces a set of server descriptions based on tags requested by the read preference180 *181 * @param readPreference - The read preference providing the requested tags182 * @param servers - The list of server descriptions to reduce183 * @returns The list of servers matching the requested tags184 */185function tagSetReducer(186  readPreference: ReadPreference,187  servers: ServerDescription[]188): ServerDescription[] {189  if (190    readPreference.tags == null ||191    (Array.isArray(readPreference.tags) && readPreference.tags.length === 0)192  ) {193    return servers;194  }195 196  for (let i = 0; i < readPreference.tags.length; ++i) {197    const tagSet = readPreference.tags[i];198    const serversMatchingTagset = servers.reduce(199      (matched: ServerDescription[], server: ServerDescription) => {200        if (tagSetMatch(tagSet, server.tags)) matched.push(server);201        return matched;202      },203      []204    );205 206    if (serversMatchingTagset.length) {207      return serversMatchingTagset;208    }209  }210 211  return [];212}213 214/**215 * Reduces a list of servers to ensure they fall within an acceptable latency window. This is216 * further specified in the "Server Selection" specification, found here:217 *218 * @see https://github.com/mongodb/specifications/blob/master/source/server-selection/server-selection.md219 *220 * @param topologyDescription - The topology description221 * @param servers - The list of servers to reduce222 * @returns The servers which fall within an acceptable latency window223 */224function latencyWindowReducer(225  topologyDescription: TopologyDescription,226  servers: ServerDescription[]227): ServerDescription[] {228  const low = servers.reduce(229    (min: number, server: ServerDescription) => Math.min(server.roundTripTime, min),230    Infinity231  );232 233  const high = low + topologyDescription.localThresholdMS;234  return servers.reduce((result: ServerDescription[], server: ServerDescription) => {235    if (server.roundTripTime <= high && server.roundTripTime >= low) result.push(server);236    return result;237  }, []);238}239 240// filters241function primaryFilter(server: ServerDescription): boolean {242  return server.type === ServerType.RSPrimary;243}244 245function secondaryFilter(server: ServerDescription): boolean {246  return server.type === ServerType.RSSecondary;247}248 249function nearestFilter(server: ServerDescription): boolean {250  return server.type === ServerType.RSSecondary || server.type === ServerType.RSPrimary;251}252 253function knownFilter(server: ServerDescription): boolean {254  return server.type !== ServerType.Unknown;255}256 257function loadBalancerFilter(server: ServerDescription): boolean {258  return server.type === ServerType.LoadBalancer;259}260 261/**262 * Returns a function which selects servers based on a provided read preference263 *264 * @param readPreference - The read preference to select with265 */266export function readPreferenceServerSelector(readPreference: ReadPreference): ServerSelector {267  if (!readPreference.isValid()) {268    throw new MongoInvalidArgumentError('Invalid read preference specified');269  }270 271  return function readPreferenceServers(272    topologyDescription: TopologyDescription,273    servers: ServerDescription[],274    deprioritized: ServerDescription[] = []275  ): ServerDescription[] {276    if (topologyDescription.type === TopologyType.LoadBalanced) {277      return servers.filter(loadBalancerFilter);278    }279 280    if (topologyDescription.type === TopologyType.Unknown) {281      return [];282    }283 284    if (topologyDescription.type === TopologyType.Single) {285      return latencyWindowReducer(topologyDescription, servers.filter(knownFilter));286    }287 288    if (topologyDescription.type === TopologyType.Sharded) {289      const filtered = servers.filter(server => {290        return !deprioritized.includes(server);291      });292      const selectable = filtered.length > 0 ? filtered : deprioritized;293      return latencyWindowReducer(topologyDescription, selectable.filter(knownFilter));294    }295 296    const mode = readPreference.mode;297    if (mode === ReadPreference.PRIMARY) {298      return servers.filter(primaryFilter);299    }300 301    if (mode === ReadPreference.PRIMARY_PREFERRED) {302      const result = servers.filter(primaryFilter);303      if (result.length) {304        return result;305      }306    }307 308    const filter = mode === ReadPreference.NEAREST ? nearestFilter : secondaryFilter;309    const selectedServers = latencyWindowReducer(310      topologyDescription,311      tagSetReducer(312        readPreference,313        maxStalenessReducer(readPreference, topologyDescription, servers.filter(filter))314      )315    );316 317    if (mode === ReadPreference.SECONDARY_PREFERRED && selectedServers.length === 0) {318      return servers.filter(primaryFilter);319    }320 321    return selectedServers;322  };323}324