@peerbit/pubsub 5.3.5 → 5.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -154,6 +154,9 @@ export declare class TopicControlPlane extends DirectStream<PubSubEvents> implem
154
154
  private autoTopicRootCandidates;
155
155
  private autoTopicRootCandidateSet?;
156
156
  private reconcileShardOverlaysInFlight?;
157
+ private reconcileShardOverlaysDirty;
158
+ private topicControlPlaneStopping;
159
+ private topicControlPlaneLifecycleRevision;
157
160
  private hostOwnedShardRootsInFlight?;
158
161
  private hostOwnedShardRootsDirty;
159
162
  private autoCandidatesBroadcastTimers;
@@ -161,6 +164,7 @@ export declare class TopicControlPlane extends DirectStream<PubSubEvents> implem
161
164
  private autoCandidatesGossipUntil;
162
165
  private _onFanoutPeerUnreachable?;
163
166
  private fanoutChannels;
167
+ private ensureFanoutChannelInFlight;
164
168
  private pendingTopicRootQueries;
165
169
  private nextTopicRootQueryId;
166
170
  constructor(components: TopicControlPlaneComponents, props?: TopicControlPlaneOptions);
@@ -215,9 +219,12 @@ export declare class TopicControlPlane extends DirectStream<PubSubEvents> implem
215
219
  * their fallback behavior).
216
220
  */
217
221
  private decodePubSubMessage;
222
+ private getTopicRootCandidateGeneration;
223
+ private assertTopicControlPlaneActive;
224
+ private normalizePeerTopicRootState;
218
225
  private resolveTopicRootState;
219
226
  resolveTopicRoot(topic: string): Promise<string | undefined>;
220
- private resolveShardRoot;
227
+ private resolveShardRootState;
221
228
  private getConnectedTopicRootTrackers;
222
229
  private nextTopicRootRequestIdValue;
223
230
  private sendDirectControlMessage;
@@ -227,6 +234,7 @@ export declare class TopicControlPlane extends DirectStream<PubSubEvents> implem
227
234
  private resolveQueryableTopicRoot;
228
235
  private resolveTopicRootThroughPeers;
229
236
  private ensureFanoutChannel;
237
+ private ensureFanoutChannelOnce;
230
238
  private closeFanoutChannel;
231
239
  hostShardRootsNow(): Promise<void>;
232
240
  subscribe(topic: string): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
package/dist/src/index.js CHANGED
@@ -148,6 +148,9 @@ export class TopicControlPlane extends DirectStream {
148
148
  autoTopicRootCandidates = false;
149
149
  autoTopicRootCandidateSet;
150
150
  reconcileShardOverlaysInFlight;
151
+ reconcileShardOverlaysDirty = false;
152
+ topicControlPlaneStopping = false;
153
+ topicControlPlaneLifecycleRevision = 0;
151
154
  hostOwnedShardRootsInFlight;
152
155
  hostOwnedShardRootsDirty = false;
153
156
  autoCandidatesBroadcastTimers = [];
@@ -155,6 +158,7 @@ export class TopicControlPlane extends DirectStream {
155
158
  autoCandidatesGossipUntil = 0;
156
159
  _onFanoutPeerUnreachable;
157
160
  fanoutChannels = new Map();
161
+ ensureFanoutChannelInFlight = new Map();
158
162
  pendingTopicRootQueries = new Map();
159
163
  nextTopicRootQueryId = 1;
160
164
  constructor(components, props) {
@@ -263,6 +267,7 @@ export class TopicControlPlane extends DirectStream {
263
267
  this.scheduleReconcileShardOverlays();
264
268
  }
265
269
  async start() {
270
+ this.topicControlPlaneStopping = false;
266
271
  await this.fanout.start();
267
272
  this._onFanoutPeerUnreachable =
268
273
  this._onFanoutPeerUnreachable ||
@@ -275,8 +280,15 @@ export class TopicControlPlane extends DirectStream {
275
280
  if (this.hostOwnedShardRootsDirty) {
276
281
  this.scheduleHostOwnedShardRoots();
277
282
  }
283
+ if (this.reconcileShardOverlaysDirty) {
284
+ this.scheduleReconcileShardOverlays();
285
+ }
278
286
  }
279
287
  async stop() {
288
+ this.topicControlPlaneStopping = true;
289
+ this.topicControlPlaneLifecycleRevision += 1;
290
+ this.reconcileShardOverlaysDirty = false;
291
+ this.ensureFanoutChannelInFlight.clear();
280
292
  if (this._onFanoutPeerUnreachable) {
281
293
  this.fanout.removeEventListener("fanout:peer-unreachable", this._onFanoutPeerUnreachable);
282
294
  }
@@ -338,7 +350,8 @@ export class TopicControlPlane extends DirectStream {
338
350
  this.pendingTopicRootQueries.clear();
339
351
  this.debounceSubscribeAggregator.close();
340
352
  this.debounceUnsubscribeAggregator.close();
341
- return super.stop();
353
+ await super.stop();
354
+ this.reconcileShardOverlaysDirty = false;
342
355
  }
343
356
  async onPeerConnected(peerId, connection) {
344
357
  await super.onPeerConnected(peerId, connection);
@@ -554,22 +567,45 @@ export class TopicControlPlane extends DirectStream {
554
567
  });
555
568
  }
556
569
  scheduleReconcileShardOverlays() {
570
+ this.reconcileShardOverlaysDirty = true;
571
+ if (!this.started || this.stopping || this.topicControlPlaneStopping)
572
+ return;
557
573
  if (this.reconcileShardOverlaysInFlight)
558
574
  return;
559
- this.reconcileShardOverlaysInFlight = this.reconcileShardOverlays()
560
- .catch(() => {
561
- // best-effort retry: fanout streams/roots might not be ready yet.
562
- if (!this.started || this.stopping)
563
- return;
564
- const t = setTimeout(() => this.scheduleReconcileShardOverlays(), 250);
565
- t.unref?.();
566
- })
567
- .finally(() => {
575
+ this.reconcileShardOverlaysInFlight = (async () => {
576
+ for (;;) {
577
+ if (!this.started || this.stopping || this.topicControlPlaneStopping)
578
+ return;
579
+ if (!this.reconcileShardOverlaysDirty)
580
+ return;
581
+ this.reconcileShardOverlaysDirty = false;
582
+ try {
583
+ await this.reconcileShardOverlays();
584
+ }
585
+ catch {
586
+ // Fanout streams or roots might not be ready yet. Preserve both
587
+ // failures and root changes that arrived during the active pass.
588
+ if (!this.started || this.stopping || this.topicControlPlaneStopping)
589
+ return;
590
+ this.reconcileShardOverlaysDirty = true;
591
+ await new Promise((resolve) => {
592
+ const timer = setTimeout(resolve, 250);
593
+ timer.unref?.();
594
+ });
595
+ }
596
+ }
597
+ })().finally(() => {
568
598
  this.reconcileShardOverlaysInFlight = undefined;
599
+ if (this.reconcileShardOverlaysDirty &&
600
+ this.started &&
601
+ !this.stopping &&
602
+ !this.topicControlPlaneStopping) {
603
+ this.scheduleReconcileShardOverlays();
604
+ }
569
605
  });
570
606
  }
571
607
  async reconcileShardOverlays() {
572
- if (!this.started)
608
+ if (!this.started || this.topicControlPlaneStopping)
573
609
  return;
574
610
  const byShard = new Map();
575
611
  for (const topic of this.subscriptions.keys()) {
@@ -819,6 +855,30 @@ export class TopicControlPlane extends DirectStream {
819
855
  }
820
856
  return PubSubMessage.from(bytes);
821
857
  }
858
+ getTopicRootCandidateGeneration() {
859
+ return JSON.stringify([
860
+ this.autoTopicRootCandidates,
861
+ this.topicRootControlPlane.getTopicRootCandidates(),
862
+ ]);
863
+ }
864
+ assertTopicControlPlaneActive(lifecycleRevision) {
865
+ if (lifecycleRevision !== this.topicControlPlaneLifecycleRevision ||
866
+ !this.started ||
867
+ this.stopping ||
868
+ this.topicControlPlaneStopping) {
869
+ throw new NotStartedError();
870
+ }
871
+ }
872
+ normalizePeerTopicRootState(topic, root) {
873
+ const deterministic = this.topicRootControlPlane.resolveDeterministicTopicRoot(topic);
874
+ if (this.autoTopicRootCandidates &&
875
+ topic.startsWith(this.shardTopicPrefix) &&
876
+ deterministic &&
877
+ root !== deterministic) {
878
+ return { root: deterministic, authoritative: false };
879
+ }
880
+ return { root, authoritative: true };
881
+ }
822
882
  async resolveTopicRootState(topic) {
823
883
  const tracked = await this.topicRootControlPlane.resolveTrackedTopicRoot(topic);
824
884
  if (tracked) {
@@ -826,7 +886,12 @@ export class TopicControlPlane extends DirectStream {
826
886
  }
827
887
  const resolvedThroughPeers = await this.resolveTopicRootThroughPeers(topic);
828
888
  if (resolvedThroughPeers) {
829
- return { root: resolvedThroughPeers, authoritative: true };
889
+ // Unconfigured peer-query replies cannot override the locally
890
+ // deterministic root for internal shards in auto mode. Roots, resolvers,
891
+ // and trackers explicitly configured on this control plane are resolved
892
+ // above and remain authoritative. A leaf relying on a queried gateway for
893
+ // shard roots must first disable auto mode with setTopicRootCandidates([]).
894
+ return this.normalizePeerTopicRootState(topic, resolvedThroughPeers);
830
895
  }
831
896
  const deterministic = this.topicRootControlPlane.resolveDeterministicTopicRoot(topic);
832
897
  if (deterministic === this.publicKeyHash &&
@@ -836,38 +901,50 @@ export class TopicControlPlane extends DirectStream {
836
901
  await delay(150 * (attempt < 4 ? 1 : 2));
837
902
  const retried = await this.resolveTopicRootThroughPeers(topic);
838
903
  if (retried) {
839
- return { root: retried, authoritative: true };
904
+ return this.normalizePeerTopicRootState(topic, retried);
840
905
  }
841
906
  }
842
907
  }
843
- return { root: deterministic, authoritative: false };
908
+ return {
909
+ root: this.topicRootControlPlane.resolveDeterministicTopicRoot(topic),
910
+ authoritative: false,
911
+ };
844
912
  }
845
913
  async resolveTopicRoot(topic) {
846
914
  return (await this.resolveTopicRootState(topic)).root;
847
915
  }
848
- async resolveShardRoot(shardTopic) {
849
- // If someone configured topic-root candidates externally (e.g. TestSession router
850
- // selection or Peerbit.bootstrap) after this peer entered auto mode, disable auto
851
- // mode before we cache any roots based on a stale candidate set.
852
- if (this.autoTopicRootCandidates) {
853
- this.maybeDisableAutoTopicRootCandidatesIfExternallyConfigured();
854
- }
855
- const hasConnectedTrackers = this.getConnectedTopicRootTrackers().length > 0;
856
- const cached = this.shardRootCache.get(shardTopic);
857
- if (cached && (cached.authoritative || !hasConnectedTrackers)) {
858
- return cached.root;
859
- }
860
- const resolved = await this.resolveTopicRootState(shardTopic);
861
- if (!resolved.root) {
862
- throw new Error(`No root resolved for shard topic ${shardTopic}. Configure TopicRootControlPlane candidates/resolver/trackers, or dial/bootstrap a peer that can resolve shard roots.`);
863
- }
864
- if (resolved.authoritative || !hasConnectedTrackers) {
865
- this.shardRootCache.set(shardTopic, resolved);
866
- }
867
- else {
868
- this.shardRootCache.delete(shardTopic);
916
+ async resolveShardRootState(shardTopic) {
917
+ const lifecycleRevision = this.topicControlPlaneLifecycleRevision;
918
+ for (;;) {
919
+ this.assertTopicControlPlaneActive(lifecycleRevision);
920
+ // If someone configured topic-root candidates externally (e.g.
921
+ // TestSession router selection or Peerbit.bootstrap) after this peer
922
+ // entered auto mode, disable auto mode before caching a root.
923
+ if (this.autoTopicRootCandidates) {
924
+ this.maybeDisableAutoTopicRootCandidatesIfExternallyConfigured();
925
+ }
926
+ const candidateGeneration = this.getTopicRootCandidateGeneration();
927
+ const hasConnectedTrackers = this.getConnectedTopicRootTrackers().length > 0;
928
+ const cached = this.shardRootCache.get(shardTopic);
929
+ if (cached && (cached.authoritative || !hasConnectedTrackers)) {
930
+ return { root: cached.root, candidateGeneration };
931
+ }
932
+ const resolved = await this.resolveTopicRootState(shardTopic);
933
+ this.assertTopicControlPlaneActive(lifecycleRevision);
934
+ if (candidateGeneration !== this.getTopicRootCandidateGeneration()) {
935
+ continue;
936
+ }
937
+ if (!resolved.root) {
938
+ throw new Error(`No root resolved for shard topic ${shardTopic}. Configure TopicRootControlPlane candidates/resolver/trackers, or dial/bootstrap a peer that can resolve shard roots.`);
939
+ }
940
+ if (resolved.authoritative || !hasConnectedTrackers) {
941
+ this.shardRootCache.set(shardTopic, resolved);
942
+ }
943
+ else {
944
+ this.shardRootCache.delete(shardTopic);
945
+ }
946
+ return { root: resolved.root, candidateGeneration };
869
947
  }
870
- return resolved.root;
871
948
  }
872
949
  getConnectedTopicRootTrackers() {
873
950
  return [...this.peers.values()]
@@ -900,9 +977,11 @@ export class TopicControlPlane extends DirectStream {
900
977
  });
901
978
  await this.publishMessage(this.publicKey, embedded, [peer]);
902
979
  }
903
- resolvePendingTopicRootQuery(message) {
980
+ resolvePendingTopicRootQuery(message, responderPeerHash) {
904
981
  const pending = this.pendingTopicRootQueries.get(message.requestId);
905
- if (!pending || pending.topic !== message.topic) {
982
+ if (!pending ||
983
+ pending.topic !== message.topic ||
984
+ pending.expectedPeerHash !== responderPeerHash) {
906
985
  return false;
907
986
  }
908
987
  this.pendingTopicRootQueries.delete(message.requestId);
@@ -913,6 +992,7 @@ export class TopicControlPlane extends DirectStream {
913
992
  async queryTopicRootFromPeer(peer, topic, timeoutMs = DEFAULT_TOPIC_ROOT_QUERY_TIMEOUT_MS) {
914
993
  if (!this.started || this.stopping)
915
994
  return undefined;
995
+ const expectedPeerHash = peer.publicKey.hashcode();
916
996
  const requestId = this.nextTopicRootRequestIdValue();
917
997
  const responsePromise = new Promise((resolve) => {
918
998
  const timer = setTimeout(() => {
@@ -920,7 +1000,12 @@ export class TopicControlPlane extends DirectStream {
920
1000
  resolve(undefined);
921
1001
  }, Math.max(1, Math.floor(timeoutMs)));
922
1002
  timer.unref?.();
923
- this.pendingTopicRootQueries.set(requestId, { topic, resolve, timer });
1003
+ this.pendingTopicRootQueries.set(requestId, {
1004
+ expectedPeerHash,
1005
+ topic,
1006
+ resolve,
1007
+ timer,
1008
+ });
924
1009
  });
925
1010
  try {
926
1011
  await this.sendDirectControlMessage(peer, new TopicRootQuery({ requestId, topic }));
@@ -935,38 +1020,64 @@ export class TopicControlPlane extends DirectStream {
935
1020
  }
936
1021
  return responsePromise;
937
1022
  }
938
- async confirmDirectShardRoot(shardTopic, root, signal) {
1023
+ async confirmDirectShardRoot(shardTopic, root, candidateGeneration, lifecycleRevision, signal) {
1024
+ this.assertTopicControlPlaneActive(lifecycleRevision);
939
1025
  if (root === this.publicKeyHash)
940
1026
  return root;
941
1027
  const rootPeer = this.peers.get(root);
942
1028
  if (!rootPeer)
943
1029
  return root;
944
1030
  const confirmation = await withAbort(this.queryTopicRootFromPeer(rootPeer, shardTopic, DIRECT_SHARD_ROOT_CONFIRM_TIMEOUT_MS), signal);
1031
+ this.assertTopicControlPlaneActive(lifecycleRevision);
1032
+ if (candidateGeneration !== this.getTopicRootCandidateGeneration()) {
1033
+ return root;
1034
+ }
945
1035
  if (!confirmation)
946
1036
  return root;
947
- if (confirmation !== root) {
1037
+ const resolved = this.normalizePeerTopicRootState(shardTopic, confirmation);
1038
+ if (!resolved.authoritative) {
1039
+ this.shardRootCache.delete(shardTopic);
1040
+ }
1041
+ else if (resolved.root !== root) {
948
1042
  this.shardRootCache.set(shardTopic, {
949
- root: confirmation,
1043
+ root: resolved.root,
950
1044
  authoritative: true,
951
1045
  });
952
1046
  }
953
- return confirmation;
1047
+ return resolved.root;
954
1048
  }
955
1049
  async resolveQueryableTopicRoot(topic) {
956
- const root = await this.topicRootControlPlane.resolveCanonicalTopicRoot(topic);
957
- if (root !== this.publicKeyHash) {
958
- return root;
959
- }
960
- if (!topic.startsWith(this.shardTopicPrefix)) {
961
- return root;
962
- }
963
- try {
964
- await this.ensureFanoutChannel(topic, { pin: true, root });
965
- return root;
966
- }
967
- catch (error) {
968
- warn(`Failed to host shard root ${topic} before answering root query: ${error instanceof Error ? error.message : String(error)}`);
969
- return undefined;
1050
+ const lifecycleRevision = this.topicControlPlaneLifecycleRevision;
1051
+ for (;;) {
1052
+ this.assertTopicControlPlaneActive(lifecycleRevision);
1053
+ const rootCandidateGeneration = this.getTopicRootCandidateGeneration();
1054
+ const root = await this.topicRootControlPlane.resolveCanonicalTopicRoot(topic);
1055
+ this.assertTopicControlPlaneActive(lifecycleRevision);
1056
+ if (rootCandidateGeneration !== this.getTopicRootCandidateGeneration()) {
1057
+ continue;
1058
+ }
1059
+ if (root !== this.publicKeyHash) {
1060
+ return root;
1061
+ }
1062
+ if (!topic.startsWith(this.shardTopicPrefix)) {
1063
+ return root;
1064
+ }
1065
+ try {
1066
+ await this.ensureFanoutChannel(topic, {
1067
+ pin: true,
1068
+ root,
1069
+ rootCandidateGeneration,
1070
+ });
1071
+ this.assertTopicControlPlaneActive(lifecycleRevision);
1072
+ if (rootCandidateGeneration !== this.getTopicRootCandidateGeneration()) {
1073
+ continue;
1074
+ }
1075
+ return root;
1076
+ }
1077
+ catch (error) {
1078
+ warn(`Failed to host shard root ${topic} before answering root query: ${error instanceof Error ? error.message : String(error)}`);
1079
+ return undefined;
1080
+ }
970
1081
  }
971
1082
  }
972
1083
  async resolveTopicRootThroughPeers(topic) {
@@ -988,18 +1099,86 @@ export class TopicControlPlane extends DirectStream {
988
1099
  return undefined;
989
1100
  }
990
1101
  async ensureFanoutChannel(shardTopic, options) {
991
- if (!this.started)
992
- throw new NotStartedError();
1102
+ const topic = shardTopic.toString();
1103
+ const lifecycleRevision = this.topicControlPlaneLifecycleRevision;
1104
+ for (;;) {
1105
+ this.assertTopicControlPlaneActive(lifecycleRevision);
1106
+ const candidateGeneration = this.getTopicRootCandidateGeneration();
1107
+ const active = this.ensureFanoutChannelInFlight.get(topic);
1108
+ if (active?.candidateGeneration === candidateGeneration &&
1109
+ active.lifecycleRevision === lifecycleRevision) {
1110
+ try {
1111
+ await withAbort(active.opening, options?.signal);
1112
+ }
1113
+ catch (error) {
1114
+ if (options?.signal?.aborted)
1115
+ throw error;
1116
+ this.assertTopicControlPlaneActive(lifecycleRevision);
1117
+ }
1118
+ continue;
1119
+ }
1120
+ const opening = this.ensureFanoutChannelOnce(topic, options, lifecycleRevision, candidateGeneration);
1121
+ const inFlight = {
1122
+ candidateGeneration,
1123
+ lifecycleRevision,
1124
+ opening,
1125
+ };
1126
+ this.ensureFanoutChannelInFlight.set(topic, inFlight);
1127
+ try {
1128
+ await opening;
1129
+ this.assertTopicControlPlaneActive(lifecycleRevision);
1130
+ if (candidateGeneration !== this.getTopicRootCandidateGeneration()) {
1131
+ continue;
1132
+ }
1133
+ return;
1134
+ }
1135
+ catch (error) {
1136
+ if (options?.signal?.aborted)
1137
+ throw error;
1138
+ this.assertTopicControlPlaneActive(lifecycleRevision);
1139
+ if (candidateGeneration !== this.getTopicRootCandidateGeneration()) {
1140
+ continue;
1141
+ }
1142
+ throw error;
1143
+ }
1144
+ finally {
1145
+ if (this.ensureFanoutChannelInFlight.get(topic) === inFlight) {
1146
+ this.ensureFanoutChannelInFlight.delete(topic);
1147
+ }
1148
+ }
1149
+ }
1150
+ }
1151
+ async ensureFanoutChannelOnce(shardTopic, options, lifecycleRevision, candidateGeneration) {
1152
+ this.assertTopicControlPlaneActive(lifecycleRevision);
1153
+ if (candidateGeneration !== this.getTopicRootCandidateGeneration())
1154
+ return;
993
1155
  const t = shardTopic.toString();
994
1156
  const pin = options?.pin === true;
995
1157
  const wantEphemeral = options?.ephemeral === true;
996
1158
  // Allow callers that already resolved the shard root (e.g. hostShardRootsNow)
997
1159
  // to pass it through to avoid a race where the candidate set changes between
998
1160
  // two resolve calls, causing an unnecessary (and potentially slow) join.
999
- let root = options?.root;
1161
+ const suppliedRoot = options?.root;
1162
+ const suppliedRootGeneration = options?.rootCandidateGeneration;
1163
+ if (suppliedRoot !== undefined &&
1164
+ suppliedRootGeneration !== candidateGeneration) {
1165
+ return;
1166
+ }
1167
+ this.assertTopicControlPlaneActive(lifecycleRevision);
1168
+ let resolvedGeneration = candidateGeneration;
1169
+ let root = suppliedRoot;
1000
1170
  const existing = this.fanoutChannels.get(t);
1001
1171
  if (existing) {
1002
- root = root ?? (await this.resolveShardRoot(t));
1172
+ if (!root) {
1173
+ const resolved = await this.resolveShardRootState(t);
1174
+ this.assertTopicControlPlaneActive(lifecycleRevision);
1175
+ root = resolved.root;
1176
+ resolvedGeneration = resolved.candidateGeneration;
1177
+ }
1178
+ if (resolvedGeneration !== candidateGeneration ||
1179
+ candidateGeneration !== this.getTopicRootCandidateGeneration()) {
1180
+ return;
1181
+ }
1003
1182
  if (root === existing.root) {
1004
1183
  existing.lastUsedAt = Date.now();
1005
1184
  if (existing.ephemeral && !wantEphemeral) {
@@ -1012,13 +1191,33 @@ export class TopicControlPlane extends DirectStream {
1012
1191
  if (pin)
1013
1192
  this.pinnedShards.add(t);
1014
1193
  await withAbort(existing.join, options?.signal);
1194
+ this.assertTopicControlPlaneActive(lifecycleRevision);
1195
+ if (resolvedGeneration !== candidateGeneration ||
1196
+ candidateGeneration !== this.getTopicRootCandidateGeneration()) {
1197
+ return;
1198
+ }
1015
1199
  return;
1016
1200
  }
1017
- // Root mapping changed (candidate set updated): migrate to the new overlay.
1201
+ // Root mapping changed (candidate set updated): migrate to the new
1202
+ // overlay.
1018
1203
  await withAbort(this.closeFanoutChannel(t, { force: true }), options?.signal);
1204
+ this.assertTopicControlPlaneActive(lifecycleRevision);
1205
+ if (candidateGeneration !== this.getTopicRootCandidateGeneration()) {
1206
+ return;
1207
+ }
1208
+ }
1209
+ if (!root) {
1210
+ const resolved = await this.resolveShardRootState(t);
1211
+ this.assertTopicControlPlaneActive(lifecycleRevision);
1212
+ root = resolved.root;
1213
+ resolvedGeneration = resolved.candidateGeneration;
1214
+ }
1215
+ root = await this.confirmDirectShardRoot(t, root, resolvedGeneration, lifecycleRevision, options?.signal);
1216
+ this.assertTopicControlPlaneActive(lifecycleRevision);
1217
+ if (resolvedGeneration !== candidateGeneration ||
1218
+ candidateGeneration !== this.getTopicRootCandidateGeneration()) {
1219
+ return;
1019
1220
  }
1020
- root = root ?? (await this.resolveShardRoot(t));
1021
- root = await this.confirmDirectShardRoot(t, root, options?.signal);
1022
1221
  const channel = new FanoutChannel(this.fanout, { topic: t, root });
1023
1222
  const onPayload = (payload) => {
1024
1223
  let dm;
@@ -1160,7 +1359,7 @@ export class TopicControlPlane extends DirectStream {
1160
1359
  const lastUsedAt = Date.now();
1161
1360
  if (pin)
1162
1361
  this.pinnedShards.add(t);
1163
- this.fanoutChannels.set(t, {
1362
+ const channelState = {
1164
1363
  root,
1165
1364
  channel,
1166
1365
  join,
@@ -1168,15 +1367,19 @@ export class TopicControlPlane extends DirectStream {
1168
1367
  onUnicast,
1169
1368
  ephemeral: wantEphemeral,
1170
1369
  lastUsedAt,
1171
- });
1370
+ };
1371
+ this.fanoutChannels.set(t, channelState);
1172
1372
  join.catch(() => {
1173
- this.fanoutChannels.delete(t);
1373
+ if (this.fanoutChannels.get(t) === channelState) {
1374
+ this.fanoutChannels.delete(t);
1375
+ }
1174
1376
  });
1175
1377
  join
1176
1378
  .then(() => {
1177
1379
  const st = this.fanoutChannels.get(t);
1178
- if (st?.ephemeral)
1380
+ if (st === channelState && st.ephemeral) {
1179
1381
  this.scheduleFanoutIdleClose(t);
1382
+ }
1180
1383
  })
1181
1384
  .catch(() => {
1182
1385
  // ignore
@@ -1185,6 +1388,7 @@ export class TopicControlPlane extends DirectStream {
1185
1388
  this.evictEphemeralFanoutChannels(t);
1186
1389
  }
1187
1390
  await withAbort(join, options?.signal);
1391
+ this.assertTopicControlPlaneActive(lifecycleRevision);
1188
1392
  }
1189
1393
  async closeFanoutChannel(shardTopic, options) {
1190
1394
  const t = shardTopic.toString();
@@ -1227,10 +1431,14 @@ export class TopicControlPlane extends DirectStream {
1227
1431
  const joins = [];
1228
1432
  for (let i = 0; i < this.shardCount; i++) {
1229
1433
  const shardTopic = `${this.shardTopicPrefix}${i}`;
1230
- const root = await this.resolveShardRoot(shardTopic);
1231
- if (root !== this.publicKeyHash)
1434
+ const resolved = await this.resolveShardRootState(shardTopic);
1435
+ if (resolved.root !== this.publicKeyHash)
1232
1436
  continue;
1233
- joins.push(this.ensureFanoutChannel(shardTopic, { pin: true, root }));
1437
+ joins.push(this.ensureFanoutChannel(shardTopic, {
1438
+ pin: true,
1439
+ root: resolved.root,
1440
+ rootCandidateGeneration: resolved.candidateGeneration,
1441
+ }));
1234
1442
  }
1235
1443
  await Promise.all(joins);
1236
1444
  }
@@ -1932,6 +2140,16 @@ export class TopicControlPlane extends DirectStream {
1932
2140
  }
1933
2141
  async processDirectPubSubMessage(input) {
1934
2142
  const { pubsubMessage, message, from, stream } = input;
2143
+ const isDirectRootControl = pubsubMessage instanceof TopicRootCandidates ||
2144
+ pubsubMessage instanceof TopicRootQuery ||
2145
+ pubsubMessage instanceof TopicRootQueryResponse;
2146
+ const directRootSigner = isDirectRootControl
2147
+ ? message.header.signatures?.publicKeys[0]
2148
+ : undefined;
2149
+ if (isDirectRootControl &&
2150
+ (!directRootSigner || !directRootSigner.equals(stream.publicKey))) {
2151
+ return;
2152
+ }
1935
2153
  if (pubsubMessage instanceof TopicRootCandidates) {
1936
2154
  // Used only to converge deterministic shard-root candidates in auto mode.
1937
2155
  this.mergeAutoTopicRootCandidatesFromPeer(pubsubMessage.candidates);
@@ -1947,7 +2165,8 @@ export class TopicControlPlane extends DirectStream {
1947
2165
  return;
1948
2166
  }
1949
2167
  if (pubsubMessage instanceof TopicRootQueryResponse) {
1950
- this.resolvePendingTopicRootQuery(pubsubMessage);
2168
+ const senderHash = directRootSigner.hashcode();
2169
+ this.resolvePendingTopicRootQuery(pubsubMessage, senderHash);
1951
2170
  return;
1952
2171
  }
1953
2172
  if (pubsubMessage instanceof Subscribe) {