cross-tab-worker-databus 0.20.91 → 0.20.92
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/CHANGELOG.md +28 -0
- package/dist/centrifuge.js +1 -1
- package/dist/{chunk-DMM5KBNG.js → chunk-QB74C4EH.js} +242 -87
- package/dist/chunk-QB74C4EH.js.map +7 -0
- package/dist/cjs/centrifuge.cjs +241 -86
- package/dist/cjs/centrifuge.cjs.map +2 -2
- package/dist/cjs/index.cjs +241 -86
- package/dist/cjs/index.cjs.map +2 -2
- package/dist/core/cluster.d.ts +8 -5
- package/dist/core/cluster.d.ts.map +1 -1
- package/dist/core/data-bus.d.ts +21 -1
- package/dist/core/data-bus.d.ts.map +1 -1
- package/dist/core/replay-manager.d.ts +17 -3
- package/dist/core/replay-manager.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/docs/architecture.md +9 -6
- package/docs/benchmarks.md +8 -8
- package/docs/capabilities.md +3 -1
- package/docs/release-checklist.md +2 -2
- package/docs/roadmap.md +10 -1
- package/docs/zh/architecture.md +9 -6
- package/docs/zh/benchmarks.md +8 -8
- package/docs/zh/capabilities.md +3 -1
- package/docs/zh/release-checklist.md +2 -2
- package/docs/zh/roadmap.md +10 -1
- package/package.json +1 -1
- package/dist/chunk-DMM5KBNG.js.map +0 -7
package/dist/core/cluster.d.ts
CHANGED
|
@@ -132,6 +132,9 @@ export declare class WorkerClusterRuntime {
|
|
|
132
132
|
private started;
|
|
133
133
|
private suspended;
|
|
134
134
|
private lifecycleListening;
|
|
135
|
+
/** Invalidates an in-flight pageshow resume when a synchronous onResume
|
|
136
|
+
* callback stops or pauses the cluster before activate() is reached. */
|
|
137
|
+
private lifecycleGeneration;
|
|
135
138
|
private currentRecord;
|
|
136
139
|
constructor(options: WorkerClusterOptions);
|
|
137
140
|
/** Start the cluster: register, listen for lifecycle events, and begin heartbeats. */
|
|
@@ -254,15 +257,15 @@ export declare class WorkerClusterRuntime {
|
|
|
254
257
|
private sendRouteReleased;
|
|
255
258
|
/**
|
|
256
259
|
* Accept a graceful handoff only when the route still points to this worker,
|
|
257
|
-
* the release comes from the recorded previous owner, and the generation
|
|
258
|
-
*
|
|
260
|
+
* the release comes from the recorded previous owner, and the generation
|
|
261
|
+
* exactly matches ours. Any other ROUTE_RELEASED is stale and dropped.
|
|
259
262
|
*/
|
|
260
263
|
private handleRouteReleasedMessage;
|
|
261
264
|
/** A ROUTE_RELEASED is stale (and must be dropped) unless the route still
|
|
262
265
|
* points to us, the release comes from the recorded previous owner, and
|
|
263
|
-
* the release generation
|
|
264
|
-
* earlier handoff round
|
|
265
|
-
*
|
|
266
|
+
* the release generation exactly matches ours. A delayed ACK from either an
|
|
267
|
+
* earlier or later handoff round belongs to a different route and must not
|
|
268
|
+
* confirm the current round. */
|
|
266
269
|
private isStaleRouteRelease;
|
|
267
270
|
/** True when an unconfirmed handoff route has been stuck longer than a
|
|
268
271
|
* worker TTL. The ACK for a live handoff is posted synchronously with the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cluster.d.ts","sourceRoot":"","sources":["../../src/core/cluster.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAkB,kBAAkB,EAAe,MAAM,eAAe,CAAC;AASrF,OAAO,KAAK,EACV,0BAA0B,EAC1B,oBAAoB,EAGpB,mBAAmB,EACnB,YAAY,EAEZ,WAAW,EACX,YAAY,EAEb,MAAM,SAAS,CAAC;AAEjB,OAAO,EAIL,qBAAqB,
|
|
1
|
+
{"version":3,"file":"cluster.d.ts","sourceRoot":"","sources":["../../src/core/cluster.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAkB,kBAAkB,EAAe,MAAM,eAAe,CAAC;AASrF,OAAO,KAAK,EACV,0BAA0B,EAC1B,oBAAoB,EAGpB,mBAAmB,EACnB,YAAY,EAEZ,WAAW,EACX,YAAY,EAEb,MAAM,SAAS,CAAC;AAEjB,OAAO,EAIL,qBAAqB,EAItB,MAAM,oBAAoB,CAAC;AAK5B,0EAA0E;AAC1E,MAAM,WAAW,qBAAqB;IACpC,mFAAmF;IACnF,SAAS,EAAE,CACT,MAAM,EAAE,mBAAmB,EAC3B,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,OAAO,EACd,SAAS,CAAC,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,KACf,IAAI,CAAC;IACV;;yEAEqE;IACrE,cAAc,CAAC,EAAE,CACf,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,aAAa,CAAC;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,KAC5E,IAAI,CAAC;IACV;;;2DAGuD;IACvD,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9C,OAAO,EAAE,CACP,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,OAAO,EAChB,cAAc,EAAE,MAAM,EACtB,WAAW,CAAC,EAAE,MAAM,KACjB,IAAI,CAAC;IACV,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,qGAAqG;IACrG,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE;QACrB,SAAS,EAAE,CAAC,OAAO,qBAAqB,CAAC,SAAS,GAAG,OAAO,qBAAqB,CAAC,eAAe,GAAG,OAAO,qBAAqB,CAAC,wBAAwB,CAAC,CAAC;QAC3J,KAAK,EAAE,MAAM,CAAC;KACf,KAAK,IAAI,CAAC;CACZ;AAED,MAAM,WAAW,oBAAoB;IACnC;gFAC4E;IAC5E,UAAU,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,QAAQ,EAAE,qBAAqB,CAAC;IAChC,2EAA2E;IAC3E,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,4EAA4E;IAC5E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,8EAA8E;IAC9E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qFAAqF;IACrF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,2DAA2D;IAC3D,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;sEACkE;IAClE,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;kFAG8E;IAC9E,aAAa,CAAC,EAAE,oBAAoB,CAAC;CACtC;AAED,2EAA2E;AAC3E,MAAM,WAAW,qBAAqB;IACpC,eAAe,EAAE,MAAM,CAAC;IACxB,2FAA2F;IAC3F,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;IACpD,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,YAAY,CAAC;IAC5B,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,qFAAqF;IACrF,MAAM,EAAE,KAAK,CAAC,WAAW,GAAG;QAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;IACtD,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,0DAA0D;IAC1D,WAAW,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxD,eAAe,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/E;AAUD;;;;;;;;;;;;GAYG;AACH,qBAAa,oBAAoB;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;IACjD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwB;IACjD,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IAC7C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,mFAAmF;IACnF,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAmC;IACjE,gFAAgF;IAChF,OAAO,CAAC,gBAAgB,CAItB;IAEF,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqB;IAItD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA6B;IAC5D,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA+D;IAC/F,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAC5C,OAAO,CAAC,mBAAmB,CAAK;IAChC,OAAO,CAAC,qBAAqB,CAAK;IAClC,OAAO,CAAC,mBAAmB,CAAK;IAChC,OAAO,CAAC,sBAAsB,CAAuB;IACrD,OAAO,CAAC,oBAAoB;IAS5B,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAoC;IAMzE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IACzD,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,eAAe,CAAiB;IACxC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,kBAAkB,CAAS;IACnC;4EACwE;IACxE,OAAO,CAAC,mBAAmB,CAAK;IAChC,OAAO,CAAC,aAAa,CAAe;gBAExB,OAAO,EAAE,oBAAoB;IAsCzC,sFAAsF;IACtF,KAAK,IAAI,IAAI;IAiBb;;;;;;OAMG;IACH,IAAI,IAAI,IAAI;IAcZ;;;OAGG;IACH,OAAO,CAAC,QAAQ;IAqChB;;;OAGG;IACH,OAAO,CAAC,KAAK;IAiDb,oEAAoE;IACpE,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAMrC;;;;OAIG;IACH,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IA0BjC;;;OAGG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAOhC;;yEAEqE;IACrE,OAAO,CAAC,mBAAmB;IAa3B,qGAAqG;IACrG,OAAO,CAAC,qBAAqB;IAsC7B;;;;;OAKG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO;IAClE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,0BAA0B,GAAG,OAAO;IAuCrF;;;;;;OAMG;IACH,YAAY,CACV,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,aAAa,CAAC;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,GAC9E,OAAO;IAgDV;sFACkF;IAClF,OAAO,CAAC,oBAAoB;IAmB5B,iEAAiE;IACjE,OAAO,CAAC,oBAAoB;IAa5B;6EACyE;IACzE,OAAO,CAAC,yBAAyB;IAYjC;;;mEAG+D;IAC/D,OAAO,CAAC,gBAAgB;IAIxB;;iFAE6E;IAC7E,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI;IAS/E;2CACuC;IACvC,OAAO,CAAC,aAAa;IAMrB;;+DAE2D;IAC3D,OAAO,CAAC,gBAAgB;IAsBxB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAsBlC,4EAA4E;IAC5E,cAAc,IAAI,OAAO;IAIzB;8EAC0E;IAC1E,OAAO,CAAC,aAAa;IAMrB;kEAC8D;IAC9D,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAQ1C,iEAAiE;IACjE,sBAAsB,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE;IAEpE,8EAA8E;IAC9E,WAAW,IAAI,qBAAqB;IAuBpC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAsB;IAErD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAU7B;IAEF,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAQrC;IAEF,OAAO,CAAC,qBAAqB;IAQ7B,OAAO,CAAC,wBAAwB;IAQhC,qFAAqF;IACrF,OAAO,CAAC,QAAQ,CAAC,aAAa,CAsB5B;IAEF,mFAAmF;IACnF,OAAO,CAAC,oBAAoB;IA6D5B;;;;;OAKG;IACH,OAAO,CAAC,2BAA2B;IAYnC;kEAC8D;IAC9D,OAAO,CAAC,iBAAiB;IAgBzB;;;;OAIG;IACH,OAAO,CAAC,0BAA0B;IAYlC;;;;oCAIgC;IAChC,OAAO,CAAC,mBAAmB;IAW3B;;;;oEAIgE;IAChE,OAAO,CAAC,cAAc;IAItB,8EAA8E;IAC9E,OAAO,CAAC,SAAS;IASjB;;iFAE6E;IAC7E,OAAO,CAAC,gBAAgB;IASxB;;;;;;;;OAQG;IACH,OAAO,CAAC,sBAAsB;IAkF9B,wEAAwE;IACxE,OAAO,CAAC,uBAAuB;IAa/B;;;;OAIG;IACH,OAAO,CAAC,WAAW;IA6CnB,oFAAoF;IACpF,OAAO,CAAC,IAAI;IAUZ,qFAAqF;IACrF,OAAO,CAAC,WAAW;IAenB,0EAA0E;IAC1E,OAAO,CAAC,oBAAoB;IAsB5B,0FAA0F;IAC1F,OAAO,CAAC,SAAS;IAKjB;;;yCAGqC;IACrC,OAAO,CAAC,eAAe;IAavB,2EAA2E;IAC3E,OAAO,CAAC,UAAU;IAUlB;;qCAEiC;IACjC,OAAO,CAAC,gBAAgB;IAgBxB,yFAAyF;IACzF,OAAO,CAAC,YAAY;IAYpB,8EAA8E;IAC9E,OAAO,CAAC,qBAAqB;IAU7B,oEAAoE;IACpE,OAAO,CAAC,0BAA0B;IAQlC,8DAA8D;IAC9D,OAAO,CAAC,eAAe;IAQvB;;;;;0CAKsC;IACtC,OAAO,CAAC,WAAW;IAYnB,4EAA4E;IAC5E,OAAO,CAAC,cAAc;IAItB,8GAA8G;IAC9G,OAAO,CAAC,WAAW;IAOnB,kGAAkG;IAClG,OAAO,CAAC,UAAU;IAOlB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,aAAa;IA6BrB,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,oBAAoB;IAI5B,OAAO,CAAC,aAAa;IAQrB,8EAA8E;IAC9E,OAAO,CAAC,YAAY;CAGrB"}
|
package/dist/core/data-bus.d.ts
CHANGED
|
@@ -193,7 +193,9 @@ export declare class CrossTabDataBus<TConfig = unknown, TData = unknown> {
|
|
|
193
193
|
private recoveryGateRelease;
|
|
194
194
|
private recoveryTimer;
|
|
195
195
|
private recoveryTimerToken;
|
|
196
|
+
private recoveryCancellationToken;
|
|
196
197
|
private recoveryDemandAllowed;
|
|
198
|
+
private recoveryWaiters;
|
|
197
199
|
/** Monotonic generation incremented on every successful transport open.
|
|
198
200
|
* Stays in lockstep with `lastSuccessAt` so callers can detect that the
|
|
199
201
|
* transport has been reopened even if the timestamp window is short. */
|
|
@@ -231,8 +233,18 @@ export declare class CrossTabDataBus<TConfig = unknown, TData = unknown> {
|
|
|
231
233
|
private cancelScheduledRecovery;
|
|
232
234
|
/** Keep the recovery gate closed after a failed attempt while allowing the
|
|
233
235
|
* next explicit transport operation to start an immediate on-demand reopen.
|
|
234
|
-
* If no gate/successor retry remains, release any waiters.
|
|
236
|
+
* If no gate/successor retry remains, release any waiters.
|
|
237
|
+
*
|
|
238
|
+
* `kickParkedWaiters` is set only when the failure is an automatic attempt: an
|
|
239
|
+
* operation that was already parked on the gate is itself demand, so it must
|
|
240
|
+
* not wait for some unrelated future operation. A failed *on-demand* reopen
|
|
241
|
+
* passes `false`, so it re-arms the flag for a later operation instead of
|
|
242
|
+
* looping on its own failure. */
|
|
235
243
|
private allowDemandRecovery;
|
|
244
|
+
/** Start one on-demand reopen if a failed attempt has left parked operations
|
|
245
|
+
* and enabled demand recovery. Consumes the demand token so at most one
|
|
246
|
+
* reopen is issued; every waiter stays behind the gate until it succeeds. */
|
|
247
|
+
private startDemandRecovery;
|
|
236
248
|
/** Reset failure and recovery diagnostics for a new explicit start session. */
|
|
237
249
|
private resetFailureState;
|
|
238
250
|
/**
|
|
@@ -338,6 +350,14 @@ export declare class CrossTabDataBus<TConfig = unknown, TData = unknown> {
|
|
|
338
350
|
* unless another stop() arrives first and cancels that queued restart.
|
|
339
351
|
*/
|
|
340
352
|
stop(): Promise<void>;
|
|
353
|
+
/**
|
|
354
|
+
* Synchronous teardown prelude. Flips `stopping` (the authoritative
|
|
355
|
+
* in-flight signal), cancels scheduled work, releases handlers, and emits the
|
|
356
|
+
* observable STOP lifecycle event. stop() calls it after the shared gate is
|
|
357
|
+
* installed but in the same tick, so the stop still takes effect immediately
|
|
358
|
+
* while a re-entrant stop() from the STOP event shares the one teardown.
|
|
359
|
+
*/
|
|
360
|
+
private beginStop;
|
|
341
361
|
private performStop;
|
|
342
362
|
/**
|
|
343
363
|
* Incoming message from the transport.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-bus.d.ts","sourceRoot":"","sources":["../../src/core/data-bus.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"data-bus.d.ts","sourceRoot":"","sources":["../../src/core/data-bus.ts"],"names":[],"mappings":"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"}
|
|
@@ -52,7 +52,18 @@ export declare class ReplayManager<TData = unknown> {
|
|
|
52
52
|
private retryGeneration;
|
|
53
53
|
private pendingReplayPersistence;
|
|
54
54
|
private persistenceFlushScheduled;
|
|
55
|
-
|
|
55
|
+
/** Current hydration operation, if one belongs to the active lifecycle. */
|
|
56
|
+
private hydration;
|
|
57
|
+
/** Invalidates a load from a superseded suspend/reset lifecycle. */
|
|
58
|
+
private hydrationEpoch;
|
|
59
|
+
/** Whether the active lifecycle has finished (or deliberately skipped) hydration. */
|
|
60
|
+
private hydrationComplete;
|
|
61
|
+
/** A failed load is retried by the next explicit start(), not every replay request. */
|
|
62
|
+
private hydrationFailed;
|
|
63
|
+
/** Mutations that must be applied to a load already in flight. */
|
|
64
|
+
private hydrationClearAll;
|
|
65
|
+
private hydrationClearedTopics;
|
|
66
|
+
private hydrationClearBefore;
|
|
56
67
|
/** Coalesced retention cleanup: the newest cutoff wins while one is running. */
|
|
57
68
|
private retentionCleanup;
|
|
58
69
|
private retentionCutoff;
|
|
@@ -94,7 +105,7 @@ export declare class ReplayManager<TData = unknown> {
|
|
|
94
105
|
/** Suspend the manager: cancel in-flight persistence retries (so a hidden tab
|
|
95
106
|
* or stopped bus does not keep hammering the store) and stop the sweep. */
|
|
96
107
|
suspend(): void;
|
|
97
|
-
/** Drop all in-memory buffers
|
|
108
|
+
/** Drop all in-memory buffers and require hydration for the next lifecycle. */
|
|
98
109
|
resetBuffers(): void;
|
|
99
110
|
/** Buffer occupancy for diagnostics. `bytes` is an approximate in-memory
|
|
100
111
|
* payload footprint (same heuristic as adaptive load weighting), computed on
|
|
@@ -113,7 +124,10 @@ export declare class ReplayManager<TData = unknown> {
|
|
|
113
124
|
* Only reachable when the backend advertises `appendBatch` (the sole queuer,
|
|
114
125
|
* `record()`, guards on it), so the batched path is unconditional here. */
|
|
115
126
|
private schedulePersistenceFlush;
|
|
116
|
-
/**
|
|
127
|
+
/** Start the active lifecycle's one-shot hydration, if it has not completed
|
|
128
|
+
* or deliberately skipped hydration already. */
|
|
129
|
+
private requestHydration;
|
|
130
|
+
/** Load durable history into the in-memory rings once per lifecycle, pruning
|
|
117
131
|
* entries past the retention window first. Failures are reported but do not
|
|
118
132
|
* block startup — the bus runs with whatever survived. */
|
|
119
133
|
private hydrate;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replay-manager.d.ts","sourceRoot":"","sources":["../../src/core/replay-manager.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAGrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD,iFAAiF;AACjF,qBAAa,8BAA+B,SAAQ,KAAK;;CAKxD;AAED,uEAAuE;AACvE,MAAM,WAAW,iBAAiB,CAAC,KAAK,GAAG,OAAO;IAChD,2EAA2E;IAC3E,OAAO,EAAE,OAAO,CAAC;IACjB;kEAC8D;IAC9D,WAAW,EAAE,MAAM,CAAC;IACpB,+DAA+D;IAC/D,WAAW,CAAC,EAAE,wBAAwB,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IACrD,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,+BAA+B;IAC/B,aAAa,EAAE,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,OAAO,cAAc,CAAC,CAAC;IACpE,sEAAsE;IACtE,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,kEAAkE;IAClE,2BAA2B,EAAE,MAAM,CAAC;IACpC,wDAAwD;IACxD,yBAAyB,EAAE,MAAM,CAAC;IAClC,0DAA0D;IAC1D,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,4DAA4D;IAC5D,KAAK,EAAE,oBAAoB,CAAC;IAC5B,4EAA4E;IAC5E,kBAAkB,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAC7C,2EAA2E;IAC3E,eAAe,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAC3C;AAKD,qBAAa,aAAa,CAAC,KAAK,GAAG,OAAO;
|
|
1
|
+
{"version":3,"file":"replay-manager.d.ts","sourceRoot":"","sources":["../../src/core/replay-manager.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
package/docs/architecture.md
CHANGED
|
@@ -96,7 +96,7 @@ Terms are explained in plain language; the code and the rest of this document us
|
|
|
96
96
|
| **Sticky** | — | Existing routes keep their owner while that owner is alive; load and visibility only affect placement of brand-new routes. |
|
|
97
97
|
| **Heartbeat** | `heartbeatAt` | A Worker's periodic liveness write to storage. Workers past `workerTtlMs` without refreshing are considered dead. |
|
|
98
98
|
| **Handoff** | `handoffFromWorkerId` | The graceful passing of a Topic from an old owner to a new one (e.g. on `pagehide`), with a strict release-ACK protocol so no Topic is ever owned twice simultaneously. |
|
|
99
|
-
| **Generation** | `generation` | A monotonic counter on each route. Handoff ACKs must reference
|
|
99
|
+
| **Generation** | `generation` | A monotonic counter on each route. Handoff ACKs must reference the route's exact generation, so delayed ACKs from any other handoff round are ignored. |
|
|
100
100
|
| **Local mode** | `coordinated: false` | Degraded operation when storage or BroadcastChannel is unavailable: no cross-Tab routing, the Tab only uses its own transport. |
|
|
101
101
|
|
|
102
102
|
## Storage Structure
|
|
@@ -146,7 +146,7 @@ interface WorkerRoute {
|
|
|
146
146
|
|
|
147
147
|
`generation` increments on every re-assignment and must match across the handoff handshake; `handoffFromWorkerId` records the previous owner during a graceful handoff. The interface above matches the current protocol — see [Failover](#failover) for how these two fields drive takeover.
|
|
148
148
|
|
|
149
|
-
Routes do not store the original topic string or payload. When the actual owner receives `CONTROL/SUBSCRIBE`, the original topic string is only passed through the BroadcastChannel in-memory message. `confirmedAt` is written after the owner processes the control message; before the route is confirmed, the subscriber Runtime holding the original topic string will resend `SUBSCRIBE` to recover from BroadcastChannel message loss that results in "a route without a real subscription".
|
|
149
|
+
Routes do not store the original topic string or payload. When the actual owner receives `CONTROL/SUBSCRIBE`, the original topic string is only passed through the BroadcastChannel in-memory message. The receiver accepts that control frame only when the durable route currently names it; a delayed frame from an earlier assignment round is dropped, and a route awaiting `ROUTE_RELEASED` can be confirmed only by the matching handoff ACK. `confirmedAt` is written after the owner processes the control message; before the route is confirmed, the subscriber Runtime holding the original topic string will resend `SUBSCRIBE` to recover from BroadcastChannel message loss that results in "a route without a real subscription".
|
|
150
150
|
|
|
151
151
|
### How `topic`, `topicKey`, `tabId`, `workerId`, and BroadcastChannel relate
|
|
152
152
|
|
|
@@ -316,7 +316,7 @@ All real-time coordination flows through one BroadcastChannel per cluster, whose
|
|
|
316
316
|
|
|
317
317
|
| Type | Direction | Purpose |
|
|
318
318
|
|---|---|---|
|
|
319
|
-
| `CONTROL` | point-to-point (A → B) | Ask the target Worker to `SUBSCRIBE`, `UNSUBSCRIBE`, or `PUBLISH` a topic. Carries `action`, `topic`, `topicKey`, `targetWorkerId`, and an optional `data` payload. |
|
|
319
|
+
| `CONTROL` | point-to-point (A → B) | Ask the target Worker to `SUBSCRIBE`, `UNSUBSCRIBE`, or `PUBLISH` a topic. Carries `action`, `topic`, `topicKey`, `targetWorkerId`, and an optional `data` payload. A `SUBSCRIBE` is honored only when the durable route currently names the target and is not awaiting `ROUTE_RELEASED`. |
|
|
320
320
|
| `EVENT` | broadcast (owner → all Tabs) | Fan out a publication that the transport delivered to the owning Worker. Carries `eventType` and `payload`. |
|
|
321
321
|
| `REGISTRY` | broadcast | Nudge every Tab to reconcile immediately after a registry or route write, instead of waiting for the next heartbeat. |
|
|
322
322
|
| `ROUTE_RELEASED` | point-to-point (old owner → new owner) | Acknowledge a graceful handoff; only the new owner whose route `generation` matches may `SUBSCRIBE` (see Failover). |
|
|
@@ -539,11 +539,14 @@ This process prevents overlap during graceful owner handoff while retaining avai
|
|
|
539
539
|
|
|
540
540
|
These invariants are pinned by regression tests (see `tests/stability.test.ts` and `tests/replay-persistence.test.ts`) and must hold through future refactors:
|
|
541
541
|
|
|
542
|
-
- **
|
|
542
|
+
- **SUBSCRIBE route binding.** An inbound `CONTROL/SUBSCRIBE` is accepted only when the durable route currently names the receiver and is not an unconfirmed graceful handoff. A delayed frame from an earlier assignment round cannot add ownership, subscribe the transport, or confirm the route; only the matching `ROUTE_RELEASED` can authorize a pending handoff. Ownership follows the current route record, not the order in which control frames arrive.
|
|
543
|
+
- **Handoff ACK validity.** A `ROUTE_RELEASED` is accepted only when the route still points at the receiver, the release comes from the recorded `handoffFromWorkerId`, and the ACK generation exactly matches the stored route generation. Replayed ACKs from any other handoff round (e.g. an a↔b ping-pong) are dropped instead of confirming the current route.
|
|
543
544
|
- **Replay persistence cleanup ordering.** A batched persistence flush queued behind the current task is filtered against the cleanup that wins the race: `unsubscribe` and `clearReplayTopic` drop the topic's pending entries, `clearReplayBefore` drops entries older than the cutoff, and `suspend()`/`stop()` discard the whole queued batch before it can start under the next lifecycle generation. Cleared or stopped-session history is never re-appended by an in-flight flush.
|
|
544
545
|
- **Storage write recovery.** Coalesced writes retry with exponential backoff (50 ms → 1.6 s cap). A structurally failing key is dropped after 5 attempts (with a `console.warn`) without permanently blocking other queued keys, and the backoff delay resets once the queue fully drains or `clear()` cancels the retries.
|
|
545
546
|
- **Transport recovery budget.** Automatic recovery is paced by a cooldown, bounded by `recovery.maxAttempts`, and reports `exhausted` when the budget is spent. A successful reopen resets the attempt counter and the exhausted flag; explicit `subscribe` on a down transport can still recover manually. Scheduling alone does not reopen a connection: the backend must release the invalid connection before a retry can create or re-open one. `WebSocketTransport` resolves `start()` only after the socket opens, rejects a pre-open `error`/`close` or a `connectTimeoutMs` expiry, marks the socket active only until error/close, and drops the stale reference before the next `start()` invokes the factory, so late callbacks from the superseded socket are ignored.
|
|
546
547
|
- **BFCache suspension.** Hiding the tab stops the transport, bumps the persistence-retry generation (cancelling in-flight persistence retries without surfacing errors), pauses trace metrics and periodic dedup/replay cleanup, and gates dispatch; `pageshow` or an explicit `start()` reopens the transport, resumes those periodic resources, and re-establishes subscriptions exactly once per cycle.
|
|
548
|
+
- **Recovery waiter invalidation.** Each operation parked behind the recovery gate captures the recovery cancellation generation. `stop()` and `suspendTransport()` invalidate that generation when they supersede the recovery cycle, so a waiter whose microtask runs after an immediate explicit `start()` cannot replay into the replacement transport. The restarted cluster owns exactly one resubscription per assigned topic, and the stale operation cannot issue a duplicate subscription.
|
|
549
|
+
- **Hidden-document activation guard.** `WorkerClusterRuntime.start()` installs lifecycle listeners before it checks visibility. A start that reaches a hidden document records suspension without registering a worker, opening a channel, or starting heartbeats, so a restart queued behind an asynchronous stop cannot miss a `pagehide` during cleanup and reconnect in the background; the installed listener handles the later `pageshow`.
|
|
547
550
|
- **Handoff channel close ordering.** `pause()` defers the physical `channel.close()` by one task. Closing synchronously would discard messages still queued for delivery — including the handoff's `ROUTE_RELEASED` — stranding the handoff target with an unconfirmed route.
|
|
548
551
|
- **Stranded-handoff recovery.** If the previous owner is gone and its `ROUTE_RELEASED` never arrives (dropped channel message under load, or a crash between the route write and the ACK), the reconcile loop re-elects a live owner once the unconfirmed handoff has been stuck longer than a worker TTL (10 s default): the route is rewritten with a fresh generation and the handoff marker cleared, so the normal confirmation path completes (pinned by regression). The age gate matters — a fresh unconfirmed route may simply be waiting out its confirmation flush — and while the previous owner is still alive the new owner keeps waiting, so the strict handoff keeps its no-overlap guarantee.
|
|
549
552
|
- **Loss and recovery matrix.** Each coordination message has a bounded recovery path: a lost `CONTROL/SUBSCRIBE` is re-sent by the heartbeat reconcile for any route still lacking `confirmedAt`; a lost `REGISTRY` nudge costs at most one heartbeat interval (3 s default) because every tick reconciles anyway; a lost `ROUTE_RELEASED` is recovered by reconcile re-election once the previous owner is gone and the handoff has been stuck longer than a worker TTL (see the stranded-handoff invariant above, pinned by regression); publications dropped during a transport disconnect window are the one documented unrecoverable loss (transport contract). The storage-event fallback channel guarantees value-change delivery via a monotonic sequence in the envelope, and a dropped dispatch recovers through the same reconcile loop.
|
|
@@ -555,7 +558,7 @@ DataBus separates "business subscription intent" from "transport current subscri
|
|
|
555
558
|
|
|
556
559
|
The built-in Centrifuge transport also retains its own Subscriptions and performs protocol-level reconnection. Both layers of recovery require `subscribe` / `unsubscribe` to be idempotent.
|
|
557
560
|
|
|
558
|
-
A runtime `error` intentionally keeps `transportReady` true: the flag records that the installed transport opened for this session, so `ready()` keeps tracking the transport instead of flapping with the protocol connection. Transport *operations* are gated separately by the recovery gate. While an automatic or demand-driven reopen is pending, `runTransport()` parks new `subscribe` / `publish` calls behind that gate rather than writing them to the connection that just reported `error`; the gate is released only once a reopen succeeds (or the transport self-heals to `connected`), and every parked operation then runs against the live transport. A failed automatic attempt keeps the gate closed but lets the next explicit operation drive an immediate on-demand reopen instead of waiting out another cooldown;
|
|
561
|
+
A runtime `error` intentionally keeps `transportReady` true: the flag records that the installed transport opened for this session, so `ready()` keeps tracking the transport instead of flapping with the protocol connection. Transport *operations* are gated separately by the recovery gate. While an automatic or demand-driven reopen is pending, `runTransport()` parks new `subscribe` / `publish` calls behind that gate rather than writing them to the connection that just reported `error`; the gate is released only once a reopen succeeds (or the transport self-heals to `connected`), and every parked operation then runs against the live transport. A failed automatic attempt keeps the gate closed but lets the next explicit operation drive an immediate on-demand reopen instead of waiting out another cooldown; an exhausted recovery budget releases the gate so parked operations can follow the documented explicit-retry path. A `stop()` / page-hide instead invalidates the parked recovery generation before releasing the gate, so an immediate `start()` re-establishes subscriptions on the replacement transport without a stale waiter replaying its operation afterward. `disconnected` is a clean close rather than a recoverable failure: it never schedules a background DataBus reopen, and only an explicit `start()`, a page restore, the transport's own reconnection, or a later transport operation returns it to `connected`. That last path matters because the ready fast path is refused once the transport has actually reached `connected` and then reports `disconnected`; a `subscribe()` / `publish()` arriving after such a clean close is parked behind the same recovery gate and drives exactly one on-demand reopen, then flushes against the replacement connection instead of being written to the closed one. A transport that resolved `start()` before its first `connected` (worker-style backends report the connection asynchronously) is still handed operations directly, because its `disconnected` status means "not connected yet" rather than "a working connection was lost".
|
|
559
562
|
|
|
560
563
|
## Lifecycle State Machine
|
|
561
564
|
|
|
@@ -617,7 +620,7 @@ A runtime `error` intentionally keeps `transportReady` true: the flag records th
|
|
|
617
620
|
- **Readiness during suspend**: A suspended bus sets `suspended = true` and reuses `startPromise`/`pendingStop` for the chained `transport.stop()`, so that promise proves cleanup completed rather than readiness. `ready()` checks `suspended` after the `stopping` gate and rejects with a suspended-state error instead of returning the stop gate. `pageshow`/`reopenTransport()` and an explicit `start()` clear the flag and install a real reopen promise, so `ready()` follows the newest lifecycle intent. Because `pagehide` pauses the *cluster* independently of the transport, an explicit `start()` that leaves suspension resumes the cluster too; without that, the bus would report a healthy transport while its channel listener, heartbeat, and route assignments stayed dormant until the next `pageshow`, and every incoming publication would be discarded by `isAssigned()` against the cleared assignment map. `getHealthSummary()` already reported `{ healthy: false, state: 'suspended' }`; rejection keeps that verdict consistent with `ready()`.
|
|
618
621
|
- **Superseded open invalidation**: Every fresh start, reopen, suspend, and stop advances `lifecycleEpoch`. An open captures its epoch, ignores stale status/message/error callbacks, and neither marks the transport ready nor performs failure cleanup after a newer transition owns the lifecycle. `stop()` therefore waits for pending opens/reopens and prevents a superseded open from becoming ready after the stop completes.
|
|
619
622
|
- **Recovery cooldown**: When the transport reports `error` while `started` is true and `stopping` is false, `updateStatus` schedules an automatic `reopenTransport()` after `RECOVERY_COOLDOWN_MS` (1000 ms). A second error within the cooldown window is suppressed to prevent a tight retry loop.
|
|
620
|
-
- **Transport recovery gate**: Scheduling a reopen also arms a recovery gate, so `subscribe` / `publish` issued during the cooldown cannot reach the failed connection; they are released after the reopen succeeds. The gate deliberately survives a failed automatic attempt: the next explicit operation starts an immediate on-demand reopen instead of waiting for the next paced attempt, and parked operations flush behind that success. Exhausting `recovery.maxAttempts
|
|
623
|
+
- **Transport recovery gate**: Scheduling a reopen also arms a recovery gate, so `subscribe` / `publish` issued during the cooldown cannot reach the failed connection; they are released after the reopen succeeds. The gate deliberately survives a failed automatic attempt: the next explicit operation starts an immediate on-demand reopen instead of waiting for the next paced attempt, and parked operations flush behind that success. An explicit `start()` that supersedes the automatic timer cancels that attempt and resets the failure ledger but leaves the gate closed; if the manual open also fails, its parked operations remain queued for a later automatic or demand-driven reopen instead of being discarded with the superseded opening. Exhausting `recovery.maxAttempts` releases the gate so the explicit-retry path remains reachable. Superseding the wait with `stop()` / `suspendTransport()` also invalidates every parked waiter before releasing the gate, so the explicit restart and its cluster resubscription cannot be followed by a duplicate stale operation. A runtime `error` does not clear `transportReady`, because clearing it would let caller traffic reopen the transport outside the cooldown and report readiness against a connection that is not carrying data.
|
|
621
624
|
- **Stop during suspend**: `stop()` sets `stopping = true`, which prevents `suspendTransport()` from running. The cleanup awaits `startPromise` and `pendingStop` to ensure any in-flight open or stop completes before the final `transport.stop()`.
|
|
622
625
|
|
|
623
626
|
## Degradation
|
package/docs/benchmarks.md
CHANGED
|
@@ -2,20 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
# Browser Benchmark Trend
|
|
4
4
|
|
|
5
|
-
> Data through 2026-09-
|
|
5
|
+
> Data through 2026-09-17, from the 27 archived `bench-results/browser-*.json` reports (run `pnpm bench:browser` to add one; regenerate this doc with `node scripts/bench-trend.mjs`).
|
|
6
6
|
|
|
7
7
|
The comparison baseline for release gating is `pnpm bench:compare --fail-above-pct 50` between the two most recent reports (50% ceiling absorbs shared-runner noise). This doc records the long-run picture: values are per-metric latencies where lower is better, and the all-time best marks the healthiest observed run on this machine.
|
|
8
8
|
|
|
9
9
|
<!-- BENCH-TREND:BEGIN (machine-generated table) -->
|
|
10
10
|
| Metric | Previous (ms) | Latest (ms) | Δ | All-time best (ms) |
|
|
11
11
|
|---|---|---|---|---|
|
|
12
|
-
| publish per-message (ms, lower is better) — dedicated |
|
|
13
|
-
| publish per-message (ms, lower is better) — shared |
|
|
14
|
-
| wildcard dispatch ×1000 (ms, lower is better) | 7
|
|
15
|
-
| publishBatch ×1000 (ms, lower is better) | 4.
|
|
16
|
-
| dedup ×1000 (ms, lower is better) |
|
|
17
|
-
| trace + publish ×1000 (ms, lower is better) | 5
|
|
18
|
-
| first-packet cold dispatch (ms, lower is better) | 0.1 | 0 |
|
|
12
|
+
| publish per-message (ms, lower is better) — dedicated | 52.4371 | 50.838 | -1.60 | 35.3543 |
|
|
13
|
+
| publish per-message (ms, lower is better) — shared | 34.2414 | 34.4971 | +0.26 | 33.6784 |
|
|
14
|
+
| wildcard dispatch ×1000 (ms, lower is better) | 5.7 | 6.4 | +0.70 | 0.1 |
|
|
15
|
+
| publishBatch ×1000 (ms, lower is better) | 4.2 | 4.3 | +0.10 | 0.4 |
|
|
16
|
+
| dedup ×1000 (ms, lower is better) | 23.2 | 13.3 | -9.90 | 0 |
|
|
17
|
+
| trace + publish ×1000 (ms, lower is better) | 5 | 5 | +0.00 | 4.5 |
|
|
18
|
+
| first-packet cold dispatch (ms, lower is better) | 0.1 | 0.1 | +0.00 | 0 |
|
|
19
19
|
<!-- BENCH-TREND:END -->
|
|
20
20
|
|
|
21
21
|
Notes:
|
package/docs/capabilities.md
CHANGED
|
@@ -46,10 +46,12 @@ and regression-locked:
|
|
|
46
46
|
|
|
47
47
|
| Invariant | Area | Regressed guarantee |
|
|
48
48
|
|---|---|---|
|
|
49
|
-
|
|
|
49
|
+
| SUBSCRIBE route binding | coordination | An inbound `CONTROL/SUBSCRIBE` is honored only when the durable route currently names the receiver and is not awaiting `ROUTE_RELEASED`; delayed frames from earlier assignment rounds cannot create ownership, subscribe the transport, or confirm a pending handoff |
|
|
50
|
+
| Handoff ACK validity | coordination | `ROUTE_RELEASED` is honored only when the route still points to the receiver, the release matches `handoffFromWorkerId`, and the ACK generation exactly matches the stored route — delayed ACKs from another a↔b ping-pong round are dropped |
|
|
50
51
|
| Replay persistence cleanup ordering | durability | Queued batch flushes are filtered against the winning cleanup (`unsubscribe`/`clearReplayTopic` drop the topic's pending entries, `clearReplayBefore` drops entries older than the cutoff, and `suspend()`/`stop()` discard the queued batch); cleared or stopped-session history is never re-appended by an in-flight flush |
|
|
51
52
|
| Storage write recovery | coordination | Coalesced writes retry with exponential backoff (50 ms → 1.6 s cap); a structurally failing key is dropped after 5 attempts (with `console.warn`) without blocking other queued keys; backoff resets once the queue drains or `clear()` cancels the retries |
|
|
52
53
|
| Transport recovery budget | lifecycle | Auto-recovery is paced by a cooldown, bounded by `recovery.maxAttempts`, and reports `exhausted` when the budget is spent; a successful reopen resets attempt + exhausted, and an explicit `subscribe` on a down transport can still recover manually |
|
|
54
|
+
| Recovery waiter lifetime | lifecycle | A recovery operation parked behind the transport gate captures the recovery cancellation generation; `stop()` / page-hide invalidates it, so an immediate explicit `start()` cannot be followed by a stale duplicate operation after the replacement transport re-establishes subscriptions. By contrast, an explicit `start()` that supersedes the automatic recovery timer preserves the gate and its parked operations if the manual open also fails, allowing a later automatic or demand-driven reopen to flush them |
|
|
53
55
|
| BFCache suspension | lifecycle | Hiding the tab stops the transport and silently cancels in-flight persistence retries; pageshow reopens the transport and re-establishes subscriptions exactly once per cycle |
|
|
54
56
|
| Handoff channel close ordering | coordination | `pause()` defers the physical `channel.close()` by one task so queued handoff frames (including `ROUTE_RELEASED`) flush before the channel dies |
|
|
55
57
|
| Loss & recovery matrix | coordination | Every coordination message has a bounded recovery path (re-send of unconfirmed SUBSCRIBE, REGISTRY nudge, TTL + re-election for lost ACKs); publications lost during a transport disconnect window are the one documented unrecoverable loss |
|
|
@@ -22,7 +22,7 @@ Only `pnpm bench:browser` / `pnpm bench:compare` stay local-only: shared-runner
|
|
|
22
22
|
3. Gate dependency security: `pnpm audit --registry=https://registry.npmjs.org` (the configured mirror registry lacks the audit endpoint; CI runs it on the public registry in the verify job). Fail the release on any known-vulnerability advisory; `pnpm-workspace.yaml` overrides pin patched ranges.
|
|
23
23
|
4. Gate browser benchmark regressions: `pnpm bench:compare --fail-above-pct 50` after two `pnpm bench:browser` runs, using a 50% ceiling so unrelated runner noise (see the known shared-runner jitter note) cannot fail the gate; a baseline shift (e.g. a metric becoming real instead of a no-op) is an expected one-time failure. Refresh the long-run trend doc with `pnpm bench:trend` and commit it when the tables change.
|
|
24
24
|
5. Confirm the package contains only intended files with `npm pack --dry-run --json`.
|
|
25
|
-
6. Commit,
|
|
25
|
+
6. Commit on a feature branch, push that branch, and merge its green PR using squash or fast-forward (no merge commit). Fetch the merged commit, tag that exact commit, and push only the specific version tag; never push `main`/`master` directly or force-push. The workflow runs `node scripts/verify-release-version.mjs` to require `RELEASE_TAG` to equal `v` + the package version and to require exactly one non-empty CHANGELOG section.
|
|
26
26
|
|
|
27
27
|
## Security and dependency scanning
|
|
28
28
|
|
|
@@ -30,7 +30,7 @@ The repository runs CodeQL (`javascript-typescript`; on push, on pull request, a
|
|
|
30
30
|
|
|
31
31
|
## Tagged-release workflow
|
|
32
32
|
|
|
33
|
-
Pushing a version tag triggers the `Release` GitHub Action: it runs `pnpm check` and `pnpm lint` (a tag can point at a commit that never passed CI's lint step), runs `verify:compat` and `verify:pack`, opens the GitHub release from the `CHANGELOG` section, publishes to npm when the `NPM_TOKEN` secret is set, and then runs the **blocking** published-consumer verification with the same budget as a manual run (`PUBLISHED_VERIFY_ATTEMPTS=48`, `PUBLISHED_VERIFY_DELAY_MS=7500`, a 6-minute ceiling). A release whose published package cannot be imported by a clean consumer fails the workflow — treat every `verify:published` failure as a failed release
|
|
33
|
+
Pushing a version tag triggers the `Release` GitHub Action: it runs `pnpm check` and `pnpm lint` (a tag can point at a commit that never passed CI's lint step), runs `verify:compat` and `verify:pack`, opens the GitHub release from the `CHANGELOG` section, publishes to npm when the `NPM_TOKEN` secret is set, and then runs the **blocking** published-consumer verification with the same budget as a manual run (`PUBLISHED_VERIFY_ATTEMPTS=48`, `PUBLISHED_VERIFY_DELAY_MS=7500`, a 6-minute ceiling). A release whose published package cannot be imported by a clean consumer fails the workflow — treat every `verify:published` failure as a failed release. For registry propagation or infrastructure failures, rerun the workflow against the unchanged tag; for artifact defects, ship a new patch version. Never move or reuse a published tag. When no token is configured the publish step is skipped, but verification still passes against whatever version is already on npm (e.g. one published manually).
|
|
34
34
|
|
|
35
35
|
## Publishing
|
|
36
36
|
|
package/docs/roadmap.md
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
# Roadmap
|
|
2
2
|
|
|
3
|
-
0.20.91 is released. The project is intentionally continuing through reliability-focused releases before a 1.0.0 stability freeze.
|
|
3
|
+
0.20.91 is released; 0.20.92 is in RELEASE_FREEZE pending PR and registry verification. The project is intentionally continuing through reliability-focused releases before a 1.0.0 stability freeze.
|
|
4
|
+
|
|
5
|
+
## 0.20.92 frozen scope
|
|
6
|
+
|
|
7
|
+
The current line continues lifecycle error-path verification around predecessor reopen settlement, queued-start readiness, and stop-promise cleanup. Sixteen fixes have now landed. First, the shared stop gate is installed before the synchronous teardown prelude, so a `stop()` re-entered from the synchronous STOP lifecycle trace event shares the one teardown instead of running `transport.stop()` twice. Second, lifecycle ownership and `startPromise` are installed before the synchronous START trace: if a trace or status callback re-enters `stop()`, the outer `start()` immediately stops later timer, cluster, and topic setup; the epoch guard abandons the old opening before `transport.start()`. Third, the same ownership-before-callback ordering now applies to `reopenTransport()`'s synchronous CONNECTING status notification, so a stop from that callback cancels recovery instead of letting it open a replacement transport after teardown. Fourth, a stop from the synchronous RESUME lifecycle trace now invalidates both explicit and native pageshow resumes; `WorkerClusterRuntime` uses a lifecycle generation so the outer pageshow handler cannot reactivate a cluster already stopped or paused by `onResume`. Previously each outer operation could settle after the nested stop and leave a stopped bus with a live connected transport or an active cluster. Fifth, transport operations already parked on the recovery gate are no longer stranded when the automatic recovery attempt they were waiting on fails: `runTransport()` now counts the parked operations, and a failed automatic attempt starts one on-demand reopen immediately when any waiter is present, so a `publish()` / `subscribe()` issued during the cooldown itself drives recovery instead of waiting for an unrelated later operation. A failed on-demand reopen still re-arms the demand flag for a later operation rather than looping on its own failure, and every waiter flushes in order once a reopen succeeds. The cluster-key isolation guarantee now has a dedicated two-runtime regression: same-topic ownership remains independent across tenants, publications do not cross the namespace boundary, and persisted keys use distinct opaque hashes without exposing the plaintext key. A sixth replay-retention fix now hands a cutoff queued during a suspend/resume cycle to a fresh cleanup when the older transaction is still unwinding, so the newest cutoff is not stranded until an unrelated future publication. A seventh fix clears `transportReady` before `reopenTransport()` emits its synchronous CONNECTING notification, so a second operation in the same tick cannot mistake the still-closing old transport for a usable connection; all operations remain parked on the opening and flush against the replacement in order. An eighth fix makes the pagehide suspension continuation epoch-aware: if the synchronous DISCONNECTED status callback invokes public `start()` as the documented resume path, the stale hide continuation is abandoned instead of stopping the replacement transport after it opens and leaving a false healthy state. A ninth fix preserves replay ordering when durable hydration is slower than live traffic: the loaded snapshot is placed ahead of publications recorded while `load()` was pending, so count pruning retains the newest live message instead of evicting it as older history. A tenth fix makes replay hydration replaceable across lifecycle boundaries: clear/clear-topic/unsubscribe/clear-before mutations are applied to an in-flight snapshot so they cannot be resurrected, a suspend/restart cancels the superseded load and starts a fresh hydration generation, and an explicit stop/start restores durable history instead of leaving the restarted rings empty. An eleventh fix closes the strict-handoff generation gap: `ROUTE_RELEASED` must exactly match the stored route generation, so an ACK from another handoff round cannot confirm the current route or release its `SUBSCRIBE` early. A twelfth fix closes the queued-restart/BFCache race: `WorkerClusterRuntime.start()` installs lifecycle listeners before checking visibility, so a restart queued behind an asynchronous stop observes a `pagehide` that arrives during cleanup and waits suspended for `pageshow` instead of reconnecting the hidden page.
|
|
8
|
+
A fifteenth fix preserves operations parked on that gate when an explicit `start()` supersedes the automatic timer: cancelling the timer no longer releases the gate, so if the manual open also fails those operations stay queued for the next automatic or demand-driven recovery instead of being silently dropped.
|
|
9
|
+
A sixteenth fix keeps a rejected teardown in both failure ledgers: `stop()` still resolves and reports through `onError`, but the recovery ledger now retains the same stop failure as the unified `lastFailure` record until an explicit `start()` resets both.
|
|
10
|
+
A thirteenth fix binds every inbound `CONTROL/SUBSCRIBE` to the durable route: a delayed frame from an earlier assignment round can no longer make a non-owner subscribe, and it cannot confirm a pending graceful handoff before the matching `ROUTE_RELEASED`; legitimate same-route subscriptions and normal handoff ACKs remain unchanged.
|
|
11
|
+
A fourteenth fix invalidates operations parked on the recovery gate when page-hide/stop cancels the recovery cycle: an immediate explicit `start()` can re-establish subscriptions on the replacement transport, but a stale waiter can no longer replay afterward and race that restart with a duplicate subscription.
|
|
12
|
+
|
|
4
13
|
|
|
5
14
|
## 0.20.91 delivered scope
|
|
6
15
|
|
package/docs/zh/architecture.md
CHANGED
|
@@ -86,7 +86,7 @@ graph TB
|
|
|
86
86
|
| **Sticky**(粘性) | — | 已有 route 在 owner 存活期间保持归属;负载和可见性只影响全新 route 的放置。 |
|
|
87
87
|
| **Heartbeat**(心跳) | `heartbeatAt` | Worker 周期性写入存储的存活标记。超过 `workerTtlMs` 未刷新即视为死亡。 |
|
|
88
88
|
| **Handoff**(交接) | `handoffFromWorkerId` | 把 Topic 从旧 owner 移交给新 owner(如 `pagehide` 时)的流程,使用严格的释放-确认协议,保证同一 Topic 不会被两个 Worker 同时拥有。 |
|
|
89
|
-
| **Generation**(代次) | `generation` | 每条 route
|
|
89
|
+
| **Generation**(代次) | `generation` | 每条 route 上的单调递增计数器。交接确认必须精确引用当前 route 的代次,因此其他交接轮次的迟到确认会被忽略。 |
|
|
90
90
|
| **Local mode**(本地模式) | `coordinated: false` | storage 或 BroadcastChannel 不可用时的降级运行:无跨 Tab 路由,仅使用本 Tab 自己的 transport。 |
|
|
91
91
|
|
|
92
92
|
## 存储结构
|
|
@@ -136,7 +136,7 @@ interface WorkerRoute {
|
|
|
136
136
|
|
|
137
137
|
`generation` 在每次重新分配时递增,交接握手必须匹配该值;`handoffFromWorkerId` 记录优雅迁移时的前任 owner。上面的接口与当前协议一致——这两个字段如何驱动接管见 [故障转移](#故障转移)。
|
|
138
138
|
|
|
139
|
-
路由不保存原始 Topic 字符串或 payload。真实 owner 收到 `CONTROL/SUBSCRIBE` 时,原始 Topic 字符串只通过 BroadcastChannel
|
|
139
|
+
路由不保存原始 Topic 字符串或 payload。真实 owner 收到 `CONTROL/SUBSCRIBE` 时,原始 Topic 字符串只通过 BroadcastChannel 内存消息传递。接收方只有在持久化 route 当前指向自己时才接受该控制帧;来自较早分配轮的迟到帧会被丢弃,等待 `ROUTE_RELEASED` 的交接也只能由精确匹配的 ACK 确认。`confirmedAt` 在 owner 处理控制消息后写入;在路由确认之前,持有原始 Topic 字符串的订阅方 Runtime 会重发 `SUBSCRIBE`,以从"有路由但无真实订阅"的 BroadcastChannel 消息丢失中恢复。
|
|
140
140
|
|
|
141
141
|
### `topic`、`topicKey`、`tabId`、`workerId` 与 BroadcastChannel 的关联
|
|
142
142
|
|
|
@@ -309,7 +309,7 @@ SDK 不依赖 `storage` 事件驱动协调,控制通知使用 BroadcastChannel
|
|
|
309
309
|
|
|
310
310
|
| 类型 | 方向 | 用途 |
|
|
311
311
|
|---|---|---|
|
|
312
|
-
| `CONTROL` | 点对点(A → B) | 请求目标 Worker 对某 Topic 执行 `SUBSCRIBE`、`UNSUBSCRIBE` 或 `PUBLISH`。携带 `action`、`topic`、`topicKey`、`targetWorkerId` 和可选 `data
|
|
312
|
+
| `CONTROL` | 点对点(A → B) | 请求目标 Worker 对某 Topic 执行 `SUBSCRIBE`、`UNSUBSCRIBE` 或 `PUBLISH`。携带 `action`、`topic`、`topicKey`、`targetWorkerId` 和可选 `data`。只有持久化 route 当前指向目标且不在等待 `ROUTE_RELEASED` 时,`SUBSCRIBE` 才会被执行。 |
|
|
313
313
|
| `EVENT` | 广播(owner → 所有 Tab) | 把 transport 投递给 owner Worker 的 publication 扇出到所有 Tab。携带 `eventType` 和 `payload`。 |
|
|
314
314
|
| `REGISTRY` | 广播 | 注册表或路由写入后通知所有 Tab 立即 reconcile,而不是等下一轮心跳。 |
|
|
315
315
|
| `ROUTE_RELEASED` | 点对点(旧 owner → 新 owner) | 确认一次优雅迁移;只有 route `generation` 匹配的新 owner 才允许发送 `SUBSCRIBE`(见故障转移)。 |
|
|
@@ -509,11 +509,14 @@ Transport 消息 → isAssigned(topic)? → 是 → broadcastEvent(EVENT)
|
|
|
509
509
|
|
|
510
510
|
以下不变量由回归测试固化(见 `tests/stability.test.ts` 与 `tests/replay-persistence.test.ts`),后续重构必须继续保持:
|
|
511
511
|
|
|
512
|
-
- **
|
|
512
|
+
- **SUBSCRIBE 路由绑定。** 入站 `CONTROL/SUBSCRIBE` 只有在持久化 route 当前指向接收方、且该 route 不是未确认的优雅交接时才会被接受。来自较早分配轮的迟到帧不能加入 ownership、不能订阅 transport,也不能确认 route;等待中的交接只能由精确匹配的 `ROUTE_RELEASED` 授权。ownership 以当前 route 记录为准,而不以控制帧到达顺序为准。
|
|
513
|
+
- **Handoff ACK 有效性。** `ROUTE_RELEASED` 只有在 route 仍指向接收方、释放来自记录的 `handoffFromWorkerId`、且 ACK generation 与存储 route 的 generation 精确相等时才被接受。来自其他交接轮次的重复或迟到 ACK(如 a↔b 反复交接)会被丢弃,不能确认当前 route。
|
|
513
514
|
- **Replay 持久化清理顺序。** 排队在当前任务之后的批量持久化 flush 会与竞速的清理操作对账:`unsubscribe` 与 `clearReplayTopic` 丢弃该 topic 的待写条目,`clearReplayBefore` 丢弃早于截止时间的条目,`suspend()`/`stop()` 则丢弃整个待写批次,避免它在新生命周期代际下启动。已清理或属于已停止会话的历史不会被在途 flush 复活。
|
|
514
515
|
- **存储写失败恢复。** 合并写入按指数退避重试(50 ms → 1.6 s 封顶)。结构性失败的关键在 5 次尝试后被丢弃(伴随 `console.warn`),且不会永久阻塞其他排队 key;队列完全清空或 `clear()` 取消重试后,退避延迟重置。
|
|
515
516
|
- **Transport 恢复预算。** 自动恢复由冷却时间限速、由 `recovery.maxAttempts` 限量,预算耗尽后标记 `exhausted`。成功的重开会重置尝试计数与 exhausted 标记;transport 宕机时显式 `subscribe` 仍可手动恢复。自动调度本身不会重开连接:后端必须先释放失效连接,重试才能创建或重开 socket。`WebSocketTransport` 只在 socket `open` 后 resolve `start()`;握手前的 `error`/`close` 或 `connectTimeoutMs` 超时都会 reject。它仅在 socket 有效期间将其标记为 active;`error`/`close` 会立即失效,下一次 `start()` 在调用工厂前清除旧引用,因此被取代 socket 的迟到回调会被忽略。
|
|
516
517
|
- **BFCache 挂起。** Tab 隐藏时停止 transport、递增持久化重试 generation(取消在途重试且不对外报错),同时暂停 trace metrics 与 dedup/replay 周期清理并门控分发;`pageshow` 或显式 `start()` 会重开 transport、恢复这些周期资源,并且每轮循环只重建一次订阅。
|
|
518
|
+
- **恢复 waiter 失效。** 每个停靠在 recovery gate 上的操作都会捕获当前恢复取消 generation;`stop()` 与 `suspendTransport()` 取代恢复周期时会递增该 generation,因此即使 waiter 的微任务在紧随其后的显式 `start()` 之后才运行,也不能把操作重放到替换 transport。重建后的 cluster 对每个已分配 topic 只负责一次重新订阅,旧操作不能再发出重复订阅。
|
|
519
|
+
- **隐藏文档的激活守卫。** `WorkerClusterRuntime.start()` 会先安装 lifecycle listener 再检查可见性。若启动时文档已隐藏,则只记录挂起状态,不注册 worker、不打开 channel,也不启动 heartbeat;因此排队在异步 stop 之后的重启不会漏掉清理期间的 `pagehide` 并在后台重连,后续 `pageshow` 会由已安装的 listener 恢复。
|
|
517
520
|
- **挂起态就绪判定。** 挂起中的 bus 会把 `startPromise` 复用为 `pendingStop`(即 chained `transport.stop()` 的 gate),该 Promise 只能证明清理完成,不能证明可以承载数据。`ready()` 在 `stopping` 门之后检查 `suspended`,以挂起态错误 reject,而不是返回 stop gate;`pageshow`/`reopenTransport()` 与显式 `start()` 会清除标记并安装真正的重开 Promise,使 `ready()` 跟随最新生命周期意图。`getHealthSummary()` 原本就报告 `{ healthy: false, state: 'suspended' }`,reject 让 `ready()` 与该判定保持一致。由于 `pagehide` 会独立于 transport 暂停 cluster,显式 `start()` 在解除挂起时也必须一并恢复 cluster;否则 bus 会报告 transport 健康,而 channel listener、heartbeat 与 route 分配仍保持休眠直到下一次 `pageshow`,所有入站 publication 都会因 `isAssigned()` 对照已清空的分配表而被丢弃。
|
|
518
521
|
- **交接通道关闭顺序。** `pause()` 将物理 `channel.close()` 推迟一个任务。同步关闭会丢弃仍在排队等待投递的消息(包括交接的 `ROUTE_RELEASED`),使交接目标持有未确认路由。
|
|
519
522
|
- **悬挂交接恢复。** 若前任 owner 已消失而其 `ROUTE_RELEASED` 始终未到达(高负载下通道消息丢失,或 route 写入与 ACK 发送之间崩溃),reconcile 循环会在该未确认交接悬挂超过一个 worker TTL(默认 10 秒)后重新选举存活 owner:路由以全新 generation 重写并清除交接标记,使常规确认路径得以完成(已有回归固化)。年龄门限很关键——刚写入的未确认路由可能只是在等确认落盘,不能误判为悬挂;而只要前任 owner 仍然存活,新 owner 会继续等待,因此严格交接的无重叠保证不受影响。
|
|
@@ -526,7 +529,7 @@ DataBus 将"业务订阅意图"与"transport 当前订阅状态"分离。transpo
|
|
|
526
529
|
|
|
527
530
|
内置 Centrifuge transport 也会保留自己的 Subscriptions 并做协议层重连。两层恢复都要求 `subscribe` / `unsubscribe` 幂等。
|
|
528
531
|
|
|
529
|
-
运行期 `error` 会有意保留 `transportReady`:该标记记录「本次会话中已安装的 transport 曾成功打开」,使 `ready()` 跟随 transport 而不是随协议连接抖动。transport 的*操作*由独立的恢复门(recovery gate)控制:当自动或按需重开尚未完成时,`runTransport()` 会把新的 `subscribe` / `publish` 挂在该门之后,而不是写入刚刚上报 `error` 的连接;门只在重开成功(或 transport 自愈回到 `connected`)后释放,此时所有挂起的操作才在可用 transport
|
|
532
|
+
运行期 `error` 会有意保留 `transportReady`:该标记记录「本次会话中已安装的 transport 曾成功打开」,使 `ready()` 跟随 transport 而不是随协议连接抖动。transport 的*操作*由独立的恢复门(recovery gate)控制:当自动或按需重开尚未完成时,`runTransport()` 会把新的 `subscribe` / `publish` 挂在该门之后,而不是写入刚刚上报 `error` 的连接;门只在重开成功(或 transport 自愈回到 `connected`)后释放,此时所有挂起的操作才在可用 transport 上执行。自动尝试失败后门保持关闭,但下一次显式操作可以立即触发按需重开,而不必再等一个冷却周期;`recovery.maxAttempts` 耗尽后会释放门,使停靠操作遵循文档化的显式重试路径。若等待被 `stop()` / 页面隐藏取代,则释放门前会使这一代停靠操作失效:随后的显式 `start()` 会在替换连接上重建订阅,而旧 waiter 不会再次执行并造成重复订阅。`disconnected` 是干净关闭而非可恢复失败:它不会调度后台 DataBus 重开,只有显式 `start()`、页面恢复、transport 自身的重连,或后续的 transport 操作才会回到 `connected`。最后一条路径很关键:transport 一旦真正到达过 `connected` 后再上报 `disconnected`,ready 快速路径就会被拒绝;这类干净关闭后到达的 `subscribe()` / `publish()` 会被挂在同一个恢复门之后并触发一次按需重开,随后在替换连接上 flush,而不再写入已关闭的连接。对于在首次 `connected` 之前就 resolve `start()` 的 transport(worker 型后端异步上报连接状态),操作仍会直接交给它,因为此时的 `disconnected` 表示「尚未连接」,而不是「已建立的连接断开」。
|
|
530
533
|
|
|
531
534
|
## 生命周期状态机
|
|
532
535
|
|
|
@@ -587,7 +590,7 @@ DataBus 将"业务订阅意图"与"transport 当前订阅状态"分离。transpo
|
|
|
587
590
|
- **启动期间隐藏**:`pagehide` 在 `openTransport` 飞行中触发时,`suspendTransport()` 设置 `suspended = true`,并在飞行中的 start 之后链式执行 `transport.stop()`。`openTransport` 的 catch 路径检测到 `suspended` 后放弃本次 open,不视为失败。当重复的 hide/show 让排队的 resume opening 与更早的 stop gate 交错时,挂起会安装新的串行 stop gate 并恢复 `startPromise === pendingStop` 不变量,使下一次 `pageshow` 真正重开,而不是复用已被淘汰的 opening 并永久停留在挂起状态。
|
|
588
591
|
- **被取代 open 失效**:每次全新 start、reopen、suspend 和 stop 都会推进 `lifecycleEpoch`。open 会捕获自己的 epoch;一旦更新的转换接管生命周期,旧 open 的 status/message/error 回调会被忽略,也不会再把 transport 标记为 ready 或执行失败清理。因此 `stop()` 会等待未完成的 open/reopen,并阻止被取代的 open 在 stop 完成后变为 ready。
|
|
589
592
|
- **恢复冷却**:transport 上报 `error` 且 `started` 为 true、`stopping` 为 false 时,`updateStatus` 在 `RECOVERY_COOLDOWN_MS`(1000 ms)后调度自动 `reopenTransport()`。冷却窗口内的第二次错误被抑制,防止紧循环重试。
|
|
590
|
-
- **传输恢复门**:调度重开的同时会抬起恢复门,使冷却期间发起的 `subscribe` / `publish` 无法到达失效连接,等重开成功后才释放。自动尝试失败后门刻意保持关闭:下一次显式操作会立即触发按需重开,而不是等待下一个限速尝试,挂起的操作则在该次成功后一并 flush
|
|
593
|
+
- **传输恢复门**:调度重开的同时会抬起恢复门,使冷却期间发起的 `subscribe` / `publish` 无法到达失效连接,等重开成功后才释放。自动尝试失败后门刻意保持关闭:下一次显式操作会立即触发按需重开,而不是等待下一个限速尝试,挂起的操作则在该次成功后一并 flush。显式 `start()` 取代自动 timer 时会取消该次尝试并重置失败账本,但保持门关闭;若手动重开也失败,停靠操作会继续等待后续自动或按需重开,而不会随被取代的 opening 一起静默丢弃。`recovery.maxAttempts` 耗尽时会释放门,以保留显式重试路径;若被 `stop()` / `suspendTransport()` 取代,则在释放门前使全部停靠 waiter 失效,因此显式重启及其 cluster 重新订阅之后不会再多执行一次过期操作。运行期 `error` 不会清除 `transportReady`——清除它会让调用方流量绕过冷却重开,并在连接尚未承载数据时报告 ready。
|
|
591
594
|
- **暂停期间停止**:`stop()` 设置 `stopping = true`,阻止 `suspendTransport()` 执行。清理过程会 await `startPromise` 和 `pendingStop`,确保任何飞行中的 open 或 stop 完成后才执行最终的 `transport.stop()`。
|
|
592
595
|
|
|
593
596
|
## 降级
|
package/docs/zh/benchmarks.md
CHANGED
|
@@ -2,20 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
# 浏览器基准趋势
|
|
4
4
|
|
|
5
|
-
> 数据截至 2026-09-
|
|
5
|
+
> 数据截至 2026-09-17,基于 27 份归档的 `bench-results/browser-*.json` 报告(运行 `pnpm bench:browser` 追加一份;用 `node scripts/bench-trend.mjs` 重新生成本文档)。
|
|
6
6
|
|
|
7
7
|
发布门禁的对比基线是最近两份报告之间的 `pnpm bench:compare --fail-above-pct 50`(50% 上限用于吸收共享 runner 的噪声)。本文记录长期趋势:数值为逐指标延迟,越低越好;历史最优为本机观察到的最健康一次运行。
|
|
8
8
|
|
|
9
9
|
<!-- BENCH-TREND:BEGIN (machine-generated table) -->
|
|
10
10
|
| 指标 | 上次 (ms) | 本次 (ms) | Δ | 历史最优 (ms) |
|
|
11
11
|
|---|---|---|---|---|
|
|
12
|
-
| publish per-message (ms, lower is better) — dedicated |
|
|
13
|
-
| publish per-message (ms, lower is better) — shared |
|
|
14
|
-
| wildcard dispatch ×1000 (ms, lower is better) | 7
|
|
15
|
-
| publishBatch ×1000 (ms, lower is better) | 4.
|
|
16
|
-
| dedup ×1000 (ms, lower is better) |
|
|
17
|
-
| trace + publish ×1000 (ms, lower is better) | 5
|
|
18
|
-
| first-packet cold dispatch (ms, lower is better) | 0.1 | 0 |
|
|
12
|
+
| publish per-message (ms, lower is better) — dedicated | 52.4371 | 50.838 | -1.60 | 35.3543 |
|
|
13
|
+
| publish per-message (ms, lower is better) — shared | 34.2414 | 34.4971 | +0.26 | 33.6784 |
|
|
14
|
+
| wildcard dispatch ×1000 (ms, lower is better) | 5.7 | 6.4 | +0.70 | 0.1 |
|
|
15
|
+
| publishBatch ×1000 (ms, lower is better) | 4.2 | 4.3 | +0.10 | 0.4 |
|
|
16
|
+
| dedup ×1000 (ms, lower is better) | 23.2 | 13.3 | -9.90 | 0 |
|
|
17
|
+
| trace + publish ×1000 (ms, lower is better) | 5 | 5 | +0.00 | 4.5 |
|
|
18
|
+
| first-packet cold dispatch (ms, lower is better) | 0.1 | 0.1 | +0.00 | 0 |
|
|
19
19
|
<!-- BENCH-TREND:END -->
|
|
20
20
|
|
|
21
21
|
说明:
|
package/docs/zh/capabilities.md
CHANGED
|
@@ -43,10 +43,12 @@
|
|
|
43
43
|
|
|
44
44
|
| 不变量 | 领域 | 被回归固化的保证 |
|
|
45
45
|
|---|---|---|
|
|
46
|
-
|
|
|
46
|
+
| SUBSCRIBE 路由绑定 | 协调 | 仅当持久化 route 当前指向接收方、且不在等待 `ROUTE_RELEASED` 时才执行入站 `CONTROL/SUBSCRIBE`;较早分配轮的迟到帧不能创建 ownership、订阅 transport 或确认悬挂交接 |
|
|
47
|
+
| 交接 ACK 有效性 | 协调 | 仅当 route 仍指向接收方、释放方匹配 `handoffFromWorkerId`、且 ACK 代数与存储 route 代数精确相等时才接受 `ROUTE_RELEASED`——其他交接轮次的过期 ACK(如 a↔b 乒乓)会被丢弃 |
|
|
47
48
|
| 回放持久化清理顺序 | 持久性 | 排队中的批量 flush 会按先到清理过滤(`unsubscribe`/`clearReplayTopic` 丢弃该 topic 的待写条目,`clearReplayBefore` 丢弃早于截止时间的条目,`suspend()`/`stop()` 丢弃整个待写批次);已清或已停止会话的历史不会被进行中的 flush 重新追加 |
|
|
48
49
|
| 存储写入恢复 | 协调 | 合并写以指数退避重试(50 ms → 1.6 s 上限);结构性失败键在 5 次后丢弃(并 `console.warn`)而不阻塞其他排队键;队列清空或 `clear()` 取消后退避重置 |
|
|
49
50
|
| 传输恢复预算 | 生命周期 | 自动恢复由冷却间隔节流、以 `recovery.maxAttempts` 为界,预算耗尽时报 `exhausted`;成功重开后重置尝试计数与 exhausted,断线传输上的显式 `subscribe` 仍可手动恢复 |
|
|
51
|
+
| 恢复 waiter 生命周期 | 生命周期 | 停靠在 transport recovery gate 上的操作会捕获恢复取消 generation;`stop()` / 页面隐藏会使其失效,因此紧随其后的显式 `start()` 在 replacement transport 重建订阅后,不会再执行过期操作造成重复订阅。相对地,显式 `start()` 取代自动恢复 timer 时会保留门及其停靠操作;若手动重开也失败,后续自动或按需重开仍可成功回放这些操作 |
|
|
50
52
|
| BFCache 挂起 | 生命周期 | 隐藏页面停掉传输并静默取消进行中的持久化重试;pageshow 重开传输并每个周期恰好一次重建订阅 |
|
|
51
53
|
| 交接通道关闭顺序 | 协调 | `pause()` 将物理 `channel.close()` 推迟一个任务,确保排队中的交接帧(含 `ROUTE_RELEASED`)先冲刷再关闭 |
|
|
52
54
|
| 丢失与恢复矩阵 | 协调 | 每条协调消息都有有界的恢复路径(未确认 SUBSCRIBE 的重发、REGISTRY 催促、丢失 ACK 的 TTL 清理 + 重新选举);传输断线窗口内丢失的发布是唯一有文档佐证的不恢复损失 |
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
3. 依赖安全门禁:`pnpm audit --registry=https://registry.npmjs.org`(配置的镜像 registry 缺少 audit 端点;CI 在 verify job 中于公共 registry 运行)。任一已知漏洞公告即视为发布失败;`pnpm-workspace.yaml` overrides 钉住补丁版本。
|
|
23
23
|
4. 浏览器基准回归门禁:运行两次 `pnpm bench:browser` 后执行 `pnpm bench:compare --fail-above-pct 50`(50% 上限用于吸收共享 runner 的无关噪声,参见已知的共享 runner 抖动说明);基线迁移(例如某指标从空操作变为真实路径)属预期内的一次性失败。用 `pnpm bench:trend` 刷新长期趋势文档,表格变化时一并提交。
|
|
24
24
|
5. 用 `npm pack --dry-run --json` 确认发布包只包含预期文件。
|
|
25
|
-
6.
|
|
25
|
+
6. 在功能分支提交并推送该分支,PR 验证通过后使用 squash 或 fast-forward 合入(不创建 merge commit)。获取合入后的精确提交并打 tag,只推送该版本 tag;禁止直接推送 `main`/`master` 或 force-push。工作流运行 `node scripts/verify-release-version.mjs`,要求 `RELEASE_TAG` 等于 `v` 加 package 版本,且 CHANGELOG 中恰好有一个非空的对应版本章节。
|
|
26
26
|
|
|
27
27
|
## 安全与依赖扫描
|
|
28
28
|
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
## 打 tag 的发布工作流
|
|
32
32
|
|
|
33
|
-
推送版本 tag 会触发 `Release` GitHub Action:先跑 `pnpm check` 与 `pnpm lint`(tag 可能指向从未通过 CI lint 步骤的提交),再跑 `verify:compat` 与 `verify:pack`,从 `CHANGELOG` 对应章节生成 GitHub release,配置了 `NPM_TOKEN` 时自动发布到 npm,然后运行与手动执行相同预算的**阻塞式**消费者验证(`PUBLISHED_VERIFY_ATTEMPTS=48`、`PUBLISHED_VERIFY_DELAY_MS=7500`,即 6 分钟上限)。已发布包若无法被干净消费者导入,工作流即失败——任何 `verify:published`
|
|
33
|
+
推送版本 tag 会触发 `Release` GitHub Action:先跑 `pnpm check` 与 `pnpm lint`(tag 可能指向从未通过 CI lint 步骤的提交),再跑 `verify:compat` 与 `verify:pack`,从 `CHANGELOG` 对应章节生成 GitHub release,配置了 `NPM_TOKEN` 时自动发布到 npm,然后运行与手动执行相同预算的**阻塞式**消费者验证(`PUBLISHED_VERIFY_ATTEMPTS=48`、`PUBLISHED_VERIFY_DELAY_MS=7500`,即 6 分钟上限)。已发布包若无法被干净消费者导入,工作流即失败——任何 `verify:published` 失败都应视为发布失败。若为 registry 传播延迟或基础设施故障,针对不变的 tag 重跑工作流;若为产物缺陷,发布新的 patch 版本。禁止移动或重用已发布 tag。未配置 token 时跳过发布步骤,但验证仍会针对 npm 上已有的版本(例如手动发布的)通过。
|
|
34
34
|
|
|
35
35
|
## 发布(手动场景)
|
|
36
36
|
|
package/docs/zh/roadmap.md
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
# 路线图
|
|
2
2
|
|
|
3
|
-
0.20.91
|
|
3
|
+
0.20.91 已发布;0.20.92 处于 RELEASE_FREEZE,等待 PR 与 registry 验证。项目会先持续完成可靠性发布,再进入 1.0.0 稳定性冻结。
|
|
4
|
+
|
|
5
|
+
## 0.20.92 冻结范围
|
|
6
|
+
|
|
7
|
+
当前开发线继续验证 predecessor reopen 结算、排队启动就绪与 stop promise 清理等生命周期错误路径。该开发线现已落地十六处修复。第一处让共享 stop gate 先于同步 teardown 前奏安装:从同步 STOP lifecycle trace 事件重入的 `stop()` 会共享同一次 teardown,而不是再调用一次 `transport.stop()`。第二处在 START trace 发出前安装 lifecycle epoch 与 `startPromise`:若同步 trace/status 回调重入 `stop()`,外层 `start()` 会立即停止后续 timer、cluster 与 topic 启动,旧 opening 由 epoch guard 放弃,transport 不会被重新打开。第三处把相同的「先安装 lifecycle、再发同步回调」顺序应用到 `reopenTransport()` 的 CONNECTING 状态通知,使恢复期间重入的 stop 能取消本次 reopen,而不是在 teardown 后重新打开 transport。第四处让同步 RESUME trace 回调内的 stop 同时取消显式 `start()` 与原生 `pageshow` 恢复;`WorkerClusterRuntime` 通过 lifecycle generation 阻止外层 pageshow 在 `onResume` 已停止或暂停 cluster 后再次 `activate()`。此前这些场景都可能留下 `state: stopped` 但 transport 或 cluster 仍活跃的半停止状态。第五处让已经停靠在 recovery gate 上的 transport 操作不再因等待中的自动恢复尝试失败而滞留:`runTransport()` 现在统计停靠操作数,自动尝试失败时若仍有 waiter 会立即发起一次 on-demand reopen,使在 cooldown 期间发出的 `publish()` / `subscribe()` 自身即可驱动恢复,而不必等待之后某个无关操作。demand reopen 自身失败时仍会重新武装 flag 留待后续操作重试,而不会在自身失败上自循环;一旦 reopen 成功,全部 waiter 会按序 flush。cluster-key 隔离保证现在由双 runtime 回归固定:不同租户可独立拥有同一 topic、publication 不会跨命名空间边界、持久化 key 使用不同 opaque hash 且不暴露明文 key。第六处 replay retention 修复会在 suspend/resume 期间旧 cleanup 事务仍在收尾时,把新排队的最新 cutoff 交给新一轮 cleanup,避免它一直滞留到未来某个无关 publication 才被处理。第七处在 `reopenTransport()` 发出同步 CONNECTING 通知前清空 `transportReady`,避免同一 tick 内第二个操作把仍在关闭的旧 transport 误判为可用连接;所有操作都会继续停靠在 opening 上,并在 replacement 打开后按序 flush。第八处让 pagehide suspension 续体具备 epoch 感知:若同步 DISCONNECTED status 回调按公开恢复路径调用 `start()`,旧 hide 续体会被放弃,不会在 replacement 打开后再次停止 transport 并留下错误的 healthy 状态。第九处修复保留 durable hydration 与实时流量的回放顺序:若 `load()` 尚未完成时已有 publication 写入,加载快照会排在实时消息之前,数量裁剪因此保留最新的实时消息,而不会把旧历史误认为更新内容。第十处让 replay hydration 能跨 lifecycle 替换:清除全部、清除单 topic、退订与 cutoff 裁剪会应用到仍在进行的加载快照,避免已清除历史复活;suspend/restart 会取消被取代的 load 并启动新一代 hydration;显式 stop/start 后也会重新加载 durable history,而不是留下空 ring。第十一处补上严格交接的 generation 缺口:`ROUTE_RELEASED` 必须与存储 route 的 generation 精确相等,其他交接轮次的 ACK 不能确认当前 route,也不能提前释放其 `SUBSCRIBE`。第十二处修复排队重启与 BFCache 的竞态:`WorkerClusterRuntime.start()` 会先安装 lifecycle listener 再检查可见性,因此排队在异步 stop 之后的重启能观察到清理期间到达的 `pagehide`,保持挂起并等待 `pageshow`,而不会重新连接隐藏页面。
|
|
8
|
+
第十三处把每个入站 `CONTROL/SUBSCRIBE` 绑定到持久化 route:较早分配轮的迟到帧不能再让非 owner 订阅 transport,也不能在匹配的 `ROUTE_RELEASED` 之前确认悬挂交接;同一 route 的合法订阅与正常交接 ACK 保持不变。
|
|
9
|
+
第十四处修复 recovery gate 取消竞态:当 pagehide/stop 取代恢复周期时,已停靠在门上的操作会失效;紧随其后的显式 `start()` 可以在 replacement transport 上重建订阅,而旧 waiter 不能再于重启后重放并与该过程竞争出重复订阅。
|
|
10
|
+
第十五处修复显式 `start()` 取代自动恢复 timer 时停靠操作被静默丢弃的问题:取消 timer 不再释放 recovery gate;若该显式重开也失败,停靠操作会保留到下一次自动或按需恢复成功后再回放。
|
|
11
|
+
第十六处修复让被拒绝的 teardown 同时保留在两个失败账本中:`stop()` 仍会 resolve 并通过 `onError` 上报,但 recovery 账本会与统一的 `lastFailure` 记录保持同一次 stop 失败,直到显式 `start()` 同时重置两者。
|
|
12
|
+
|
|
4
13
|
|
|
5
14
|
## 0.20.91 已完成范围
|
|
6
15
|
|