import ConsistentHash from "consistent-hash";1import { hash_string } from "@cocalc/util/misc";23export function consistentHashingChoice(4v: Set<string>,5resource: string,6): string {7if (v.size == 0) {8throw Error("v must have size at least 1");9}10if (v.size == 1) {11for (const x of v) {12return x;13}14}15const hr = new ConsistentHash({ distribution: "uniform" });16const w = Array.from(v);17w.sort();18for (const x of w) {19hr.add(x);20}21// we hash the resource so that the values are randomly distributed even22// if the resources look very similar (e.g., subject.1, subject.2, etc.)23// I thought that "consistent-hash" hashed the resource, but it doesn't really.24return hr.get(hash_string(resource));25}2627export function stickyChoice({28subject,29pattern,30targets,31updateSticky,32getStickyTarget,33}: {34subject: string;35pattern: string;36targets: Set<string>;37updateSticky?;38getStickyTarget: (opts: {39pattern: string;40subject: string;41targets: Set<string>;42}) => string | undefined;43}) {44const v = subject.split(".");45subject = v.slice(0, v.length - 1).join(".");46const currentTarget = getStickyTarget({ pattern, subject, targets });47if (currentTarget === undefined || !targets.has(currentTarget)) {48// we use consistent hashing instead of random to make the choice, because if49// choice is being made by two different socketio servers at the same time,50// and they make different choices, it would be (temporarily) bad since a51// couple messages could get routed inconsistently.52// It's actually very highly likely to have such parallel choices53// happening in cocalc, since when a file is opened a persistent stream is opened54// in the browser and the project at the exact same time, and those are likely55// to be connected to different socketio servers. By using consistent hashing,56// all conflicts are avoided except for a few moments when the actual targets57// (e.g., the persist servers) are themselves changing, which should be something58// that only happens for a moment every few days.59const target = consistentHashingChoice(targets, subject);60updateSticky?.({ pattern, subject, target });61return target;62}63return currentTarget;64}656667