Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/backend/conat/test/cluster/node-discovery.test.ts
1712 views
1
import {
2
before,
3
after,
4
delay,
5
wait,
6
waitForConsistentState,
7
} from "@cocalc/backend/conat/test/setup";
8
import { createClusterNode } from "./util";
9
import { isEqual } from "lodash";
10
11
beforeAll(before);
12
13
jest.setTimeout(30000);
14
describe("test automatic node discovery (and forgetting)", () => {
15
const nodes: { client; server }[] = [];
16
const clusterName = "auto";
17
const create = async (id) => {
18
nodes.push(
19
await createClusterNode({
20
id,
21
clusterName,
22
autoscanInterval: 50,
23
longAutoscanInterval: 6000,
24
forgetClusterNodeInterval: 500, // make automatic forgetting short so we can test.
25
}),
26
);
27
};
28
29
it("create two servers with cluster support enabled", async () => {
30
await create("node0");
31
await create("node1");
32
});
33
34
it("connect 0 -> 1 and see other link get automatically added", async () => {
35
expect(nodes[0].server.clusterAddresses(clusterName).length).toBe(1);
36
await nodes[0].server.join(nodes[1].server.address());
37
expect(nodes[0].server.clusterAddresses(clusterName).length).toBe(2);
38
expect(nodes[1].server.clusterAddresses(clusterName).length).toBe(1);
39
await wait({
40
until: () => {
41
return nodes[1].server.clusterAddresses(clusterName).length == 2;
42
},
43
});
44
});
45
46
it("make a new node and a connection 2 -> 1 and observe cluster gets completed automatically", async () => {
47
await create("node2");
48
await nodes[2].server.join(nodes[1].server.address());
49
// node0 and node1 don't instantly know node2
50
expect(nodes[0].server.clusterAddresses(clusterName).length).toBe(2);
51
expect(nodes[1].server.clusterAddresses(clusterName).length).toBe(2);
52
expect(nodes[2].server.clusterAddresses(clusterName).length).toBe(2);
53
// but soon they will all know each other
54
await wait({
55
until: () => {
56
return (
57
nodes[0].server.clusterAddresses(clusterName).length == 3 &&
58
nodes[1].server.clusterAddresses(clusterName).length == 3 &&
59
nodes[2].server.clusterAddresses(clusterName).length == 3
60
);
61
},
62
});
63
});
64
65
// WORRY -- with count bigger, e.g., 5, sometimes this doesn't work.
66
// It might be an indicator of an issue.
67
const count = 3;
68
it(`check state is consistent -- before adding more ${count} nodes`, async () => {
69
await waitForConsistentState(nodes.map((x) => x.server));
70
});
71
72
it(`add ${count} more nodes`, async () => {
73
for (let i = 3; i < 3 + count; i++) {
74
await create(`node${i}`);
75
await nodes[i].server.join(nodes[i - 1].server.address());
76
}
77
});
78
79
it("wait until every node knows about every other node", async () => {
80
const total = nodes.length;
81
const all = new Set(nodes.map((x) => x.server.address()));
82
await wait({
83
until: () => {
84
for (let i = 0; i < total; i++) {
85
if (
86
!isEqual(
87
all,
88
new Set(nodes[i].server.clusterAddresses(clusterName)),
89
)
90
) {
91
return false;
92
}
93
}
94
return true;
95
},
96
});
97
});
98
99
it(`wait for cluster to have consistent state -- after adding ${count} nodes`, async () => {
100
await waitForConsistentState(nodes.map((x) => x.server));
101
});
102
103
it("close nodes[1], run scan, and observe that nodes[1] is forgotten", async () => {
104
const numNodes = () => {
105
return Object.keys(
106
nodes[0].server.clusterLinks[nodes[0].server.clusterName],
107
).length;
108
};
109
const n = numNodes();
110
nodes[1].server.close();
111
expect(nodes[1].server.isHealthy()).toBe(false);
112
// not instantly gone
113
expect(numNodes()).toBe(n);
114
await nodes[0].server.scan();
115
// still not gone
116
expect(numNodes()).toBe(n);
117
// wait a second and scan, and it must be gone (because we set the interval very short)
118
await delay(1000);
119
await nodes[0].server.scan();
120
expect(numNodes()).toBe(n - 1);
121
});
122
});
123
124
afterAll(after);
125
126