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