Skip to content

Commit 7390989

Browse files
committed
Merge remote-tracking branch 'origin/master' into cocalc-api-20250927
2 parents 29dc582 + 4f58818 commit 7390989

File tree

12 files changed

+332
-390
lines changed

12 files changed

+332
-390
lines changed

src/packages/backend/conat/test/cluster/cluster-sticky-state.test.ts

Lines changed: 0 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import {
33
after,
44
defaultCluster as servers,
55
waitForConsistentState,
6-
wait,
76
addNodeToDefaultCluster,
87
} from "@cocalc/backend/conat/test/setup";
98
import { STICKY_QUEUE_GROUP } from "@cocalc/conat/core/client";
@@ -46,9 +45,6 @@ describe("ensure sticky state sync and use is working properly", () => {
4645
);
4746
// publishing causes a choice to be made and saved on servers[0]
4847
await clients[0].publish(`subject.${i}.foo`, "hello");
49-
expect(servers[0].sticky[`subject.${i}.*`]).not.toBe(undefined);
50-
// but no choice on servers[1]
51-
expect(servers[1].sticky[`subject.${i}.*`]).toBe(undefined);
5248
}
5349
});
5450

@@ -65,51 +61,12 @@ describe("ensure sticky state sync and use is working properly", () => {
6561
chosen = await Promise.race([p0(), p1()]);
6662
});
6763

68-
it(`sticky on servers[0] should have ${count} entries starting in "subject".`, async () => {
69-
const v = Object.keys(servers[0].sticky).filter((s) =>
70-
s.startsWith("subject."),
71-
);
72-
expect(v.length).toBe(count);
73-
});
74-
75-
it(`sticky on servers[1] should have no entries starting in "subject".`, async () => {
76-
const v = Object.keys(servers[1].sticky).filter((s) =>
77-
s.startsWith("subject."),
78-
);
79-
expect(v.length).toBe(0);
80-
});
81-
82-
it(`servers[1]'s link to servers[0] should *eventually* have ${count} entries starting in "subject."`, async () => {
83-
// @ts-ignore
84-
const link = servers[1].clusterLinksByAddress[servers[0].address()];
85-
let v;
86-
await wait({
87-
until: () => {
88-
v = Object.keys(link.sticky).filter((s) => s.startsWith("subject."));
89-
return v.length == count;
90-
},
91-
});
92-
expect(v.length).toBe(count);
93-
});
94-
9564
it("send message from clients[1] to each subject", async () => {
9665
for (let i = 0; i < count; i++) {
9766
await clients[1].publish(`subject.${i}.foo`);
9867
}
9968
});
10069

101-
// Sometimes this fails under very heavy load.
102-
// It's not a good test, because it probably hits some timeouts sometimes, and
103-
// it is testing internal structure/optimizations, not behavior.
104-
// Note also that minimizing sticky state computation is just an optimization so even if this test were failing
105-
// due to a bug, it might just mean things are slightly slower.
106-
// it(`sticky on servers[1] should STILL have no entries starting in "subject", since no choices had to be made`, async () => {
107-
// const v = Object.keys(servers[1].sticky).filter((s) =>
108-
// s.startsWith("subject."),
109-
// );
110-
// expect(v.length).toBe(0);
111-
// });
112-
11370
async function deliveryTest() {
11471
const sub = chosen == 0 ? subs0[0] : subs1[0];
11572

@@ -154,17 +111,6 @@ describe("ensure sticky state sync and use is working properly", () => {
154111
await waitForConsistentState(servers);
155112
});
156113

157-
it("double check the links have the sticky state", () => {
158-
for (const server of servers.slice(1)) {
159-
// @ts-ignore
160-
const link = server.clusterLinksByAddress[servers[0].address()];
161-
const v = Object.keys(link.sticky).filter((s) =>
162-
s.startsWith("subject."),
163-
);
164-
expect(v.length).toBe(count);
165-
}
166-
});
167-
168114
it(
169115
"in bigger, cluster, publish from every node to subject.0.foo",
170116
deliveryTest,
@@ -180,14 +126,6 @@ describe("ensure sticky state sync and use is working properly", () => {
180126
}
181127
sub.close();
182128
});
183-
184-
it("unjoining servers[0] from servers[1] should transfer the sticky state to servers[1]", async () => {
185-
await servers[1].unjoin({ address: servers[0].address() });
186-
const v = Object.keys(servers[1].sticky).filter((s) =>
187-
s.startsWith("subject."),
188-
);
189-
expect(v.length).toBe(count);
190-
});
191129
});
192130

193131
afterAll(after);

src/packages/backend/conat/test/setup.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,6 @@ export async function waitForConsistentState(
206206
// now look at everybody else's view of servers[i].
207207
// @ts-ignore
208208
const a = servers[i].interest.serialize().patterns;
209-
const b = servers[i].sticky;
210209
const hashServer = servers[i].hash();
211210
for (let j = 0; j < servers.length; j++) {
212211
if (i != j) {
@@ -220,9 +219,8 @@ export async function waitForConsistentState(
220219
}
221220
const hashLink = link.hash();
222221
const x = link.interest.serialize().patterns;
223-
const y = link.sticky;
224222
const showInfo = () => {
225-
for (const type of ["interest", "sticky"]) {
223+
for (const type of ["interest"]) {
226224
console.log(
227225
`server stream ${type}: `,
228226
hashServer[type],
@@ -254,8 +252,6 @@ export async function waitForConsistentState(
254252
j,
255253
serverInterest: a,
256254
linkInterest: x,
257-
serverSticky: b,
258-
linkSticky: y,
259255
});
260256
};
261257
if (!isEqual(hashServer, hashLink)) {

src/packages/backend/conat/test/sync/open-files.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ to open so they can fulfill their backend responsibilities:
88
99
DEVELOPMENT:
1010
11-
pnpm test ./open-files.test.ts
11+
pnpm test `pwd`/open-files.test.ts
1212
1313
*/
1414

src/packages/conat/core/cluster.ts

Lines changed: 8 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
import { type Client, connect } from "./client";
22
import { Patterns } from "./patterns";
3-
import {
4-
updateInterest,
5-
updateSticky,
6-
type InterestUpdate,
7-
type StickyUpdate,
8-
} from "@cocalc/conat/core/server";
3+
import { updateInterest, type InterestUpdate } from "@cocalc/conat/core/server";
94
import type { DStream } from "@cocalc/conat/sync/dstream";
105
import { once } from "@cocalc/util/async-utils";
116
import { server as createPersistServer } from "@cocalc/conat/persist/server";
@@ -47,14 +42,12 @@ export async function clusterLink(
4742
return link;
4843
}
4944

50-
export type Sticky = { [pattern: string]: { [subject: string]: string } };
5145
export type Interest = Patterns<{ [queue: string]: Set<string> }>;
5246

5347
export { type ClusterLink };
5448

5549
class ClusterLink {
5650
public interest: Interest = new Patterns();
57-
public sticky: Sticky = {};
5851
private streams: ClusterStreams;
5952
private state: "init" | "ready" | "closed" = "init";
6053
private clientStateChanged = Date.now(); // when client status last changed
@@ -85,10 +78,7 @@ class ClusterLink {
8578
clusterName: this.clusterName,
8679
});
8780
for (const update of this.streams.interest.getAll()) {
88-
updateInterest(update, this.interest, this.sticky);
89-
}
90-
for (const update of this.streams.sticky.getAll()) {
91-
updateSticky(update, this.sticky);
81+
updateInterest(update, this.interest);
9282
}
9383
// I have a slight concern about this because updates might not
9484
// arrive in order during automatic failover. That said, maybe
@@ -97,7 +87,6 @@ class ClusterLink {
9787
// it is about, and when that server goes down none of this state
9888
// matters anymore.
9989
this.streams.interest.on("change", this.handleInterestUpdate);
100-
this.streams.sticky.on("change", this.handleStickyUpdate);
10190
this.state = "ready";
10291
};
10392

@@ -106,11 +95,7 @@ class ClusterLink {
10695
};
10796

10897
handleInterestUpdate = (update: InterestUpdate) => {
109-
updateInterest(update, this.interest, this.sticky);
110-
};
111-
112-
handleStickyUpdate = (update: StickyUpdate) => {
113-
updateSticky(update, this.sticky);
98+
updateInterest(update, this.interest);
11499
};
115100

116101
private handleClientStateChanged = () => {
@@ -134,7 +119,6 @@ class ClusterLink {
134119
if (this.streams != null) {
135120
this.streams.interest.removeListener("change", this.handleInterestUpdate);
136121
this.streams.interest.close();
137-
this.streams.sticky.close();
138122
// @ts-ignore
139123
delete this.streams;
140124
}
@@ -178,10 +162,9 @@ class ClusterLink {
178162
return false;
179163
};
180164

181-
hash = (): { interest: number; sticky: number } => {
165+
hash = (): { interest: number } => {
182166
return {
183167
interest: hashInterest(this.interest),
184-
sticky: hashSticky(this.sticky),
185168
};
186169
};
187170
}
@@ -195,7 +178,6 @@ function clusterStreamNames({
195178
}) {
196179
return {
197180
interest: `cluster/${clusterName}/${id}/interest`,
198-
sticky: `cluster/${clusterName}/${id}/sticky`,
199181
};
200182
}
201183

@@ -225,7 +207,6 @@ export async function createClusterPersistServer({
225207

226208
export interface ClusterStreams {
227209
interest: DStream<InterestUpdate>;
228-
sticky: DStream<StickyUpdate>;
229210
}
230211

231212
export async function clusterStreams({
@@ -252,27 +233,21 @@ export async function clusterStreams({
252233
name: names.interest,
253234
...opts,
254235
});
255-
const sticky = await client.sync.dstream<StickyUpdate>({
256-
noInventory: true,
257-
name: names.sticky,
258-
...opts,
259-
});
260236
logger.debug("clusterStreams: got them", { clusterName });
261-
return { interest, sticky };
237+
return { interest };
262238
}
263239

264240
// Periodically delete not-necessary updates from the interest stream
265241
export async function trimClusterStreams(
266242
streams: ClusterStreams,
267243
data: {
268244
interest: Patterns<{ [queue: string]: Set<string> }>;
269-
sticky: { [pattern: string]: { [subject: string]: string } };
270245
links: { interest: Patterns<{ [queue: string]: Set<string> }> }[];
271246
},
272247
// don't delete anything that isn't at lest minAge ms old.
273248
minAge: number,
274-
): Promise<{ seqsInterest: number[]; seqsSticky: number[] }> {
275-
const { interest, sticky } = streams;
249+
): Promise<{ seqsInterest: number[] }> {
250+
const { interest } = streams;
276251
// First deal with interst
277252
// we iterate over the interest stream checking for subjects
278253
// with no current interest at all; in such cases it is safe
@@ -300,45 +275,7 @@ export async function trimClusterStreams(
300275
logger.debug("trimClusterStream: successfully trimmed interest", { seqs });
301276
}
302277

303-
// Next deal with sticky -- trim ones where the pattern is no longer of interest.
304-
// There could be other reasons to trim but it gets much trickier. This one is more
305-
// obvious, except we have to check for any interest in the whole cluster, not
306-
// just this node.
307-
const seqs2: number[] = [];
308-
function noInterest(pattern: string) {
309-
if (data.interest.hasPattern(pattern)) {
310-
return false;
311-
}
312-
for (const link of data.links) {
313-
if (link.interest.hasPattern(pattern)) {
314-
return false;
315-
}
316-
}
317-
// nobody cares
318-
return true;
319-
}
320-
for (let n = 0; n < sticky.length; n++) {
321-
const time = sticky.time(n);
322-
if (time == null) continue;
323-
if (now - time.valueOf() <= minAge) {
324-
break;
325-
}
326-
const update = sticky.get(n) as StickyUpdate;
327-
if (noInterest(update.pattern)) {
328-
const seq = sticky.seq(n);
329-
if (seq != null) {
330-
seqs2.push(seq);
331-
}
332-
}
333-
}
334-
if (seqs2.length > 0) {
335-
// [ ] todo -- add to interest.delete a version where it takes an array of sequence numbers
336-
logger.debug("trimClusterStream: trimming sticky", { seqs2 });
337-
await sticky.delete({ seqs: seqs2 });
338-
logger.debug("trimClusterStream: successfully trimmed sticky", { seqs2 });
339-
}
340-
341-
return { seqsInterest: seqs, seqsSticky: seqs2 };
278+
return { seqsInterest: seqs };
342279
}
343280

344281
function hashSet(X: Set<string>): number {
@@ -362,15 +299,3 @@ export function hashInterest(
362299
): number {
363300
return interest.hash(hashInterestValue);
364301
}
365-
366-
export function hashSticky(sticky: Sticky): number {
367-
let h = 0;
368-
for (const pattern in sticky) {
369-
h += hash_string(pattern);
370-
const x = sticky[pattern];
371-
for (const subject in x) {
372-
h += hash_string(x[subject]);
373-
}
374-
}
375-
return h;
376-
}

0 commit comments

Comments
 (0)