@peerbit/pubsub 5.3.9 → 5.3.10
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.
- package/dist/src/index.d.ts +15 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +256 -82
- package/dist/src/index.js.map +1 -1
- package/dist/src/topic-root-control-plane.d.ts +11 -8
- package/dist/src/topic-root-control-plane.d.ts.map +1 -1
- package/dist/src/topic-root-control-plane.js +51 -15
- package/dist/src/topic-root-control-plane.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +356 -109
- package/src/topic-root-control-plane.ts +89 -15
package/dist/src/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { type PubSub, type PubSubEvents, SubscriptionData } from "@peerbit/pubsu
|
|
|
4
4
|
import { DirectStream, type DirectStreamComponents, type DirectStreamOptions, type PeerStreams } from "@peerbit/stream";
|
|
5
5
|
import { AcknowledgeDelivery, DataMessage, type ExpiresAtOptions, type IdOptions, type PriorityOptions, type ResponsePriorityOptions, type RouteHint, SilentDelivery, type WithExtraSigners } from "@peerbit/stream-interface";
|
|
6
6
|
import type { FanoutTree, FanoutTreeChannelOptions, FanoutTreeJoinOptions } from "./fanout-tree.js";
|
|
7
|
-
import { TopicRootControlPlane } from "./topic-root-control-plane.js";
|
|
7
|
+
import { TopicRootControlPlane, type TopicRootResolutionOptions } from "./topic-root-control-plane.js";
|
|
8
8
|
export * from "./fanout-tree.js";
|
|
9
9
|
export * as fanoutWire from "./fanout-tree-codec.js";
|
|
10
10
|
export * as fanoutParentUpgrade from "./fanout-tree-parent-upgrade.js";
|
|
@@ -153,10 +153,15 @@ export declare class TopicControlPlane extends DirectStream<PubSubEvents> implem
|
|
|
153
153
|
private readonly fanoutPublishMaxEphemeralChannels;
|
|
154
154
|
private autoTopicRootCandidates;
|
|
155
155
|
private autoTopicRootCandidateSet?;
|
|
156
|
+
private pendingAutoTopicRootCandidates?;
|
|
157
|
+
private autoTopicRootCandidateUpdateTimer?;
|
|
156
158
|
private reconcileShardOverlaysInFlight?;
|
|
157
159
|
private reconcileShardOverlaysDirty;
|
|
158
160
|
private topicControlPlaneStopping;
|
|
159
161
|
private topicControlPlaneLifecycleRevision;
|
|
162
|
+
private topicRootResolutionAbortController;
|
|
163
|
+
private topicRootCandidateResolutionGeneration?;
|
|
164
|
+
private topicRootCandidateResolutionAbortController;
|
|
160
165
|
private hostOwnedShardRootsInFlight?;
|
|
161
166
|
private hostOwnedShardRootsDirty;
|
|
162
167
|
private autoCandidatesBroadcastTimers;
|
|
@@ -194,6 +199,11 @@ export declare class TopicControlPlane extends DirectStream<PubSubEvents> implem
|
|
|
194
199
|
private ensureAutoCandidatesGossipInterval;
|
|
195
200
|
private sendAutoTopicRootCandidates;
|
|
196
201
|
private mergeAutoTopicRootCandidatesFromPeer;
|
|
202
|
+
private queueAutoTopicRootCandidateUpdate;
|
|
203
|
+
private applyAutoTopicRootCandidates;
|
|
204
|
+
private scheduleAutoTopicRootCandidateUpdateCooldown;
|
|
205
|
+
private flushPendingAutoTopicRootCandidateUpdate;
|
|
206
|
+
private clearAutoTopicRootCandidateUpdateSchedule;
|
|
197
207
|
private scheduleHostOwnedShardRoots;
|
|
198
208
|
private scheduleReconcileShardOverlays;
|
|
199
209
|
private reconcileShardOverlays;
|
|
@@ -221,9 +231,11 @@ export declare class TopicControlPlane extends DirectStream<PubSubEvents> implem
|
|
|
221
231
|
private decodePubSubMessage;
|
|
222
232
|
private getTopicRootCandidateGeneration;
|
|
223
233
|
private assertTopicControlPlaneActive;
|
|
234
|
+
private syncTopicRootCandidateResolutionSignal;
|
|
235
|
+
private withTopicRootCandidateResolution;
|
|
224
236
|
private normalizePeerTopicRootState;
|
|
225
237
|
private resolveTopicRootState;
|
|
226
|
-
resolveTopicRoot(topic: string): Promise<string | undefined>;
|
|
238
|
+
resolveTopicRoot(topic: string, options?: TopicRootResolutionOptions): Promise<string | undefined>;
|
|
227
239
|
private resolveShardRootState;
|
|
228
240
|
private getConnectedTopicRootTrackers;
|
|
229
241
|
private nextTopicRootRequestIdValue;
|
|
@@ -236,7 +248,7 @@ export declare class TopicControlPlane extends DirectStream<PubSubEvents> implem
|
|
|
236
248
|
private ensureFanoutChannel;
|
|
237
249
|
private ensureFanoutChannelOnce;
|
|
238
250
|
private closeFanoutChannel;
|
|
239
|
-
hostShardRootsNow(): Promise<void>;
|
|
251
|
+
hostShardRootsNow(options?: TopicRootResolutionOptions): Promise<void>;
|
|
240
252
|
subscribe(topic: string): Promise<void>;
|
|
241
253
|
private _subscribe;
|
|
242
254
|
unsubscribe(topic: string, options?: {
|
package/dist/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,UAAU,EACf,KAAK,MAAM,IAAI,YAAY,EAC3B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,aAAa,EAA0B,MAAM,iBAAiB,CAAC;AAExE,OAAO,EAON,KAAK,MAAM,EAEX,KAAK,YAAY,EAIjB,gBAAgB,EAShB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACN,YAAY,EACZ,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAEhB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAEN,mBAAmB,EAEnB,WAAW,EAEX,KAAK,gBAAgB,EACrB,KAAK,SAAS,EAGd,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,SAAS,EACd,cAAc,EACd,KAAK,gBAAgB,EAOrB,MAAM,2BAA2B,CAAC;AASnC,OAAO,KAAK,EACX,UAAU,EACV,wBAAwB,EAExB,qBAAqB,EACrB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,UAAU,EACf,KAAK,MAAM,IAAI,YAAY,EAC3B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,aAAa,EAA0B,MAAM,iBAAiB,CAAC;AAExE,OAAO,EAON,KAAK,MAAM,EAEX,KAAK,YAAY,EAIjB,gBAAgB,EAShB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACN,YAAY,EACZ,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAEhB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAEN,mBAAmB,EAEnB,WAAW,EAEX,KAAK,gBAAgB,EACrB,KAAK,SAAS,EAGd,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,SAAS,EACd,cAAc,EACd,KAAK,gBAAgB,EAOrB,MAAM,2BAA2B,CAAC;AASnC,OAAO,KAAK,EACX,UAAU,EACV,wBAAwB,EAExB,qBAAqB,EACrB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACN,qBAAqB,EACrB,KAAK,0BAA0B,EAC/B,MAAM,+BAA+B,CAAC;AAEvC,cAAc,kBAAkB,CAAC;AAIjC,OAAO,KAAK,UAAU,MAAM,wBAAwB,CAAC;AACrD,OAAO,KAAK,mBAAmB,MAAM,iCAAiC,CAAC;AACvE,cAAc,qBAAqB,CAAC;AACpC,cAAc,+BAA+B,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,eAAO,MAAM,MAAM,oCAAoD,CAAC;AAgIxE,MAAM,MAAM,wBAAwB,GAAG,mBAAmB,GAAG;IAC5D,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,qBAAqB,CAAC,EAAE,qBAAqB,CAAC;IAC9C;;OAEG;IACH,MAAM,EAAE,UAAU,CAAC;IACnB;;OAEG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,wBAAwB,EAAE,MAAM,CAAC,CAAC,CAAC;IAChE;;OAEG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,wBAAwB,EAAE,MAAM,CAAC,CAAC,CAAC;IACpE;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,wBAAwB,EAAE,MAAM,CAAC,CAAC,CAAC;IACpE;;OAEG;IACH,UAAU,CAAC,EAAE,qBAAqB,CAAC;IACnC;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,8BAA8B,CAAC,EAAE,OAAO,CAAC;IACzC;;;;;OAKG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC;;;;OAIG;IACH,iCAAiC,CAAC,EAAE,MAAM,CAAC;IAC3C;;;;;OAKG;IACH,yBAAyB,CAAC,EAAE,MAAM,CAAC;CACnC,CAAC;AAUF,MAAM,MAAM,2BAA2B,GAAG,sBAAsB,CAAC;AAEjE,MAAM,MAAM,MAAM,GAAG,YAAY,GAAG,aAAa,CAAC;AAElD;;;;;;GAMG;AACH,MAAM,MAAM,gCAAgC,GAAG,QAAQ,CAAC;IACvD,MAAM,EAAE,QAAQ,CAAC;QAChB,IAAI,EAAE,QAAQ,CAAC;YAAE,cAAc,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAC3C,IAAI,EAAE,QAAQ,CAAC;YAAE,cAAc,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KAC3C,CAAC,CAAC;CACH,CAAC,CAAC;AAWH;;GAEG;AACH,qBAAa,iBACZ,SAAQ,YAAY,CAAC,YAAY,CACjC,YAAW,MAAM;IAGV,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAEnD,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IAEtC,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAEvD,OAAO,CAAC,oBAAoB,CAAc;IAC1C;;;;;;OAMG;IACH,OAAO,CAAC,0BAA0B,CAOpB;IACP,wBAAwB,EAAE,GAAG,CACnC,MAAM,EACN,GAAG,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CACnD,CAAa;IACP,0BAA0B,EAAE,OAAO,CAAC;IAC3C,SAAgB,qBAAqB,EAAE,qBAAqB,CAAC;IAC7D,SAAgB,yBAAyB,EAAE,MAAM,CAAC;IAClD,SAAgB,MAAM,EAAE,UAAU,CAAC;IAOnC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAmB;IAEvD,OAAO,CAAC,2BAA2B,CAAiC;IACpE,OAAO,CAAC,6BAA6B,CAAiC;IAEtE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAU;IACrC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA+D;IAC9F,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA6B;IAC7D,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA6B;IAC5D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAqB;IAElD,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAGvC;IACF,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAGvC;IACF,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAwB;IAC3D,OAAO,CAAC,QAAQ,CAAC,8BAA8B,CAAU;IACzD,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAS;IAClD,OAAO,CAAC,QAAQ,CAAC,iCAAiC,CAAS;IAK3D,OAAO,CAAC,uBAAuB,CAAS;IACxC,OAAO,CAAC,yBAAyB,CAAC,CAAc;IAChD,OAAO,CAAC,8BAA8B,CAAC,CAAW;IAClD,OAAO,CAAC,iCAAiC,CAAC,CAAgC;IAC1E,OAAO,CAAC,8BAA8B,CAAC,CAAgB;IACvD,OAAO,CAAC,2BAA2B,CAAS;IAC5C,OAAO,CAAC,yBAAyB,CAAS;IAC1C,OAAO,CAAC,kCAAkC,CAAK;IAC/C,OAAO,CAAC,kCAAkC,CAAyB;IACnE,OAAO,CAAC,sCAAsC,CAAC,CAAS;IACxD,OAAO,CAAC,2CAA2C,CAAyB;IAC5E,OAAO,CAAC,2BAA2B,CAAC,CAAgB;IACpD,OAAO,CAAC,wBAAwB,CAAS;IACzC,OAAO,CAAC,6BAA6B,CACjC;IACJ,OAAO,CAAC,4BAA4B,CAAC,CAAiC;IACtE,OAAO,CAAC,yBAAyB,CAAK;IACtC,OAAO,CAAC,wBAAwB,CAAC,CAEvB;IAEV,OAAO,CAAC,cAAc,CAYlB;IACJ,OAAO,CAAC,2BAA2B,CAS/B;IACJ,OAAO,CAAC,uBAAuB,CAQ3B;IACJ,OAAO,CAAC,oBAAoB,CAAK;gBAGhC,UAAU,EAAE,2BAA2B,EACvC,KAAK,CAAC,EAAE,wBAAwB;IAmGjC;;OAEG;IACI,kBAAkB,IAAI,gCAAgC;IAmB7D;;;;;;;;;OASG;IACI,sBAAsB,CAAC,UAAU,EAAE,MAAM,EAAE;IAyB5B,KAAK;IA0BL,IAAI;IA8EJ,eAAe,CACpC,MAAM,EAAE,YAAY,EACpB,UAAU,EAAE,UAAU;IAoBP,OAAO,CACtB,MAAM,EAAE,YAAY,EACpB,SAAS,EAAE,aAAa,EACxB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACZ,WAAW;IASd,OAAO,CAAC,yDAAyD;IA2BjE,OAAO,CAAC,kCAAkC;IAW1C,OAAO,CAAC,gCAAgC;IAqBxC,OAAO,CAAC,wCAAwC;IA4BhD,OAAO,CAAC,kCAAkC;YAsB5B,2BAA2B;IAiBzC,OAAO,CAAC,oCAAoC;IAK5C,OAAO,CAAC,iCAAiC;IA+BzC,OAAO,CAAC,4BAA4B;IASpC,OAAO,CAAC,4CAA4C;IAUpD,OAAO,CAAC,wCAAwC;IAuBhD,OAAO,CAAC,yCAAyC;IAQjD,OAAO,CAAC,2BAA2B;IAyBnC,OAAO,CAAC,8BAA8B;YA+CxB,sBAAsB;IAiCpC,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,YAAY;IAcpB,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,qBAAqB;IAiB7B,OAAO,CAAC,sBAAsB;IAyB9B,OAAO,CAAC,oBAAoB;IAS5B,OAAO,CAAC,uBAAuB;IAiB/B,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,4BAA4B;IAwBpC,OAAO,CAAC,yBAAyB;IAejC;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IA+C3B;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;IAiD3B,OAAO,CAAC,+BAA+B;IAOvC,OAAO,CAAC,6BAA6B;IAWrC,OAAO,CAAC,sCAAsC;YAiBhC,gCAAgC;IA4C9C,OAAO,CAAC,2BAA2B;YAiBrB,qBAAqB;IAkDtB,gBAAgB,CAC5B,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,0BAA0B,GAClC,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;YAYhB,qBAAqB;IAgDnC,OAAO,CAAC,6BAA6B;IAYrC,OAAO,CAAC,2BAA2B;YAYrB,wBAAwB;IAetC,OAAO,CAAC,4BAA4B;YAkBtB,sBAAsB;YAiEtB,sBAAsB;YAsCtB,yBAAyB;YAiDzB,4BAA4B;YAiC5B,mBAAmB;YA6GnB,uBAAuB;YA8QvB,kBAAkB;IAgCnB,iBAAiB,CAAC,OAAO,CAAC,EAAE,0BAA0B;IAiC7D,SAAS,CAAC,KAAK,EAAE,MAAM;YAQf,UAAU;IAkDlB,WAAW,CAChB,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE;QACT,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,IAAI,CAAC,EAAE,UAAU,CAAC;KAClB;YA0CY,oBAAoB;YAgDpB,uBAAuB;YA6DvB,8BAA8B;IA4B5C,OAAO,CAAC,uBAAuB;IAQ/B,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,EAAE,GAAG,SAAS;IAa1D;;;;OAIG;IACH,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS,EAAE;IA4B9D,kBAAkB,CACvB,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,EACxB,EAAE,CAAC,EAAE,aAAa,GAChB,OAAO,CAAC,IAAI,CAAC;IAuDV,OAAO,CACZ,IAAI,EAAE,UAAU,GAAG,SAAS,EAC5B,OAAO,CAAC,EAAE;QACT,MAAM,EAAE,MAAM,EAAE,CAAC;KACjB,GAAG;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG;QACzB,IAAI,CAAC,EAAE,cAAc,GAAG,mBAAmB,CAAC;KAC5C,GAAG,eAAe,GAClB,uBAAuB,GACvB,gBAAgB,GAChB,SAAS,GACT,gBAAgB,GAAG;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAC1C,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IA8HlC;;;;;;;;;;OAUG;IACG,qBAAqB,CAC1B,OAAO,EAAE,UAAU,EACnB,UAAU,EAAE;QACX,MAAM,EAAE,MAAM,EAAE,CAAC;KACjB,EACD,OAAO,EAAE;QACR,IAAI,EAAE,cAAc,GAAG,mBAAmB,CAAC;KAC3C,GAAG;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,eAAe,GACxC,uBAAuB,GACvB,gBAAgB,GAChB,SAAS,GACT,gBAAgB,GAAG;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAC1C,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IA+C3B,aAAa,CAAC,GAAG,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAWhD,iBAAiB,CAAC,aAAa,EAAE,MAAM;IA2BvD,OAAO,CAAC,wBAAwB;IA2BhC,OAAO,CAAC,gCAAgC;IAoCxC,OAAO,CAAC,mBAAmB;IA8B3B,OAAO,CAAC,yBAAyB;IA4DjC;;;OAGG;IACH,OAAO,CAAC,sBAAsB;IAa9B,OAAO,CAAC,2BAA2B;IAcnC;;;;;;;OAOG;IACH,OAAO,CAAC,uBAAuB;YAiCjB,wBAAwB;YA+BxB,4BAA4B;YA+B5B,0BAA0B;YAuI1B,yBAAyB;IA4MjB,aAAa,CAClC,IAAI,EAAE,aAAa,EACnB,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,WAAW,EACpB,UAAU,EAAE,MAAM;CAqFnB;AAED,eAAO,MAAM,kBAAkB,GAC9B,QAAQ;IAAE,QAAQ,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,EACxC,aACG,MAAM,GACN,MAAM,EAAE,GACR;IAAE,MAAM,EAAE,YAAY,CAAA;CAAE,GACxB;IAAE,MAAM,EAAE,YAAY,CAAA;CAAE,EAAE,GAC1B,MAAM,GACN,MAAM,EAAE,EACX,OAAO,MAAM,EACb,UAAU;IAAE,MAAM,CAAC,EAAE,WAAW,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,kBAiKpD,CAAC"}
|
package/dist/src/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import { Uint8ArrayList } from "uint8arraylist";
|
|
|
9
9
|
import { toUint8Array } from "./bytes.js";
|
|
10
10
|
import { debouncedAccumulatorSetCounter, } from "./debounced-set.js";
|
|
11
11
|
import { FanoutChannel } from "./fanout-channel.js";
|
|
12
|
-
import { TopicRootControlPlane } from "./topic-root-control-plane.js";
|
|
12
|
+
import { TopicRootControlPlane, } from "./topic-root-control-plane.js";
|
|
13
13
|
export * from "./fanout-tree.js";
|
|
14
14
|
// The complete /peerbit/fanout-tree/0.5.0 wire codec and the
|
|
15
15
|
// parent-upgrade decision module (the TS references the native rust-core
|
|
@@ -43,6 +43,7 @@ const withAbort = async (promise, signal) => {
|
|
|
43
43
|
if (!signal)
|
|
44
44
|
return promise;
|
|
45
45
|
if (signal.aborted) {
|
|
46
|
+
void promise.catch(() => { });
|
|
46
47
|
throw signal.reason ?? new AbortError("Operation was aborted");
|
|
47
48
|
}
|
|
48
49
|
return new Promise((resolve, reject) => {
|
|
@@ -68,6 +69,41 @@ const withAbort = async (promise, signal) => {
|
|
|
68
69
|
});
|
|
69
70
|
});
|
|
70
71
|
};
|
|
72
|
+
const throwIfAborted = (signal) => {
|
|
73
|
+
if (signal?.aborted) {
|
|
74
|
+
throw signal.reason ?? new AbortError("Operation was aborted");
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const linkAbortSignals = (signals) => {
|
|
78
|
+
const unique = [...new Set(signals.filter((signal) => signal !== undefined))];
|
|
79
|
+
if (unique.length === 1) {
|
|
80
|
+
return { signal: unique[0], clear: () => { } };
|
|
81
|
+
}
|
|
82
|
+
const controller = new AbortController();
|
|
83
|
+
const listeners = new Map();
|
|
84
|
+
const clear = () => {
|
|
85
|
+
for (const [signal, listener] of listeners) {
|
|
86
|
+
signal.removeEventListener("abort", listener);
|
|
87
|
+
}
|
|
88
|
+
listeners.clear();
|
|
89
|
+
};
|
|
90
|
+
const abortFrom = (signal) => {
|
|
91
|
+
clear();
|
|
92
|
+
controller.abort(signal.reason ?? new AbortError("Topic-root resolution aborted"));
|
|
93
|
+
};
|
|
94
|
+
const aborted = unique.find((signal) => signal.aborted);
|
|
95
|
+
if (aborted) {
|
|
96
|
+
abortFrom(aborted);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
for (const signal of unique) {
|
|
100
|
+
const listener = () => abortFrom(signal);
|
|
101
|
+
listeners.set(signal, listener);
|
|
102
|
+
signal.addEventListener("abort", listener, { once: true });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return { signal: controller.signal, clear };
|
|
106
|
+
};
|
|
71
107
|
const SUBSCRIBER_CACHE_MAX_ENTRIES_HARD_CAP = 100_000;
|
|
72
108
|
const SUBSCRIBER_CACHE_DEFAULT_MAX_ENTRIES = 4_096;
|
|
73
109
|
const DEFAULT_FANOUT_PUBLISH_IDLE_CLOSE_MS = 60_000;
|
|
@@ -75,6 +111,7 @@ const DEFAULT_FANOUT_PUBLISH_MAX_EPHEMERAL_CHANNELS = 64;
|
|
|
75
111
|
const DEFAULT_PUBSUB_SHARD_COUNT = 256;
|
|
76
112
|
const PUBSUB_SHARD_COUNT_HARD_CAP = 16_384;
|
|
77
113
|
const DEFAULT_PUBSUB_SHARD_TOPIC_PREFIX = "/peerbit/pubsub-shard/1/";
|
|
114
|
+
const AUTO_TOPIC_ROOT_CANDIDATE_UPDATE_COOLDOWN_MS = 2_000;
|
|
78
115
|
const sameCandidates = (left, right) => left.length === right.length &&
|
|
79
116
|
left.every((candidate, index) => candidate === right[index]);
|
|
80
117
|
// Topic-root queries may need to wait for the responder to finish opening an
|
|
@@ -148,10 +185,15 @@ export class TopicControlPlane extends DirectStream {
|
|
|
148
185
|
// This keeps small ad-hoc networks working without explicit bootstraps.
|
|
149
186
|
autoTopicRootCandidates = false;
|
|
150
187
|
autoTopicRootCandidateSet;
|
|
188
|
+
pendingAutoTopicRootCandidates;
|
|
189
|
+
autoTopicRootCandidateUpdateTimer;
|
|
151
190
|
reconcileShardOverlaysInFlight;
|
|
152
191
|
reconcileShardOverlaysDirty = false;
|
|
153
192
|
topicControlPlaneStopping = false;
|
|
154
193
|
topicControlPlaneLifecycleRevision = 0;
|
|
194
|
+
topicRootResolutionAbortController = new AbortController();
|
|
195
|
+
topicRootCandidateResolutionGeneration;
|
|
196
|
+
topicRootCandidateResolutionAbortController = new AbortController();
|
|
155
197
|
hostOwnedShardRootsInFlight;
|
|
156
198
|
hostOwnedShardRootsDirty = false;
|
|
157
199
|
autoCandidatesBroadcastTimers = [];
|
|
@@ -250,6 +292,7 @@ export class TopicControlPlane extends DirectStream {
|
|
|
250
292
|
// Disable auto mode and stop its background gossip/timers.
|
|
251
293
|
this.autoTopicRootCandidates = false;
|
|
252
294
|
this.autoTopicRootCandidateSet = undefined;
|
|
295
|
+
this.clearAutoTopicRootCandidateUpdateSchedule();
|
|
253
296
|
for (const t of this.autoCandidatesBroadcastTimers)
|
|
254
297
|
clearTimeout(t);
|
|
255
298
|
this.autoCandidatesBroadcastTimers = [];
|
|
@@ -269,6 +312,9 @@ export class TopicControlPlane extends DirectStream {
|
|
|
269
312
|
}
|
|
270
313
|
async start() {
|
|
271
314
|
this.topicControlPlaneStopping = false;
|
|
315
|
+
if (this.topicRootResolutionAbortController.signal.aborted) {
|
|
316
|
+
this.topicRootResolutionAbortController = new AbortController();
|
|
317
|
+
}
|
|
272
318
|
await this.fanout.start();
|
|
273
319
|
this._onFanoutPeerUnreachable =
|
|
274
320
|
this._onFanoutPeerUnreachable ||
|
|
@@ -287,6 +333,8 @@ export class TopicControlPlane extends DirectStream {
|
|
|
287
333
|
}
|
|
288
334
|
async stop() {
|
|
289
335
|
this.topicControlPlaneStopping = true;
|
|
336
|
+
this.topicRootResolutionAbortController.abort(new AbortError("topic control plane stopped"));
|
|
337
|
+
this.clearAutoTopicRootCandidateUpdateSchedule();
|
|
290
338
|
this.topicControlPlaneLifecycleRevision += 1;
|
|
291
339
|
this.reconcileShardOverlaysDirty = false;
|
|
292
340
|
for (const opening of this.ensureFanoutChannelInFlight.values()) {
|
|
@@ -398,6 +446,7 @@ export class TopicControlPlane extends DirectStream {
|
|
|
398
446
|
// intact and reconcile shard overlays under the new mapping.
|
|
399
447
|
this.autoTopicRootCandidates = false;
|
|
400
448
|
this.autoTopicRootCandidateSet = undefined;
|
|
449
|
+
this.clearAutoTopicRootCandidateUpdateSchedule();
|
|
401
450
|
this.shardRootCache.clear();
|
|
402
451
|
// Ensure we host any shard roots we're now responsible for. This is important
|
|
403
452
|
// in tests where candidates may be configured before protocol streams have
|
|
@@ -413,29 +462,7 @@ export class TopicControlPlane extends DirectStream {
|
|
|
413
462
|
if (!isCanonicalTopicRootCandidate(peerHash) ||
|
|
414
463
|
peerHash === this.publicKeyHash)
|
|
415
464
|
return;
|
|
416
|
-
|
|
417
|
-
return;
|
|
418
|
-
const current = this.topicRootControlPlane.getTopicRootCandidates();
|
|
419
|
-
const managed = this.autoTopicRootCandidateSet;
|
|
420
|
-
if (current.includes(peerHash))
|
|
421
|
-
return;
|
|
422
|
-
managed?.add(peerHash);
|
|
423
|
-
const next = this.normalizeAutoTopicRootCandidates(managed ? [...managed] : [...current, peerHash]);
|
|
424
|
-
this.autoTopicRootCandidateSet = new Set(next);
|
|
425
|
-
if (sameCandidates(current, next))
|
|
426
|
-
return;
|
|
427
|
-
this.topicRootControlPlane.setTopicRootCandidates(next);
|
|
428
|
-
this.shardRootCache.clear();
|
|
429
|
-
this.scheduleReconcileShardOverlays();
|
|
430
|
-
// In auto-candidate mode, shard roots are selected deterministically across
|
|
431
|
-
// *all* connected peers (not just those currently subscribed to a shard).
|
|
432
|
-
// That means a peer can be selected as root for shards it isn't using yet.
|
|
433
|
-
// Ensure we proactively host the shard roots we're responsible for so other
|
|
434
|
-
// peers can join without timing out in small ad-hoc networks.
|
|
435
|
-
this.scheduleHostOwnedShardRoots();
|
|
436
|
-
// Share the updated candidate set so other peers converge on the same
|
|
437
|
-
// deterministic mapping even in partially connected topologies.
|
|
438
|
-
this.scheduleAutoTopicRootCandidatesBroadcast();
|
|
465
|
+
this.queueAutoTopicRootCandidateUpdate([peerHash]);
|
|
439
466
|
}
|
|
440
467
|
normalizeAutoTopicRootCandidates(candidates) {
|
|
441
468
|
const canonicalCandidates = candidates.filter(isCanonicalTopicRootCandidate);
|
|
@@ -520,27 +547,78 @@ export class TopicControlPlane extends DirectStream {
|
|
|
520
547
|
mergeAutoTopicRootCandidatesFromPeer(candidates) {
|
|
521
548
|
if (!this.autoTopicRootCandidates)
|
|
522
549
|
return false;
|
|
550
|
+
return this.queueAutoTopicRootCandidateUpdate(candidates);
|
|
551
|
+
}
|
|
552
|
+
queueAutoTopicRootCandidateUpdate(candidates) {
|
|
553
|
+
if (!this.autoTopicRootCandidates ||
|
|
554
|
+
this.stopping ||
|
|
555
|
+
this.topicControlPlaneStopping)
|
|
556
|
+
return false;
|
|
523
557
|
if (this.maybeDisableAutoTopicRootCandidatesIfExternallyConfigured())
|
|
524
558
|
return false;
|
|
525
559
|
const managed = this.autoTopicRootCandidateSet;
|
|
526
560
|
if (!managed)
|
|
527
561
|
return false;
|
|
528
|
-
const before = this.
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
}
|
|
534
|
-
const next = this.normalizeAutoTopicRootCandidates([...managed]);
|
|
535
|
-
this.autoTopicRootCandidateSet = new Set(next);
|
|
562
|
+
const before = this.pendingAutoTopicRootCandidates ?? [...managed];
|
|
563
|
+
const next = this.normalizeAutoTopicRootCandidates([
|
|
564
|
+
...before,
|
|
565
|
+
...candidates,
|
|
566
|
+
]);
|
|
536
567
|
if (sameCandidates(before, next))
|
|
537
568
|
return false;
|
|
569
|
+
if (this.autoTopicRootCandidateUpdateTimer) {
|
|
570
|
+
this.pendingAutoTopicRootCandidates = next;
|
|
571
|
+
return true;
|
|
572
|
+
}
|
|
573
|
+
this.scheduleAutoTopicRootCandidateUpdateCooldown();
|
|
574
|
+
this.applyAutoTopicRootCandidates(next);
|
|
575
|
+
return true;
|
|
576
|
+
}
|
|
577
|
+
applyAutoTopicRootCandidates(next) {
|
|
578
|
+
this.autoTopicRootCandidateSet = new Set(next);
|
|
538
579
|
this.topicRootControlPlane.setTopicRootCandidates(next);
|
|
539
580
|
this.shardRootCache.clear();
|
|
540
581
|
this.scheduleReconcileShardOverlays();
|
|
541
582
|
this.scheduleHostOwnedShardRoots();
|
|
542
583
|
this.scheduleAutoTopicRootCandidatesBroadcast();
|
|
543
|
-
|
|
584
|
+
}
|
|
585
|
+
scheduleAutoTopicRootCandidateUpdateCooldown() {
|
|
586
|
+
if (this.autoTopicRootCandidateUpdateTimer)
|
|
587
|
+
return;
|
|
588
|
+
const timer = setTimeout(() => {
|
|
589
|
+
if (this.autoTopicRootCandidateUpdateTimer !== timer)
|
|
590
|
+
return;
|
|
591
|
+
this.flushPendingAutoTopicRootCandidateUpdate();
|
|
592
|
+
}, AUTO_TOPIC_ROOT_CANDIDATE_UPDATE_COOLDOWN_MS);
|
|
593
|
+
timer.unref?.();
|
|
594
|
+
this.autoTopicRootCandidateUpdateTimer = timer;
|
|
595
|
+
}
|
|
596
|
+
flushPendingAutoTopicRootCandidateUpdate() {
|
|
597
|
+
if (this.autoTopicRootCandidateUpdateTimer) {
|
|
598
|
+
clearTimeout(this.autoTopicRootCandidateUpdateTimer);
|
|
599
|
+
this.autoTopicRootCandidateUpdateTimer = undefined;
|
|
600
|
+
}
|
|
601
|
+
const next = this.pendingAutoTopicRootCandidates;
|
|
602
|
+
this.pendingAutoTopicRootCandidates = undefined;
|
|
603
|
+
if (!next)
|
|
604
|
+
return;
|
|
605
|
+
if (!this.autoTopicRootCandidates ||
|
|
606
|
+
this.stopping ||
|
|
607
|
+
this.topicControlPlaneStopping)
|
|
608
|
+
return;
|
|
609
|
+
if (this.maybeDisableAutoTopicRootCandidatesIfExternallyConfigured())
|
|
610
|
+
return;
|
|
611
|
+
// The trailing update starts its own fixed window so another inbound update
|
|
612
|
+
// cannot create a second candidate generation immediately afterwards.
|
|
613
|
+
this.scheduleAutoTopicRootCandidateUpdateCooldown();
|
|
614
|
+
this.applyAutoTopicRootCandidates(next);
|
|
615
|
+
}
|
|
616
|
+
clearAutoTopicRootCandidateUpdateSchedule() {
|
|
617
|
+
if (this.autoTopicRootCandidateUpdateTimer) {
|
|
618
|
+
clearTimeout(this.autoTopicRootCandidateUpdateTimer);
|
|
619
|
+
this.autoTopicRootCandidateUpdateTimer = undefined;
|
|
620
|
+
}
|
|
621
|
+
this.pendingAutoTopicRootCandidates = undefined;
|
|
544
622
|
}
|
|
545
623
|
scheduleHostOwnedShardRoots() {
|
|
546
624
|
this.hostOwnedShardRootsDirty = true;
|
|
@@ -574,6 +652,7 @@ export class TopicControlPlane extends DirectStream {
|
|
|
574
652
|
}
|
|
575
653
|
scheduleReconcileShardOverlays() {
|
|
576
654
|
const candidateGeneration = this.getTopicRootCandidateGeneration();
|
|
655
|
+
this.syncTopicRootCandidateResolutionSignal(candidateGeneration);
|
|
577
656
|
for (const opening of this.ensureFanoutChannelInFlight.values()) {
|
|
578
657
|
if (opening.candidateGeneration !== candidateGeneration) {
|
|
579
658
|
opening.abortController.abort(new AbortError("topic root candidates changed"));
|
|
@@ -885,6 +964,53 @@ export class TopicControlPlane extends DirectStream {
|
|
|
885
964
|
throw new NotStartedError();
|
|
886
965
|
}
|
|
887
966
|
}
|
|
967
|
+
syncTopicRootCandidateResolutionSignal(candidateGeneration = this.getTopicRootCandidateGeneration()) {
|
|
968
|
+
if (this.topicRootCandidateResolutionGeneration === undefined) {
|
|
969
|
+
this.topicRootCandidateResolutionGeneration = candidateGeneration;
|
|
970
|
+
}
|
|
971
|
+
else if (candidateGeneration !== this.topicRootCandidateResolutionGeneration) {
|
|
972
|
+
this.topicRootCandidateResolutionAbortController.abort(new AbortError("topic root candidates changed"));
|
|
973
|
+
this.topicRootCandidateResolutionAbortController = new AbortController();
|
|
974
|
+
this.topicRootCandidateResolutionGeneration = candidateGeneration;
|
|
975
|
+
}
|
|
976
|
+
return this.topicRootCandidateResolutionAbortController.signal;
|
|
977
|
+
}
|
|
978
|
+
async withTopicRootCandidateResolution(operation, terminalSignals = []) {
|
|
979
|
+
for (;;) {
|
|
980
|
+
for (const signal of terminalSignals)
|
|
981
|
+
throwIfAborted(signal);
|
|
982
|
+
const candidateGeneration = this.getTopicRootCandidateGeneration();
|
|
983
|
+
const candidateSignal = this.syncTopicRootCandidateResolutionSignal(candidateGeneration);
|
|
984
|
+
const linked = linkAbortSignals([...terminalSignals, candidateSignal]);
|
|
985
|
+
try {
|
|
986
|
+
const result = await operation({
|
|
987
|
+
candidateGeneration,
|
|
988
|
+
signal: linked.signal,
|
|
989
|
+
});
|
|
990
|
+
for (const signal of terminalSignals)
|
|
991
|
+
throwIfAborted(signal);
|
|
992
|
+
if (candidateSignal.aborted ||
|
|
993
|
+
candidateGeneration !== this.getTopicRootCandidateGeneration()) {
|
|
994
|
+
continue;
|
|
995
|
+
}
|
|
996
|
+
return result;
|
|
997
|
+
}
|
|
998
|
+
catch (error) {
|
|
999
|
+
for (const signal of terminalSignals) {
|
|
1000
|
+
if (signal?.aborted)
|
|
1001
|
+
throw signal.reason ?? error;
|
|
1002
|
+
}
|
|
1003
|
+
if (candidateSignal.aborted ||
|
|
1004
|
+
candidateGeneration !== this.getTopicRootCandidateGeneration()) {
|
|
1005
|
+
continue;
|
|
1006
|
+
}
|
|
1007
|
+
throw error;
|
|
1008
|
+
}
|
|
1009
|
+
finally {
|
|
1010
|
+
linked.clear();
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
888
1014
|
normalizePeerTopicRootState(topic, root) {
|
|
889
1015
|
const deterministic = this.topicRootControlPlane.resolveDeterministicTopicRoot(topic);
|
|
890
1016
|
if (this.autoTopicRootCandidates &&
|
|
@@ -895,12 +1021,13 @@ export class TopicControlPlane extends DirectStream {
|
|
|
895
1021
|
}
|
|
896
1022
|
return { root, authoritative: true };
|
|
897
1023
|
}
|
|
898
|
-
async resolveTopicRootState(topic) {
|
|
899
|
-
|
|
1024
|
+
async resolveTopicRootState(topic, options) {
|
|
1025
|
+
throwIfAborted(options?.signal);
|
|
1026
|
+
const tracked = await this.topicRootControlPlane.resolveTrackedTopicRoot(topic, options);
|
|
900
1027
|
if (tracked) {
|
|
901
1028
|
return { root: tracked, authoritative: true };
|
|
902
1029
|
}
|
|
903
|
-
const resolvedThroughPeers = await this.resolveTopicRootThroughPeers(topic);
|
|
1030
|
+
const resolvedThroughPeers = await this.resolveTopicRootThroughPeers(topic, options);
|
|
904
1031
|
if (resolvedThroughPeers) {
|
|
905
1032
|
// Unconfigured peer-query replies cannot override the locally
|
|
906
1033
|
// deterministic root for internal shards in auto mode. Roots, resolvers,
|
|
@@ -914,8 +1041,8 @@ export class TopicControlPlane extends DirectStream {
|
|
|
914
1041
|
this.autoTopicRootCandidates &&
|
|
915
1042
|
this.getConnectedTopicRootTrackers().length > 0) {
|
|
916
1043
|
for (let attempt = 0; attempt < 8; attempt++) {
|
|
917
|
-
await delay(150 * (attempt < 4 ? 1 : 2));
|
|
918
|
-
const retried = await this.resolveTopicRootThroughPeers(topic);
|
|
1044
|
+
await withAbort(delay(150 * (attempt < 4 ? 1 : 2), options), options?.signal);
|
|
1045
|
+
const retried = await this.resolveTopicRootThroughPeers(topic, options);
|
|
919
1046
|
if (retried) {
|
|
920
1047
|
return this.normalizePeerTopicRootState(topic, retried);
|
|
921
1048
|
}
|
|
@@ -926,12 +1053,16 @@ export class TopicControlPlane extends DirectStream {
|
|
|
926
1053
|
authoritative: false,
|
|
927
1054
|
};
|
|
928
1055
|
}
|
|
929
|
-
async resolveTopicRoot(topic) {
|
|
930
|
-
|
|
1056
|
+
async resolveTopicRoot(topic, options) {
|
|
1057
|
+
const lifecycleSignal = this.started && !this.stopping && !this.topicControlPlaneStopping
|
|
1058
|
+
? this.topicRootResolutionAbortController.signal
|
|
1059
|
+
: undefined;
|
|
1060
|
+
return this.withTopicRootCandidateResolution(async ({ signal }) => (await this.resolveTopicRootState(topic, { signal })).root, [lifecycleSignal, options?.signal]);
|
|
931
1061
|
}
|
|
932
|
-
async resolveShardRootState(shardTopic) {
|
|
1062
|
+
async resolveShardRootState(shardTopic, options) {
|
|
933
1063
|
const lifecycleRevision = this.topicControlPlaneLifecycleRevision;
|
|
934
1064
|
for (;;) {
|
|
1065
|
+
throwIfAborted(options?.signal);
|
|
935
1066
|
this.assertTopicControlPlaneActive(lifecycleRevision);
|
|
936
1067
|
// If someone configured topic-root candidates externally (e.g.
|
|
937
1068
|
// TestSession router selection or Peerbit.bootstrap) after this peer
|
|
@@ -945,7 +1076,7 @@ export class TopicControlPlane extends DirectStream {
|
|
|
945
1076
|
if (cached && (cached.authoritative || !hasConnectedTrackers)) {
|
|
946
1077
|
return { root: cached.root, candidateGeneration };
|
|
947
1078
|
}
|
|
948
|
-
const resolved = await this.resolveTopicRootState(shardTopic);
|
|
1079
|
+
const resolved = await this.resolveTopicRootState(shardTopic, options);
|
|
949
1080
|
this.assertTopicControlPlaneActive(lifecycleRevision);
|
|
950
1081
|
if (candidateGeneration !== this.getTopicRootCandidateGeneration()) {
|
|
951
1082
|
continue;
|
|
@@ -1005,36 +1136,57 @@ export class TopicControlPlane extends DirectStream {
|
|
|
1005
1136
|
pending.resolve(message.root);
|
|
1006
1137
|
return true;
|
|
1007
1138
|
}
|
|
1008
|
-
async queryTopicRootFromPeer(peer, topic, timeoutMs = DEFAULT_TOPIC_ROOT_QUERY_TIMEOUT_MS) {
|
|
1139
|
+
async queryTopicRootFromPeer(peer, topic, timeoutMs = DEFAULT_TOPIC_ROOT_QUERY_TIMEOUT_MS, signal) {
|
|
1140
|
+
throwIfAborted(signal);
|
|
1009
1141
|
if (!this.started || this.stopping)
|
|
1010
1142
|
return undefined;
|
|
1011
1143
|
const expectedPeerHash = peer.publicKey.hashcode();
|
|
1012
1144
|
const requestId = this.nextTopicRootRequestIdValue();
|
|
1013
1145
|
const responsePromise = new Promise((resolve) => {
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1146
|
+
let settled = false;
|
|
1147
|
+
const settle = (root) => {
|
|
1148
|
+
if (settled)
|
|
1149
|
+
return;
|
|
1150
|
+
settled = true;
|
|
1151
|
+
const pending = this.pendingTopicRootQueries.get(requestId);
|
|
1152
|
+
if (pending?.resolve === settle) {
|
|
1153
|
+
this.pendingTopicRootQueries.delete(requestId);
|
|
1154
|
+
}
|
|
1155
|
+
clearTimeout(timer);
|
|
1156
|
+
if (signal && onAbort) {
|
|
1157
|
+
signal.removeEventListener("abort", onAbort);
|
|
1158
|
+
}
|
|
1159
|
+
resolve(root);
|
|
1160
|
+
};
|
|
1161
|
+
const onAbort = signal ? () => settle(undefined) : undefined;
|
|
1162
|
+
const timer = setTimeout(() => settle(undefined), Math.max(1, Math.floor(timeoutMs)));
|
|
1018
1163
|
timer.unref?.();
|
|
1019
1164
|
this.pendingTopicRootQueries.set(requestId, {
|
|
1020
1165
|
expectedPeerHash,
|
|
1021
1166
|
topic,
|
|
1022
|
-
resolve,
|
|
1167
|
+
resolve: settle,
|
|
1023
1168
|
timer,
|
|
1024
1169
|
});
|
|
1170
|
+
if (signal && onAbort) {
|
|
1171
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1172
|
+
if (signal.aborted)
|
|
1173
|
+
onAbort();
|
|
1174
|
+
}
|
|
1025
1175
|
});
|
|
1026
1176
|
try {
|
|
1027
|
-
await this.sendDirectControlMessage(peer, new TopicRootQuery({ requestId, topic }));
|
|
1177
|
+
await withAbort(this.sendDirectControlMessage(peer, new TopicRootQuery({ requestId, topic })), signal);
|
|
1028
1178
|
}
|
|
1029
|
-
catch {
|
|
1179
|
+
catch (error) {
|
|
1030
1180
|
const pending = this.pendingTopicRootQueries.get(requestId);
|
|
1031
1181
|
if (pending) {
|
|
1032
1182
|
this.pendingTopicRootQueries.delete(requestId);
|
|
1033
1183
|
clearTimeout(pending.timer);
|
|
1034
1184
|
pending.resolve(undefined);
|
|
1035
1185
|
}
|
|
1186
|
+
if (signal?.aborted)
|
|
1187
|
+
throw error;
|
|
1036
1188
|
}
|
|
1037
|
-
return responsePromise;
|
|
1189
|
+
return withAbort(responsePromise, signal);
|
|
1038
1190
|
}
|
|
1039
1191
|
async confirmDirectShardRoot(shardTopic, root, candidateGeneration, lifecycleRevision, signal) {
|
|
1040
1192
|
this.assertTopicControlPlaneActive(lifecycleRevision);
|
|
@@ -1043,7 +1195,7 @@ export class TopicControlPlane extends DirectStream {
|
|
|
1043
1195
|
const rootPeer = this.peers.get(root);
|
|
1044
1196
|
if (!rootPeer)
|
|
1045
1197
|
return root;
|
|
1046
|
-
const confirmation = await withAbort(this.queryTopicRootFromPeer(rootPeer, shardTopic, DIRECT_SHARD_ROOT_CONFIRM_TIMEOUT_MS), signal);
|
|
1198
|
+
const confirmation = await withAbort(this.queryTopicRootFromPeer(rootPeer, shardTopic, DIRECT_SHARD_ROOT_CONFIRM_TIMEOUT_MS, signal), signal);
|
|
1047
1199
|
this.assertTopicControlPlaneActive(lifecycleRevision);
|
|
1048
1200
|
if (candidateGeneration !== this.getTopicRootCandidateGeneration()) {
|
|
1049
1201
|
return root;
|
|
@@ -1064,14 +1216,14 @@ export class TopicControlPlane extends DirectStream {
|
|
|
1064
1216
|
}
|
|
1065
1217
|
async resolveQueryableTopicRoot(topic) {
|
|
1066
1218
|
const lifecycleRevision = this.topicControlPlaneLifecycleRevision;
|
|
1067
|
-
|
|
1219
|
+
const lifecycleSignal = this.topicRootResolutionAbortController.signal;
|
|
1220
|
+
return this.withTopicRootCandidateResolution(async ({ candidateGeneration, signal }) => {
|
|
1221
|
+
throwIfAborted(signal);
|
|
1068
1222
|
this.assertTopicControlPlaneActive(lifecycleRevision);
|
|
1069
|
-
const
|
|
1070
|
-
|
|
1223
|
+
const root = await this.topicRootControlPlane.resolveCanonicalTopicRoot(topic, {
|
|
1224
|
+
signal,
|
|
1225
|
+
});
|
|
1071
1226
|
this.assertTopicControlPlaneActive(lifecycleRevision);
|
|
1072
|
-
if (rootCandidateGeneration !== this.getTopicRootCandidateGeneration()) {
|
|
1073
|
-
continue;
|
|
1074
|
-
}
|
|
1075
1227
|
if (root !== this.publicKeyHash) {
|
|
1076
1228
|
return root;
|
|
1077
1229
|
}
|
|
@@ -1082,34 +1234,37 @@ export class TopicControlPlane extends DirectStream {
|
|
|
1082
1234
|
await this.ensureFanoutChannel(topic, {
|
|
1083
1235
|
pin: true,
|
|
1084
1236
|
root,
|
|
1085
|
-
rootCandidateGeneration,
|
|
1237
|
+
rootCandidateGeneration: candidateGeneration,
|
|
1238
|
+
signal,
|
|
1086
1239
|
});
|
|
1087
1240
|
this.assertTopicControlPlaneActive(lifecycleRevision);
|
|
1088
|
-
if (rootCandidateGeneration !== this.getTopicRootCandidateGeneration()) {
|
|
1089
|
-
continue;
|
|
1090
|
-
}
|
|
1091
1241
|
return root;
|
|
1092
1242
|
}
|
|
1093
1243
|
catch (error) {
|
|
1244
|
+
if (signal.aborted ||
|
|
1245
|
+
candidateGeneration !== this.getTopicRootCandidateGeneration()) {
|
|
1246
|
+
throw error;
|
|
1247
|
+
}
|
|
1094
1248
|
warn(`Failed to host shard root ${topic} before answering root query: ${error instanceof Error ? error.message : String(error)}`);
|
|
1095
1249
|
return undefined;
|
|
1096
1250
|
}
|
|
1097
|
-
}
|
|
1251
|
+
}, [lifecycleSignal]);
|
|
1098
1252
|
}
|
|
1099
|
-
async resolveTopicRootThroughPeers(topic) {
|
|
1253
|
+
async resolveTopicRootThroughPeers(topic, options) {
|
|
1254
|
+
throwIfAborted(options?.signal);
|
|
1100
1255
|
const peers = this.getConnectedTopicRootTrackers();
|
|
1101
1256
|
if (peers.length === 0) {
|
|
1102
1257
|
return undefined;
|
|
1103
1258
|
}
|
|
1104
1259
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
1105
1260
|
for (const peer of peers) {
|
|
1106
|
-
const resolved = await this.queryTopicRootFromPeer(peer, topic);
|
|
1261
|
+
const resolved = await this.queryTopicRootFromPeer(peer, topic, DEFAULT_TOPIC_ROOT_QUERY_TIMEOUT_MS, options?.signal);
|
|
1107
1262
|
if (resolved) {
|
|
1108
1263
|
return resolved;
|
|
1109
1264
|
}
|
|
1110
1265
|
}
|
|
1111
1266
|
if (attempt < 2) {
|
|
1112
|
-
await delay(150 * (attempt + 1));
|
|
1267
|
+
await withAbort(delay(150 * (attempt + 1), options), options?.signal);
|
|
1113
1268
|
}
|
|
1114
1269
|
}
|
|
1115
1270
|
return undefined;
|
|
@@ -1233,7 +1388,9 @@ export class TopicControlPlane extends DirectStream {
|
|
|
1233
1388
|
const existing = this.fanoutChannels.get(t);
|
|
1234
1389
|
if (existing) {
|
|
1235
1390
|
if (!root) {
|
|
1236
|
-
const resolved = await this.resolveShardRootState(t
|
|
1391
|
+
const resolved = await this.resolveShardRootState(t, {
|
|
1392
|
+
signal: options?.signal,
|
|
1393
|
+
});
|
|
1237
1394
|
this.assertTopicControlPlaneActive(lifecycleRevision);
|
|
1238
1395
|
root = resolved.root;
|
|
1239
1396
|
resolvedGeneration = resolved.candidateGeneration;
|
|
@@ -1270,7 +1427,9 @@ export class TopicControlPlane extends DirectStream {
|
|
|
1270
1427
|
}
|
|
1271
1428
|
}
|
|
1272
1429
|
if (!root) {
|
|
1273
|
-
const resolved = await this.resolveShardRootState(t
|
|
1430
|
+
const resolved = await this.resolveShardRootState(t, {
|
|
1431
|
+
signal: options?.signal,
|
|
1432
|
+
});
|
|
1274
1433
|
this.assertTopicControlPlaneActive(lifecycleRevision);
|
|
1275
1434
|
root = resolved.root;
|
|
1276
1435
|
resolvedGeneration = resolved.candidateGeneration;
|
|
@@ -1488,22 +1647,37 @@ export class TopicControlPlane extends DirectStream {
|
|
|
1488
1647
|
}
|
|
1489
1648
|
}
|
|
1490
1649
|
}
|
|
1491
|
-
async hostShardRootsNow() {
|
|
1650
|
+
async hostShardRootsNow(options) {
|
|
1492
1651
|
if (!this.started)
|
|
1493
1652
|
throw new NotStartedError();
|
|
1494
|
-
const
|
|
1495
|
-
|
|
1496
|
-
const
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1653
|
+
const lifecycleSignal = this.topicRootResolutionAbortController.signal;
|
|
1654
|
+
return this.withTopicRootCandidateResolution(async ({ candidateGeneration, signal }) => {
|
|
1655
|
+
const joins = [];
|
|
1656
|
+
try {
|
|
1657
|
+
for (let i = 0; i < this.shardCount; i++) {
|
|
1658
|
+
throwIfAborted(signal);
|
|
1659
|
+
const shardTopic = `${this.shardTopicPrefix}${i}`;
|
|
1660
|
+
const resolved = await this.resolveShardRootState(shardTopic, {
|
|
1661
|
+
signal,
|
|
1662
|
+
});
|
|
1663
|
+
if (resolved.root !== this.publicKeyHash)
|
|
1664
|
+
continue;
|
|
1665
|
+
const joining = this.ensureFanoutChannel(shardTopic, {
|
|
1666
|
+
pin: true,
|
|
1667
|
+
root: resolved.root,
|
|
1668
|
+
rootCandidateGeneration: resolved.candidateGeneration,
|
|
1669
|
+
signal,
|
|
1670
|
+
});
|
|
1671
|
+
void joining.catch(() => { });
|
|
1672
|
+
joins.push(joining);
|
|
1673
|
+
}
|
|
1674
|
+
await Promise.all(joins);
|
|
1675
|
+
}
|
|
1676
|
+
catch (error) {
|
|
1677
|
+
await Promise.allSettled(joins);
|
|
1678
|
+
throw error;
|
|
1679
|
+
}
|
|
1680
|
+
}, [lifecycleSignal, options?.signal]);
|
|
1507
1681
|
}
|
|
1508
1682
|
async subscribe(topic) {
|
|
1509
1683
|
this.pendingSubscriptions.add(topic);
|