@livestore/webmesh 0.4.0-dev.12 → 0.4.0-dev.13
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/.tsbuildinfo +1 -1
- package/dist/examples/reload-repro.d.ts +2 -0
- package/dist/examples/reload-repro.d.ts.map +1 -0
- package/dist/examples/reload-repro.js +136 -0
- package/dist/examples/reload-repro.js.map +1 -0
- package/dist/examples/starvation-repro-single-port.d.ts +2 -0
- package/dist/examples/starvation-repro-single-port.d.ts.map +1 -0
- package/dist/examples/starvation-repro-single-port.js +89 -0
- package/dist/examples/starvation-repro-single-port.js.map +1 -0
- package/dist/examples/starvation-repro.d.ts +2 -0
- package/dist/examples/starvation-repro.d.ts.map +1 -0
- package/dist/examples/starvation-repro.js +171 -0
- package/dist/examples/starvation-repro.js.map +1 -0
- package/dist/reload-handover.mesh.test.d.ts +2 -0
- package/dist/reload-handover.mesh.test.d.ts.map +1 -0
- package/dist/reload-handover.mesh.test.js +130 -0
- package/dist/reload-handover.mesh.test.js.map +1 -0
- package/dist/reload-reconnect.test.d.ts +2 -0
- package/dist/reload-reconnect.test.d.ts.map +1 -0
- package/dist/reload-reconnect.test.js +62 -0
- package/dist/reload-reconnect.test.js.map +1 -0
- package/package.json +3 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reload-repro.d.ts","sourceRoot":"","sources":["../../src/examples/reload-repro.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { MessageChannel } from 'node:worker_threads';
|
|
2
|
+
import { Duration, Effect, Schema, Stream, Fiber } from '@livestore/utils/effect';
|
|
3
|
+
import { WebChannel } from '@livestore/utils/effect';
|
|
4
|
+
import { PlatformNode } from '@livestore/utils/node';
|
|
5
|
+
// Minimal reload/reconnect repro on a single MessagePort.
|
|
6
|
+
// Two logical roles: 'leader' and 'clientSession' ping loops.
|
|
7
|
+
// Simulates a reload event by stopping old loops and starting new ones while heavy/blocked work occurs.
|
|
8
|
+
// Goal: demonstrate a single leader timeout around reload while clientSession pings keep succeeding.
|
|
9
|
+
const env = (k, d) => (process.env[k] ?? d).trim();
|
|
10
|
+
const PING_INTERVAL_MS = Number(env('PING_INTERVAL_MS', '200'));
|
|
11
|
+
const LEADER_TIMEOUT_MS = Number(env('LEADER_TIMEOUT_MS', '120'));
|
|
12
|
+
const CLIENT_TIMEOUT_MS = Number(env('CLIENT_TIMEOUT_MS', '500'));
|
|
13
|
+
const RELOAD_AT_MS = Number(env('RELOAD_AT_MS', '1500'));
|
|
14
|
+
const BLOCK_MS = Number(env('BLOCK_MS', '300'));
|
|
15
|
+
const HEAVY_SIZE = Number(env('HEAVY_SIZE', '100000'));
|
|
16
|
+
const HEAVY_BURST = Number(env('HEAVY_BURST', '20')); // number of heavy messages near reload
|
|
17
|
+
const DROP_WINDOW_MS = Number(env('DROP_WINDOW_MS', '120')); // drop leader pongs in this window after reload
|
|
18
|
+
const Ping = Schema.TaggedStruct('Ping', { who: Schema.String, id: Schema.String, t0: Schema.Number });
|
|
19
|
+
const Pong = Schema.TaggedStruct('Pong', { who: Schema.String, id: Schema.String, t0: Schema.Number });
|
|
20
|
+
const Heavy = Schema.TaggedStruct('Heavy', { n: Schema.Number, payload: Schema.String });
|
|
21
|
+
const Msg = Schema.Union(Ping, Pong, Heavy);
|
|
22
|
+
const pad = (n, ch = 'x') => (n <= 0 ? '' : ch.repeat(n));
|
|
23
|
+
const main = Effect.gen(function* () {
|
|
24
|
+
const payload = pad(HEAVY_SIZE);
|
|
25
|
+
console.log('[reload-repro] cfg', {
|
|
26
|
+
PING_INTERVAL_MS,
|
|
27
|
+
LEADER_TIMEOUT_MS,
|
|
28
|
+
CLIENT_TIMEOUT_MS,
|
|
29
|
+
RELOAD_AT_MS,
|
|
30
|
+
BLOCK_MS,
|
|
31
|
+
HEAVY_SIZE,
|
|
32
|
+
HEAVY_BURST,
|
|
33
|
+
});
|
|
34
|
+
const mc = new MessageChannel();
|
|
35
|
+
const a = yield* WebChannel.messagePortChannel({ port: mc.port1, schema: Msg });
|
|
36
|
+
const b = yield* WebChannel.messagePortChannel({ port: mc.port2, schema: Msg });
|
|
37
|
+
// A: responder + heavy sender near reload
|
|
38
|
+
let heavySent = 0;
|
|
39
|
+
const t0 = Date.now();
|
|
40
|
+
let dropLeaderNext = false;
|
|
41
|
+
yield* a.listen
|
|
42
|
+
.pipe(Stream.flatten(), Stream.tap((m) => Effect.gen(function* () {
|
|
43
|
+
if (Schema.is(Ping)(m)) {
|
|
44
|
+
console.log('[responder] recv', m.who, Date.now());
|
|
45
|
+
const now = Date.now();
|
|
46
|
+
if (m.who === 'leader' && dropLeaderNext) {
|
|
47
|
+
console.log('[responder] drop pong for leader in drop window');
|
|
48
|
+
dropLeaderNext = false;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
yield* a.send(Pong.make({ who: m.who, id: m.id, t0: m.t0 }));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
})), Stream.runDrain, Effect.forkScoped);
|
|
55
|
+
// Schedule a heavy burst around reload
|
|
56
|
+
yield* Effect.sleep(Duration.millis(RELOAD_AT_MS - 50));
|
|
57
|
+
yield* Effect.gen(function* () {
|
|
58
|
+
for (let i = 0; i < HEAVY_BURST; i++) {
|
|
59
|
+
yield* a.send(Heavy.make({ n: i, payload }));
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
// B: receive heavy; simulate blocking around reload window
|
|
63
|
+
yield* b.listen
|
|
64
|
+
.pipe(Stream.flatten(), Stream.tap((m) => Effect.sync(() => {
|
|
65
|
+
if (Schema.is(Heavy)(m))
|
|
66
|
+
heavySent += 1;
|
|
67
|
+
const now = Date.now();
|
|
68
|
+
// Busy block during a window around reload to induce a timeout
|
|
69
|
+
if (Math.abs(now - (t0 + RELOAD_AT_MS)) < 150 && BLOCK_MS > 0) {
|
|
70
|
+
const until = now + BLOCK_MS;
|
|
71
|
+
while (Date.now() < until) { }
|
|
72
|
+
}
|
|
73
|
+
})), Stream.runDrain, Effect.forkScoped);
|
|
74
|
+
// Explicit main-thread block on B exactly at reload to induce timeout (scheduled in parallel)
|
|
75
|
+
yield* Effect.gen(function* () {
|
|
76
|
+
yield* Effect.sleep(Duration.millis(RELOAD_AT_MS));
|
|
77
|
+
if (BLOCK_MS > 0) {
|
|
78
|
+
const until = Date.now() + BLOCK_MS;
|
|
79
|
+
while (Date.now() < until) { }
|
|
80
|
+
}
|
|
81
|
+
}).pipe(Effect.forkScoped);
|
|
82
|
+
let okLeader = 0;
|
|
83
|
+
let timeoutLeader = 0;
|
|
84
|
+
let okClient = 0;
|
|
85
|
+
let timeoutClient = 0;
|
|
86
|
+
// t0 set above
|
|
87
|
+
const sendPing = (who, timeoutMs, blockBeforeWaitMs = 0) => Effect.gen(function* () {
|
|
88
|
+
const id = Math.random().toString(36).slice(2);
|
|
89
|
+
const start = Date.now();
|
|
90
|
+
const wait = b.listen
|
|
91
|
+
.pipe(Stream.flatten(), Stream.filter(Schema.is(Pong)), Stream.filter((m) => m.id === id), Stream.runHead)
|
|
92
|
+
.pipe(Effect.asVoid);
|
|
93
|
+
yield* b.send(Ping.make({ who, id, t0: start }));
|
|
94
|
+
// Simulate main-thread work immediately after sending, before waiting for pong
|
|
95
|
+
if (blockBeforeWaitMs > 0) {
|
|
96
|
+
const until = Date.now() + blockBeforeWaitMs;
|
|
97
|
+
while (Date.now() < until) { }
|
|
98
|
+
}
|
|
99
|
+
const res = yield* wait
|
|
100
|
+
.pipe(Effect.timeout(Duration.millis(timeoutMs)), Effect.as('ok'), Effect.catchTag('TimeoutException', () => Effect.succeed('timeout')));
|
|
101
|
+
if (res === 'ok') {
|
|
102
|
+
if (who === 'leader')
|
|
103
|
+
okLeader += 1;
|
|
104
|
+
else
|
|
105
|
+
okClient += 1;
|
|
106
|
+
console.log('[ping]', who, 'ok', Date.now() - start);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
if (who === 'leader')
|
|
110
|
+
timeoutLeader += 1;
|
|
111
|
+
else
|
|
112
|
+
timeoutClient += 1;
|
|
113
|
+
console.log('[ping]', who, 'timeout');
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
// Deterministic timeline:
|
|
117
|
+
// t0 + RELOAD_AT_MS + 10ms: send leader ping (expect timeout due to responder dropping leader pongs in window)
|
|
118
|
+
// t0 + RELOAD_AT_MS: optional block on receiver (B) for BLOCK_MS (to mimic heavy)
|
|
119
|
+
// t0 + RELOAD_AT_MS + DROP_WINDOW_MS + 30ms: send clientSession ping (expect ok)
|
|
120
|
+
console.log('[reload]', 'start', Date.now());
|
|
121
|
+
yield* Effect.sleep(Duration.millis(RELOAD_AT_MS));
|
|
122
|
+
dropLeaderNext = true;
|
|
123
|
+
yield* Effect.sleep(Duration.millis(10));
|
|
124
|
+
console.log('[ping-loop] leader start', Date.now());
|
|
125
|
+
yield* sendPing('leader', LEADER_TIMEOUT_MS, 0);
|
|
126
|
+
console.log('[reload]', 'block done', Date.now());
|
|
127
|
+
// Client ping after drop window
|
|
128
|
+
yield* Effect.sleep(Duration.millis(DROP_WINDOW_MS + 30));
|
|
129
|
+
console.log('[ping-loop] clientSession start', Date.now());
|
|
130
|
+
yield* sendPing('clientSession', CLIENT_TIMEOUT_MS);
|
|
131
|
+
console.log('[ping-loop] clientSession stop', Date.now());
|
|
132
|
+
console.log('[ping-loop] leader stop', Date.now());
|
|
133
|
+
console.log('[summary]', JSON.stringify({ okLeader, timeoutLeader, okClient, timeoutClient, heavySent }));
|
|
134
|
+
});
|
|
135
|
+
PlatformNode.NodeRuntime.runMain(main.pipe(Effect.scoped));
|
|
136
|
+
//# sourceMappingURL=reload-repro.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reload-repro.js","sourceRoot":"","sources":["../../src/examples/reload-repro.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"starvation-repro-single-port.d.ts","sourceRoot":"","sources":["../../src/examples/starvation-repro-single-port.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { MessageChannel } from 'node:worker_threads';
|
|
2
|
+
import { Effect, Schema, Stream, Duration } from '@livestore/utils/effect';
|
|
3
|
+
import { WebChannel } from '@livestore/utils/effect';
|
|
4
|
+
import { PlatformNode } from '@livestore/utils/node';
|
|
5
|
+
// Standalone single-port repro (no Mesh): a single MessagePort carries both
|
|
6
|
+
// ping/pong and heavy payloads. Receiver blocks on heavy, causing ping timeouts.
|
|
7
|
+
// Co-located with WebMesh package for convenience.
|
|
8
|
+
const env = (k, d) => (process.env[k] ?? d).trim();
|
|
9
|
+
const HEAVY = env('HEAVY', '1') === '1';
|
|
10
|
+
const HEAVY_SIZE = Number(env('HEAVY_SIZE', '200000'));
|
|
11
|
+
const HEAVY_FREQ_MS = Number(env('HEAVY_FREQ_MS', '1'));
|
|
12
|
+
const BLOCK_MS = Number(env('BLOCK_MS', '10'));
|
|
13
|
+
const PING_INTERVAL_MS = Number(env('PING_INTERVAL_MS', '200'));
|
|
14
|
+
const TIMEOUT_MS = Number(env('TIMEOUT_MS', '400'));
|
|
15
|
+
const DURATION_MS = Number(env('DURATION_MS', '5000'));
|
|
16
|
+
const WITH_ACK = env('WITH_ACK', '0') === '1';
|
|
17
|
+
const Ping = Schema.TaggedStruct('Ping', { id: Schema.String, t0: Schema.Number });
|
|
18
|
+
const Pong = Schema.TaggedStruct('Pong', { id: Schema.String, t0: Schema.Number });
|
|
19
|
+
const Heavy = Schema.TaggedStruct('Heavy', { n: Schema.Number, payload: Schema.String });
|
|
20
|
+
const Msg = Schema.Union(Ping, Pong, Heavy);
|
|
21
|
+
const pad = (n, ch = 'x') => (n <= 0 ? '' : ch.repeat(n));
|
|
22
|
+
const main = Effect.gen(function* () {
|
|
23
|
+
const payload = pad(HEAVY_SIZE);
|
|
24
|
+
console.log('[single-port] config', {
|
|
25
|
+
HEAVY,
|
|
26
|
+
HEAVY_SIZE: payload.length,
|
|
27
|
+
HEAVY_FREQ_MS,
|
|
28
|
+
BLOCK_MS,
|
|
29
|
+
PING_INTERVAL_MS,
|
|
30
|
+
TIMEOUT_MS,
|
|
31
|
+
DURATION_MS,
|
|
32
|
+
WITH_ACK,
|
|
33
|
+
});
|
|
34
|
+
const mc = new MessageChannel();
|
|
35
|
+
const a = WITH_ACK
|
|
36
|
+
? yield* WebChannel.messagePortChannelWithAck({ port: mc.port1, schema: Msg })
|
|
37
|
+
: yield* WebChannel.messagePortChannel({ port: mc.port1, schema: Msg });
|
|
38
|
+
const b = WITH_ACK
|
|
39
|
+
? yield* WebChannel.messagePortChannelWithAck({ port: mc.port2, schema: Msg })
|
|
40
|
+
: yield* WebChannel.messagePortChannel({ port: mc.port2, schema: Msg });
|
|
41
|
+
// A: heavy producer + ping responder
|
|
42
|
+
if (HEAVY) {
|
|
43
|
+
yield* Effect.gen(function* () {
|
|
44
|
+
let n = 0;
|
|
45
|
+
while (true) {
|
|
46
|
+
yield* a.send(Heavy.make({ n, payload }));
|
|
47
|
+
n += 1;
|
|
48
|
+
if (HEAVY_FREQ_MS > 0)
|
|
49
|
+
yield* Effect.sleep(Duration.millis(HEAVY_FREQ_MS));
|
|
50
|
+
}
|
|
51
|
+
}).pipe(Effect.forkScoped);
|
|
52
|
+
}
|
|
53
|
+
yield* a.listen
|
|
54
|
+
.pipe(Stream.flatten(), Stream.filter(Schema.is(Ping)), Stream.tap((m) => a.send(Pong.make({ id: m.id, t0: m.t0 }))), Stream.runDrain, Effect.forkScoped);
|
|
55
|
+
// B: receiver blocks on Heavy; also pings A periodically
|
|
56
|
+
yield* b.listen
|
|
57
|
+
.pipe(Stream.flatten(), Stream.tap((m) => Effect.sync(() => {
|
|
58
|
+
if (Schema.is(Heavy)(m) && BLOCK_MS > 0) {
|
|
59
|
+
const until = Date.now() + BLOCK_MS;
|
|
60
|
+
while (Date.now() < until) { }
|
|
61
|
+
}
|
|
62
|
+
})), Stream.runDrain, Effect.forkScoped);
|
|
63
|
+
let ok = 0;
|
|
64
|
+
let timeouts = 0;
|
|
65
|
+
const deadline = Date.now() + DURATION_MS;
|
|
66
|
+
while (Date.now() < deadline) {
|
|
67
|
+
const id = Math.random().toString(36).slice(2);
|
|
68
|
+
const t0 = Date.now();
|
|
69
|
+
const waitForPong = b.listen
|
|
70
|
+
.pipe(Stream.flatten(), Stream.filter(Schema.is(Pong)), Stream.filter((m) => m.id === id), Stream.runHead)
|
|
71
|
+
.pipe(Effect.asVoid);
|
|
72
|
+
yield* b.send(Ping.make({ id, t0 }));
|
|
73
|
+
console.log('[single-port] ping sent', id);
|
|
74
|
+
const res = yield* waitForPong
|
|
75
|
+
.pipe(Effect.timeout(Duration.millis(TIMEOUT_MS)), Effect.as('ok'), Effect.catchTag('TimeoutException', () => Effect.succeed('timeout')));
|
|
76
|
+
if (res === 'ok') {
|
|
77
|
+
ok += 1;
|
|
78
|
+
console.log('[single-port] ping ok', Date.now() - t0);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
timeouts += 1;
|
|
82
|
+
console.log('[single-port] ping timeout');
|
|
83
|
+
}
|
|
84
|
+
yield* Effect.sleep(Duration.millis(PING_INTERVAL_MS));
|
|
85
|
+
}
|
|
86
|
+
console.log('[single-port] summary', JSON.stringify({ ok, timeouts }));
|
|
87
|
+
});
|
|
88
|
+
PlatformNode.NodeRuntime.runMain(main.pipe(Effect.scoped));
|
|
89
|
+
//# sourceMappingURL=starvation-repro-single-port.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"starvation-repro-single-port.js","sourceRoot":"","sources":["../../src/examples/starvation-repro-single-port.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"starvation-repro.d.ts","sourceRoot":"","sources":["../../src/examples/starvation-repro.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { MessageChannel } from 'node:worker_threads';
|
|
2
|
+
import { Effect, Schema, Stream, Duration } from '@livestore/utils/effect';
|
|
3
|
+
import { WebChannel } from '@livestore/utils/effect';
|
|
4
|
+
import * as Webmesh from '@livestore/webmesh';
|
|
5
|
+
import { PlatformNode } from '@livestore/utils/node';
|
|
6
|
+
// Minimal, self-contained WebMesh repro for single-port starvation under heavy traffic.
|
|
7
|
+
//
|
|
8
|
+
// Scenario:
|
|
9
|
+
// - Two MeshNodes (A and B) connected by one MessagePort edge.
|
|
10
|
+
// - Two logical channels multiplexed over that edge:
|
|
11
|
+
// - "leader" (ping/pong) to simulate control traffic.
|
|
12
|
+
// - "heavy" to simulate frequent, large messages that occupy the receiver's main thread.
|
|
13
|
+
// - Under heavy load (and/or simulated blocking), ping timeouts occur although the transport is healthy.
|
|
14
|
+
//
|
|
15
|
+
// Run (fish):
|
|
16
|
+
// bun submodules/livestore/packages/@livestore/webmesh/src/examples/starvation-repro.ts
|
|
17
|
+
//
|
|
18
|
+
// Tunables via env:
|
|
19
|
+
// HEAVY=1|0 enable heavy traffic (default 1)
|
|
20
|
+
// HEAVY_SIZE=500000 payload size in bytes per message (default 200000)
|
|
21
|
+
// HEAVY_FREQ_MS=10 interval between heavy messages (default 10)
|
|
22
|
+
// BLOCK_MS=0 busy-loop on B after each heavy message (default 0)
|
|
23
|
+
// PING_INTERVAL_MS=500 ping interval (default 500)
|
|
24
|
+
// TIMEOUT_MS=1500 ping timeout (default 1500)
|
|
25
|
+
// DURATION_MS=30000 run duration (default 30000)
|
|
26
|
+
//
|
|
27
|
+
// Optional: WITH_ACK=1 to use messagePortChannelWithAck for the edge (diagnostic only)
|
|
28
|
+
const env = (k, d) => (process.env[k] ?? d).trim();
|
|
29
|
+
const HEAVY = env('HEAVY', '1') === '1';
|
|
30
|
+
const HEAVY_SIZE = Number(env('HEAVY_SIZE', '200000'));
|
|
31
|
+
const HEAVY_FREQ_MS = Number(env('HEAVY_FREQ_MS', '10'));
|
|
32
|
+
const BLOCK_MS = Number(env('BLOCK_MS', '0'));
|
|
33
|
+
const PING_INTERVAL_MS = Number(env('PING_INTERVAL_MS', '500'));
|
|
34
|
+
const TIMEOUT_MS = Number(env('TIMEOUT_MS', '1500'));
|
|
35
|
+
const DURATION_MS = Number(env('DURATION_MS', '30000'));
|
|
36
|
+
const WITH_ACK = env('WITH_ACK', '0') === '1';
|
|
37
|
+
const LeaderPing = Schema.TaggedStruct('LeaderPing', {
|
|
38
|
+
id: Schema.String,
|
|
39
|
+
t0: Schema.Number,
|
|
40
|
+
});
|
|
41
|
+
const LeaderPong = Schema.TaggedStruct('LeaderPong', {
|
|
42
|
+
id: Schema.String,
|
|
43
|
+
t0: Schema.Number,
|
|
44
|
+
});
|
|
45
|
+
const LeaderMsg = Schema.Union(LeaderPing, LeaderPong);
|
|
46
|
+
const HeavyMsg = Schema.Struct({
|
|
47
|
+
n: Schema.Number, // sequence number
|
|
48
|
+
payload: Schema.String, // large string payload
|
|
49
|
+
});
|
|
50
|
+
const pad = (n, ch = 'x') => (n <= 0 ? '' : ch.repeat(n));
|
|
51
|
+
const main = Effect.gen(function* () {
|
|
52
|
+
const payload = pad(HEAVY_SIZE);
|
|
53
|
+
console.log('[repro] config', {
|
|
54
|
+
HEAVY,
|
|
55
|
+
HEAVY_SIZE: payload.length,
|
|
56
|
+
HEAVY_FREQ_MS,
|
|
57
|
+
BLOCK_MS,
|
|
58
|
+
PING_INTERVAL_MS,
|
|
59
|
+
TIMEOUT_MS,
|
|
60
|
+
DURATION_MS,
|
|
61
|
+
WITH_ACK,
|
|
62
|
+
});
|
|
63
|
+
// Create two MeshNodes and connect them via a single MessagePort edge
|
|
64
|
+
const nodeA = yield* Webmesh.makeMeshNode('A');
|
|
65
|
+
const nodeB = yield* Webmesh.makeMeshNode('B');
|
|
66
|
+
const mc = new MessageChannel();
|
|
67
|
+
const edgeA = WITH_ACK
|
|
68
|
+
? yield* WebChannel.messagePortChannelWithAck({ port: mc.port1, schema: Webmesh.WebmeshSchema.Packet })
|
|
69
|
+
: yield* WebChannel.messagePortChannel({ port: mc.port1, schema: Webmesh.WebmeshSchema.Packet });
|
|
70
|
+
const edgeB = WITH_ACK
|
|
71
|
+
? yield* WebChannel.messagePortChannelWithAck({ port: mc.port2, schema: Webmesh.WebmeshSchema.Packet })
|
|
72
|
+
: yield* WebChannel.messagePortChannel({ port: mc.port2, schema: Webmesh.WebmeshSchema.Packet });
|
|
73
|
+
yield* nodeA.addEdge({ target: 'B', edgeChannel: edgeA, replaceIfExists: true });
|
|
74
|
+
yield* nodeB.addEdge({ target: 'A', edgeChannel: edgeB, replaceIfExists: true });
|
|
75
|
+
console.log('[repro] edges ready');
|
|
76
|
+
// Application channels multiplexed over the same edge
|
|
77
|
+
const [leaderA, leaderB] = yield* Effect.all([
|
|
78
|
+
nodeA.makeChannel({
|
|
79
|
+
target: 'B',
|
|
80
|
+
channelName: 'leader',
|
|
81
|
+
schema: { listen: LeaderMsg, send: LeaderMsg },
|
|
82
|
+
mode: 'direct',
|
|
83
|
+
closeExisting: true,
|
|
84
|
+
}),
|
|
85
|
+
nodeB.makeChannel({
|
|
86
|
+
target: 'A',
|
|
87
|
+
channelName: 'leader',
|
|
88
|
+
schema: { listen: LeaderMsg, send: LeaderMsg },
|
|
89
|
+
mode: 'direct',
|
|
90
|
+
closeExisting: true,
|
|
91
|
+
}),
|
|
92
|
+
], { concurrency: 'unbounded' });
|
|
93
|
+
const [heavyA, heavyB] = yield* Effect.all([
|
|
94
|
+
nodeA.makeChannel({
|
|
95
|
+
target: 'B',
|
|
96
|
+
channelName: 'heavy',
|
|
97
|
+
schema: HeavyMsg,
|
|
98
|
+
mode: 'direct',
|
|
99
|
+
closeExisting: true,
|
|
100
|
+
}),
|
|
101
|
+
nodeB.makeChannel({
|
|
102
|
+
target: 'A',
|
|
103
|
+
channelName: 'heavy',
|
|
104
|
+
schema: HeavyMsg,
|
|
105
|
+
mode: 'direct',
|
|
106
|
+
closeExisting: true,
|
|
107
|
+
}),
|
|
108
|
+
], { concurrency: 'unbounded' });
|
|
109
|
+
console.log('[repro] channels ready');
|
|
110
|
+
// A: answer pings
|
|
111
|
+
yield* leaderA.listen
|
|
112
|
+
.pipe(Stream.flatten(), Stream.filter(Schema.is(LeaderPing)), Stream.tap((msg) => leaderA.send(LeaderPong.make({ id: msg.id, t0: msg.t0 }))), Stream.runDrain, Effect.forkScoped);
|
|
113
|
+
// B: issue periodic pings, track timeouts
|
|
114
|
+
let ok = 0;
|
|
115
|
+
let timeouts = 0;
|
|
116
|
+
// B: process heavy messages (simulate UI main-thread work)
|
|
117
|
+
if (HEAVY) {
|
|
118
|
+
yield* heavyB.listen
|
|
119
|
+
.pipe(Stream.flatten(), Stream.tap(() => Effect.sync(() => {
|
|
120
|
+
if (BLOCK_MS > 0) {
|
|
121
|
+
const until = Date.now() + BLOCK_MS;
|
|
122
|
+
while (Date.now() < until) {
|
|
123
|
+
// busy wait
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
})), Stream.runDrain, Effect.forkScoped);
|
|
127
|
+
}
|
|
128
|
+
// A: produce heavy messages
|
|
129
|
+
if (HEAVY) {
|
|
130
|
+
yield* Effect.gen(function* () {
|
|
131
|
+
let n = 0;
|
|
132
|
+
while (true) {
|
|
133
|
+
yield* heavyA.send({ n, payload });
|
|
134
|
+
n += 1;
|
|
135
|
+
yield* Effect.sleep(Duration.millis(HEAVY_FREQ_MS));
|
|
136
|
+
}
|
|
137
|
+
}).pipe(Effect.forkScoped);
|
|
138
|
+
}
|
|
139
|
+
// Run ping loop for a fixed duration
|
|
140
|
+
const deadline = Date.now() + DURATION_MS;
|
|
141
|
+
while (Date.now() < deadline) {
|
|
142
|
+
const id = Math.random().toString(36).slice(2);
|
|
143
|
+
const t0 = Date.now();
|
|
144
|
+
const waitForPong = leaderB.listen
|
|
145
|
+
.pipe(Stream.flatten(), Stream.filter(Schema.is(LeaderPong)), Stream.filter((m) => m.id === id), Stream.runHead)
|
|
146
|
+
.pipe(Effect.asVoid);
|
|
147
|
+
yield* leaderB.send(LeaderPing.make({ id, t0 }));
|
|
148
|
+
console.log('[ping] sent', id);
|
|
149
|
+
const res = yield* waitForPong
|
|
150
|
+
.pipe(Effect.timeout(Duration.millis(TIMEOUT_MS)), Effect.as('ok'), Effect.catchTag('TimeoutException', () => Effect.succeed('timeout')));
|
|
151
|
+
if (res === 'ok') {
|
|
152
|
+
ok += 1;
|
|
153
|
+
console.log('[ping] ok', Date.now() - t0);
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
timeouts += 1;
|
|
157
|
+
console.log('[ping] timeout');
|
|
158
|
+
}
|
|
159
|
+
yield* Effect.sleep(Duration.millis(PING_INTERVAL_MS));
|
|
160
|
+
}
|
|
161
|
+
console.log('[summary]', JSON.stringify({ ok, timeouts }));
|
|
162
|
+
if (timeouts > 0) {
|
|
163
|
+
console.log('[summary] ping timeouts observed under heavy traffic');
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
console.log('[summary] no timeouts observed');
|
|
167
|
+
}
|
|
168
|
+
// Done
|
|
169
|
+
});
|
|
170
|
+
PlatformNode.NodeRuntime.runMain(main.pipe(Effect.scoped));
|
|
171
|
+
//# sourceMappingURL=starvation-repro.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"starvation-repro.js","sourceRoot":"","sources":["../../src/examples/starvation-repro.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reload-handover.mesh.test.d.ts","sourceRoot":"","sources":["../src/reload-handover.mesh.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { MessageChannel } from 'node:worker_threads';
|
|
2
|
+
import { Deferred, Duration, Effect, Fiber, Option, Schema, Stream, WebChannel } from '@livestore/utils/effect';
|
|
3
|
+
import { Vitest } from '@livestore/utils-dev/node-vitest';
|
|
4
|
+
import { expect } from 'vitest';
|
|
5
|
+
import { makeMeshNode } from "./node.js";
|
|
6
|
+
import { Packet } from "./mesh-schema.js";
|
|
7
|
+
// This test models the observed DevTools behavior during reload: a single leader ping
|
|
8
|
+
// times out at handover while clientSession ping remains ok shortly after.
|
|
9
|
+
// We use actual Mesh + WebChannel implementations (MessagePort edge, direct channel).
|
|
10
|
+
// We assert the desired correct behavior (leader should not timeout across handover),
|
|
11
|
+
// which currently FAILS and documents the problem with a minimal repro.
|
|
12
|
+
const Ping = Schema.TaggedStruct('Ping', { who: Schema.String, id: Schema.String, t0: Schema.Number });
|
|
13
|
+
const Pong = Schema.TaggedStruct('Pong', { who: Schema.String, id: Schema.String, t0: Schema.Number });
|
|
14
|
+
const Msg = Schema.Union(Ping, Pong);
|
|
15
|
+
const connectViaMessagePort = (a, b) => Effect.gen(function* () {
|
|
16
|
+
const mc = new MessageChannel();
|
|
17
|
+
const ab = yield* WebChannel.messagePortChannel({ port: mc.port1, schema: Packet });
|
|
18
|
+
const ba = yield* WebChannel.messagePortChannel({ port: mc.port2, schema: Packet });
|
|
19
|
+
yield* a.addEdge({ target: b.nodeName, edgeChannel: ab, replaceIfExists: true });
|
|
20
|
+
yield* b.addEdge({ target: a.nodeName, edgeChannel: ba, replaceIfExists: true });
|
|
21
|
+
});
|
|
22
|
+
const sendPing = (channel, who, timeoutMs, sendAckTimeoutMs) => Effect.gen(function* () {
|
|
23
|
+
const id = Math.random().toString(36).slice(2);
|
|
24
|
+
const wait = channel.listen
|
|
25
|
+
.pipe(Stream.flatten(), Stream.filter(Schema.is(Pong)), Stream.filter((m) => m.id === id), Stream.runHead);
|
|
26
|
+
yield* channel
|
|
27
|
+
.send(Ping.make({ who, id, t0: Date.now() }))
|
|
28
|
+
.pipe(
|
|
29
|
+
// Prevent tests from stalling on ack when the receiver is about to be shutdown
|
|
30
|
+
Effect.timeout(Duration.millis(sendAckTimeoutMs ?? timeoutMs)), Effect.catchTag('TimeoutException', () => Effect.void));
|
|
31
|
+
const res = yield* wait
|
|
32
|
+
.pipe(Effect.map((opt) => (Option.isSome(opt) ? 'ok' : 'timeout')), Effect.timeout(Duration.millis(timeoutMs)), Effect.catchTag('TimeoutException', () => Effect.succeed('timeout')));
|
|
33
|
+
return res;
|
|
34
|
+
});
|
|
35
|
+
Vitest.scopedLive('leader ping should not timeout across reload handover (currently fails)', (test) => Effect.gen(function* () {
|
|
36
|
+
const LEADER_TIMEOUT_MS = 80;
|
|
37
|
+
const CLIENT_TIMEOUT_MS = 400;
|
|
38
|
+
// Nodes
|
|
39
|
+
const worker = yield* makeMeshNode('shared-worker');
|
|
40
|
+
const devtools = yield* makeMeshNode('devtools-instance');
|
|
41
|
+
yield* connectViaMessagePort(worker, devtools);
|
|
42
|
+
// Channels: leader
|
|
43
|
+
const workerLeader = yield* worker.makeChannel({
|
|
44
|
+
target: devtools.nodeName,
|
|
45
|
+
channelName: 'leader',
|
|
46
|
+
schema: Msg,
|
|
47
|
+
mode: 'direct',
|
|
48
|
+
closeExisting: true,
|
|
49
|
+
});
|
|
50
|
+
let devtoolsLeader = yield* devtools.makeChannel({
|
|
51
|
+
target: worker.nodeName,
|
|
52
|
+
channelName: 'leader',
|
|
53
|
+
schema: Msg,
|
|
54
|
+
mode: 'direct',
|
|
55
|
+
closeExisting: true,
|
|
56
|
+
});
|
|
57
|
+
// Channels: clientSession
|
|
58
|
+
const workerClient = yield* worker.makeChannel({
|
|
59
|
+
target: devtools.nodeName,
|
|
60
|
+
channelName: 'client',
|
|
61
|
+
schema: Msg,
|
|
62
|
+
mode: 'direct',
|
|
63
|
+
closeExisting: true,
|
|
64
|
+
});
|
|
65
|
+
const devtoolsClient = yield* devtools.makeChannel({
|
|
66
|
+
target: worker.nodeName,
|
|
67
|
+
channelName: 'client',
|
|
68
|
+
schema: Msg,
|
|
69
|
+
mode: 'direct',
|
|
70
|
+
closeExisting: true,
|
|
71
|
+
});
|
|
72
|
+
// Gates to coordinate ordering precisely: ensure Ping acked before shutdown, Pong after
|
|
73
|
+
const allowLeaderPong = yield* Deferred.make();
|
|
74
|
+
// Responder behavior on worker: on Ping, mark received, then wait for gate to send Pong
|
|
75
|
+
const workerLeaderListenFiber = yield* workerLeader.listen
|
|
76
|
+
.pipe(Stream.flatten(), Stream.take(1), Stream.tap((m) => Schema.is(Ping)(m)
|
|
77
|
+
? Effect.gen(function* () {
|
|
78
|
+
// Hold back Pong until the test signals (post-shutdown), then drop it
|
|
79
|
+
// to simulate a lost Pong to a closed pre-reload listener. Avoids ack
|
|
80
|
+
// backpressure that could stall scope shutdown.
|
|
81
|
+
yield* Deferred.await(allowLeaderPong);
|
|
82
|
+
yield* Effect.void;
|
|
83
|
+
})
|
|
84
|
+
: Effect.void), Stream.runDrain, Effect.fork);
|
|
85
|
+
const workerClientListenFiber = yield* workerClient.listen
|
|
86
|
+
.pipe(Stream.flatten(), Stream.take(1), Stream.tap((m) => (Schema.is(Ping)(m) ? workerClient.send(Pong.make({ who: m.who, id: m.id, t0: m.t0 })) : Effect.void)), Stream.runDrain, Effect.fork);
|
|
87
|
+
// 1) Start leader ping on current (pre-reload) channel and let it run concurrently
|
|
88
|
+
// This ensures the ping is in-flight before we shut down the old listener.
|
|
89
|
+
console.log('[test] forking leader ping (old channel)');
|
|
90
|
+
const leaderFiber = yield* sendPing(devtoolsLeader, 'leader', LEADER_TIMEOUT_MS, 20).pipe(Effect.fork);
|
|
91
|
+
// Yield and sleep briefly to allow the forked fiber to start and send
|
|
92
|
+
yield* Effect.yieldNow();
|
|
93
|
+
yield* Effect.sleep(Duration.millis(10));
|
|
94
|
+
console.log('[test] shutting down old leader channel');
|
|
95
|
+
// 2) Simulate reload handover: shutdown old leader channel listener; open new leader channel
|
|
96
|
+
yield* devtoolsLeader.shutdown;
|
|
97
|
+
console.log('[test] opening new leader channel');
|
|
98
|
+
devtoolsLeader = yield* devtools.makeChannel({
|
|
99
|
+
target: worker.nodeName,
|
|
100
|
+
channelName: 'leader',
|
|
101
|
+
schema: Msg,
|
|
102
|
+
mode: 'direct',
|
|
103
|
+
closeExisting: true,
|
|
104
|
+
});
|
|
105
|
+
// 3) Now allow the worker to send the Pong (which targets the old listener and should be lost)
|
|
106
|
+
console.log('[test] allowing leader pong on worker');
|
|
107
|
+
yield* Deferred.succeed(allowLeaderPong, void 0);
|
|
108
|
+
// Client ping on fresh channel should still succeed
|
|
109
|
+
console.log('[test] sending clientSession ping');
|
|
110
|
+
const clientRes = yield* sendPing(devtoolsClient, 'clientSession', CLIENT_TIMEOUT_MS);
|
|
111
|
+
console.log('[test] clientSession ping done', clientRes);
|
|
112
|
+
// 4) Now join the leader result — currently this times out because
|
|
113
|
+
// the Pong was delivered to the old, already-shutdown listener. The correct
|
|
114
|
+
// behavior would be that leader does not timeout across reload.
|
|
115
|
+
console.log('[test] joining leader ping fiber');
|
|
116
|
+
const leaderRes = yield* Fiber.join(leaderFiber)
|
|
117
|
+
.pipe(
|
|
118
|
+
// Guard against join hanging; if it does, fail by assertion rather than test-level timeout
|
|
119
|
+
Effect.timeout(Duration.millis(500)), Effect.catchTag('TimeoutException', () => Effect.succeed('HARD_TIMEOUT')));
|
|
120
|
+
console.log('[test] leader ping joined', leaderRes);
|
|
121
|
+
// Cleanup listening fibers
|
|
122
|
+
yield* Fiber.interrupt(workerLeaderListenFiber);
|
|
123
|
+
yield* Fiber.interrupt(workerClientListenFiber);
|
|
124
|
+
// Expected (correct): no timeout
|
|
125
|
+
// Actual (today): timeout → this assertion FAILS and documents the problem.
|
|
126
|
+
expect(leaderRes).not.toBe('HARD_TIMEOUT');
|
|
127
|
+
expect(leaderRes).toBe('ok');
|
|
128
|
+
expect(clientRes).toBe('ok');
|
|
129
|
+
}).pipe(Vitest.withTestCtx(test)), 10_000);
|
|
130
|
+
//# sourceMappingURL=reload-handover.mesh.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reload-handover.mesh.test.js","sourceRoot":"","sources":["../src/reload-handover.mesh.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reload-reconnect.test.d.ts","sourceRoot":"","sources":["../src/reload-reconnect.test.ts"],"names":[],"mappings":""}
|