svelte-adapter-uws 0.5.7 → 0.5.8
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/files/handler.js +19 -5
- package/package.json +1 -1
- package/plugins/presence/server.js +34 -12
package/files/handler.js
CHANGED
|
@@ -366,12 +366,24 @@ const app = is_tls
|
|
|
366
366
|
: uWS.App();
|
|
367
367
|
|
|
368
368
|
// - Cross-worker pub/sub relay (batched) ------------------------------------
|
|
369
|
-
// Batch postMessage calls within a single
|
|
370
|
-
// publishes N events sends one structured-clone across the thread
|
|
371
|
-
// instead of N. No-op in single-process mode (parentPort is null).
|
|
369
|
+
// Batch postMessage calls within a single event-loop iteration. A SvelteKit
|
|
370
|
+
// action that publishes N events sends one structured-clone across the thread
|
|
371
|
+
// boundary instead of N. No-op in single-process mode (parentPort is null).
|
|
372
|
+
//
|
|
373
|
+
// Why `setTimeout(0)` and not `queueMicrotask`: uWS dispatches each WS message
|
|
374
|
+
// as its own JS task, and N-API drains microtasks at the C++/JS boundary
|
|
375
|
+
// between tasks. A microtask-deferred flush fires BEFORE the next socket's
|
|
376
|
+
// handler runs, so cross-socket coalescing is impossible at the microtask
|
|
377
|
+
// level - N publishes from N socket handlers in the same iteration produce N
|
|
378
|
+
// postMessage structured-clones instead of one batched. `setTimeout(0)` lands
|
|
379
|
+
// in libuv's timers phase, which fires only after the poll phase has
|
|
380
|
+
// dispatched every ready socket message in the current iteration. Same
|
|
381
|
+
// structural choice the 0.5.6 cursor always-tick rewrite locked in.
|
|
372
382
|
|
|
373
383
|
/** @type {Array<{topic: string, envelope: string}> | null} */
|
|
374
384
|
let relayBatch = null;
|
|
385
|
+
/** @type {ReturnType<typeof setTimeout> | null} */
|
|
386
|
+
let relayTimer = null;
|
|
375
387
|
|
|
376
388
|
/**
|
|
377
389
|
* @param {string} topic
|
|
@@ -380,12 +392,14 @@ let relayBatch = null;
|
|
|
380
392
|
function batchRelay(topic, envelope) {
|
|
381
393
|
if (!relayBatch) {
|
|
382
394
|
relayBatch = [];
|
|
383
|
-
|
|
395
|
+
relayTimer = setTimeout(() => {
|
|
396
|
+
relayTimer = null;
|
|
384
397
|
if (relayBatch) {
|
|
385
398
|
parentPort.postMessage({ type: 'publish-batch', messages: relayBatch });
|
|
386
399
|
}
|
|
387
400
|
relayBatch = null;
|
|
388
|
-
});
|
|
401
|
+
}, 0);
|
|
402
|
+
if (relayTimer.unref) relayTimer.unref();
|
|
389
403
|
}
|
|
390
404
|
relayBatch.push({ topic, envelope });
|
|
391
405
|
}
|
package/package.json
CHANGED
|
@@ -302,12 +302,28 @@ export function createPresence(options = {}) {
|
|
|
302
302
|
|
|
303
303
|
/**
|
|
304
304
|
* Per-topic pending diff buffer: latest op per key wins. Joins and leaves
|
|
305
|
-
* happening on the same key in
|
|
306
|
-
* net change. Flushed once per
|
|
305
|
+
* happening on the same key in one event-loop iteration collapse so the
|
|
306
|
+
* wire only sees the net change. Flushed once per iteration via
|
|
307
|
+
* `setTimeout(() => flushDiffs(platform), 0)` armed when the first dirty
|
|
308
|
+
* entry lands.
|
|
309
|
+
*
|
|
310
|
+
* Why `setTimeout(0)` and not `queueMicrotask`: uWS dispatches each WS
|
|
311
|
+
* message as its own JS task, and N-API drains microtasks at the C++/JS
|
|
312
|
+
* boundary between tasks. A microtask-deferred flush fires BEFORE the
|
|
313
|
+
* next socket's handler runs, so cross-socket coalescing is impossible
|
|
314
|
+
* at the microtask level - a mass-join into a populated topic produces
|
|
315
|
+
* O(N) one-entry diffs instead of one batched diff. `setTimeout(0)`
|
|
316
|
+
* lands in libuv's timers phase, which fires only after the poll phase
|
|
317
|
+
* has dispatched every ready socket message in the current iteration -
|
|
318
|
+
* so all joins arriving together end up in one flush regardless of how
|
|
319
|
+
* many task boundaries separate them. Same structural choice the
|
|
320
|
+
* 0.5.6 cursor always-tick rewrite locked in.
|
|
321
|
+
*
|
|
307
322
|
* @type {Map<string, Map<string, { op: 'join' | 'leave', data: Record<string, any> }>>}
|
|
308
323
|
*/
|
|
309
324
|
const pendingDiffs = new Map();
|
|
310
|
-
|
|
325
|
+
/** @type {ReturnType<typeof setTimeout> | null} */
|
|
326
|
+
let diffFlushTimer = null;
|
|
311
327
|
|
|
312
328
|
/**
|
|
313
329
|
* @param {string} topic
|
|
@@ -323,15 +339,18 @@ export function createPresence(options = {}) {
|
|
|
323
339
|
pendingDiffs.set(topic, entries);
|
|
324
340
|
}
|
|
325
341
|
entries.set(key, { op, data });
|
|
326
|
-
if (
|
|
327
|
-
|
|
328
|
-
|
|
342
|
+
if (diffFlushTimer === null) {
|
|
343
|
+
diffFlushTimer = setTimeout(() => flushDiffs(platform), 0);
|
|
344
|
+
if (diffFlushTimer.unref) diffFlushTimer.unref();
|
|
329
345
|
}
|
|
330
346
|
}
|
|
331
347
|
|
|
332
348
|
/** @param {import('../../index.js').Platform} platform */
|
|
333
349
|
function flushDiffs(platform) {
|
|
334
|
-
|
|
350
|
+
if (diffFlushTimer !== null) {
|
|
351
|
+
clearTimeout(diffFlushTimer);
|
|
352
|
+
diffFlushTimer = null;
|
|
353
|
+
}
|
|
335
354
|
for (const [topic, entries] of pendingDiffs) {
|
|
336
355
|
/** @type {Record<string, Record<string, any>>} */
|
|
337
356
|
const joins = {};
|
|
@@ -554,21 +573,24 @@ export function createPresence(options = {}) {
|
|
|
554
573
|
wsTopics.clear();
|
|
555
574
|
topicPresence.clear();
|
|
556
575
|
pendingDiffs.clear();
|
|
557
|
-
|
|
576
|
+
if (diffFlushTimer !== null) {
|
|
577
|
+
clearTimeout(diffFlushTimer);
|
|
578
|
+
diffFlushTimer = null;
|
|
579
|
+
}
|
|
558
580
|
connCounter = 0;
|
|
559
581
|
},
|
|
560
582
|
|
|
561
583
|
/**
|
|
562
584
|
* Drain any buffered diff publishes synchronously. Tests use this
|
|
563
|
-
* to assert on the wire output without awaiting the
|
|
564
|
-
*
|
|
565
|
-
*
|
|
585
|
+
* to assert on the wire output without awaiting the next-tick
|
|
586
|
+
* setTimeout flush. Production code generally does not need to call
|
|
587
|
+
* it - the tick flush happens automatically. Useful when a caller
|
|
566
588
|
* needs presence state visible to other workers before its own
|
|
567
589
|
* synchronous block returns (e.g. before responding to an HTTP
|
|
568
590
|
* request that just triggered a leave).
|
|
569
591
|
*/
|
|
570
592
|
flushDiffs() {
|
|
571
|
-
if (
|
|
593
|
+
if (diffFlushTimer === null || !_platform) return;
|
|
572
594
|
flushDiffs(_platform);
|
|
573
595
|
},
|
|
574
596
|
|