@peerbit/pubsub 5.3.12 → 5.4.1

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.
Files changed (50) hide show
  1. package/dist/src/index.d.ts +31 -13
  2. package/dist/src/index.d.ts.map +1 -1
  3. package/dist/src/index.js +624 -147
  4. package/dist/src/index.js.map +1 -1
  5. package/package.json +8 -7
  6. package/src/index.ts +924 -218
  7. package/dist/benchmark/fanout-tree-parent-upgrade-default-ready.d.ts +0 -3
  8. package/dist/benchmark/fanout-tree-parent-upgrade-default-ready.d.ts.map +0 -1
  9. package/dist/benchmark/fanout-tree-parent-upgrade-default-ready.js +0 -90
  10. package/dist/benchmark/fanout-tree-parent-upgrade-default-ready.js.map +0 -1
  11. package/dist/benchmark/fanout-tree-parent-upgrade-eval.d.ts +0 -2
  12. package/dist/benchmark/fanout-tree-parent-upgrade-eval.d.ts.map +0 -1
  13. package/dist/benchmark/fanout-tree-parent-upgrade-eval.js +0 -712
  14. package/dist/benchmark/fanout-tree-parent-upgrade-eval.js.map +0 -1
  15. package/dist/benchmark/fanout-tree-parent-upgrade-multi-eval.d.ts +0 -2
  16. package/dist/benchmark/fanout-tree-parent-upgrade-multi-eval.d.ts.map +0 -1
  17. package/dist/benchmark/fanout-tree-parent-upgrade-multi-eval.js +0 -1591
  18. package/dist/benchmark/fanout-tree-parent-upgrade-multi-eval.js.map +0 -1
  19. package/dist/benchmark/fanout-tree-parent-upgrade-prepush.d.ts +0 -2
  20. package/dist/benchmark/fanout-tree-parent-upgrade-prepush.d.ts.map +0 -1
  21. package/dist/benchmark/fanout-tree-parent-upgrade-prepush.js +0 -297
  22. package/dist/benchmark/fanout-tree-parent-upgrade-prepush.js.map +0 -1
  23. package/dist/benchmark/fanout-tree-parent-upgrade-preset.d.ts +0 -73
  24. package/dist/benchmark/fanout-tree-parent-upgrade-preset.d.ts.map +0 -1
  25. package/dist/benchmark/fanout-tree-parent-upgrade-preset.js +0 -183
  26. package/dist/benchmark/fanout-tree-parent-upgrade-preset.js.map +0 -1
  27. package/dist/benchmark/fanout-tree-sim-lib.d.ts +0 -303
  28. package/dist/benchmark/fanout-tree-sim-lib.d.ts.map +0 -1
  29. package/dist/benchmark/fanout-tree-sim-lib.js +0 -1752
  30. package/dist/benchmark/fanout-tree-sim-lib.js.map +0 -1
  31. package/dist/benchmark/fanout-tree-sim.d.ts +0 -2
  32. package/dist/benchmark/fanout-tree-sim.d.ts.map +0 -1
  33. package/dist/benchmark/fanout-tree-sim.js +0 -870
  34. package/dist/benchmark/fanout-tree-sim.js.map +0 -1
  35. package/dist/benchmark/index.d.ts +0 -8
  36. package/dist/benchmark/index.d.ts.map +0 -1
  37. package/dist/benchmark/index.js +0 -62
  38. package/dist/benchmark/index.js.map +0 -1
  39. package/dist/benchmark/pubsub-topic-sim-lib.d.ts +0 -86
  40. package/dist/benchmark/pubsub-topic-sim-lib.d.ts.map +0 -1
  41. package/dist/benchmark/pubsub-topic-sim-lib.js +0 -680
  42. package/dist/benchmark/pubsub-topic-sim-lib.js.map +0 -1
  43. package/dist/benchmark/pubsub-topic-sim.d.ts +0 -9
  44. package/dist/benchmark/pubsub-topic-sim.d.ts.map +0 -1
  45. package/dist/benchmark/pubsub-topic-sim.js +0 -134
  46. package/dist/benchmark/pubsub-topic-sim.js.map +0 -1
  47. package/dist/benchmark/sim/bench-utils.d.ts +0 -25
  48. package/dist/benchmark/sim/bench-utils.d.ts.map +0 -1
  49. package/dist/benchmark/sim/bench-utils.js +0 -141
  50. package/dist/benchmark/sim/bench-utils.js.map +0 -1
@@ -1,870 +0,0 @@
1
- /**
2
- * End-to-end FanoutTree simulator (real protocol + real @peerbit/stream).
3
- *
4
- * This is meant to validate that:
5
- * - join works via bootstrap trackers (announce/query + dial + join),
6
- * - the data plane scales with bounded per-node fanout (tree),
7
- * - pull repair does not explode (when enabled),
8
- * without using real sockets/crypto.
9
- */
10
- import { formatFanoutTreeSimResult, resolveFanoutTreeSimParams, runFanoutTreeSim, } from "./fanout-tree-sim-lib.js";
11
- const HELP_TEXT = [
12
- "fanout-tree-sim.ts",
13
- "",
14
- "Args:",
15
- " --preset NAME preset workload (live|reliable|scale-5k|scale-10k|ci-small|ci-loss)",
16
- " --nodes N total nodes (default: 2000)",
17
- " --rootIndex I root/publisher node index (default: 0)",
18
- " --bootstraps N bootstrap tracker nodes (default: 1)",
19
- " --bootstrapMaxPeers N max bootstraps to dial/query per node (default: 0, 0=all)",
20
- " --subscribers N total subscribers (default: nodes-1-bootstraps)",
21
- " --relayFraction F fraction of subscribers acting as relays (default: 0.25)",
22
- " --messages M messages to publish (default: 200)",
23
- " --secondBatchMessages M extra messages after settle/late topology (default: 0)",
24
- " --secondBatchSettleMs MS wait after second batch before measuring (default: 0)",
25
- " --msgRate R publish rate (msg/s, default: 30)",
26
- " --msgSize BYTES payload bytes (default: 1024)",
27
- " --intervalMs MS override publish interval (default: 0 => derived from msgRate)",
28
- " --settleMs MS wait after publish (default: 2000)",
29
- " --deadlineMs MS count delivery-within-deadline (default: 0, off)",
30
- " --maxDataAgeMs MS drop forwarding of stale data (default: 0, off)",
31
- " --timeoutMs MS global timeout (default: 300000)",
32
- " --seed S RNG seed (default: 1)",
33
- " --topic NAME topic name (default: concert)",
34
- " --rootUploadLimitBps BPS (default: 20000000)",
35
- " --rootMaxChildren N (default: 64)",
36
- " --relayUploadLimitBps BPS (default: 10000000)",
37
- " --relayMaxChildren N (default: 32)",
38
- " --allowKick 0|1 allow bid-based kicking when full (default: 0)",
39
- " --bidPerByte N bid offered by joiners (default: 0)",
40
- " --bidPerByteRelay N bid for relay joiners (default: bidPerByte)",
41
- " --bidPerByteLeaf N bid for leaf joiners (default: bidPerByte)",
42
- " --repair 0|1 enable pull repair (default: 1)",
43
- " --repairWindowMessages N (default: 1024)",
44
- " --repairMaxBackfillMessages N max missing lag to repair (-1 = same as window)",
45
- " --repairIntervalMs MS (default: 200)",
46
- " --repairMaxPerReq N (default: 64)",
47
- " --neighborRepair 0|1 enable neighbor-assisted repair (default: 0)",
48
- " --neighborRepairPeers N extra peers to query per repair tick (default: 2)",
49
- " --neighborMeshPeers N lazy repair mesh peers (-1 = FanoutTree default)",
50
- " --neighborAnnounceIntervalMs MS IHAVE announce interval (-1 = FanoutTree default)",
51
- " --neighborMeshRefreshIntervalMs MS mesh refresh interval (-1 = FanoutTree default)",
52
- " --neighborHaveTtlMs MS IHAVE TTL when selecting fetch targets (-1 = FanoutTree default)",
53
- " --neighborRepairBudgetBps BPS budget for neighbor FETCH_REQ control traffic (-1 = FanoutTree default)",
54
- " --neighborRepairBurstMs MS burst window for neighbor FETCH_REQ budget (-1 = FanoutTree default)",
55
- " --dialDelayMs MS artificial dial delay (default: 0)",
56
- " --streamRxDelayMs MS per-chunk inbound delay in shim (default: 0)",
57
- " --streamHighWaterMarkBytes B backpressure threshold (default: 262144)",
58
- " --joinConcurrency N parallel join tasks (default: 256)",
59
- " --joinPhases 0|1 join relays first, then leaves (default: 0)",
60
- " --joinPhaseSettleMs MS wait between join phases (default: 2000)",
61
- " --lateRootConnectAfterMs MS after publish, connect subscribers directly to root (-1 = off)",
62
- " --lateRootDuringPublish 0|1 apply late root topology during first publish batch (default: 0)",
63
- " --lateRootMaxChildren N after publish, raise root child capacity (0 = unchanged)",
64
- " --lateRootConnectFraction F fraction of joined subscribers to connect to root (default: 1)",
65
- " --joinReqTimeoutMs MS join request timeout per candidate (default: 2000)",
66
- " --candidateShuffleTopK N shuffle only within top K candidates (default: 8)",
67
- " --candidateScoringMode MODE join scoring (ranked-shuffle|ranked-strict|weighted, default: ranked-shuffle)",
68
- " --parentUpgradeIntervalMs MS min interval between proactive parent-upgrade checks (default: 0, off)",
69
- " --parentUpgradeLeafOnly 0|1 restrict proactive parent upgrades to leaves (default: 1)",
70
- " --parentUpgradeMinLevelGain N min tree-level gain for proactive upgrades (default: 1)",
71
- " --parentUpgradeRootMinLevelGain N min tree-level gain for root upgrade targets (default: 3)",
72
- " --parentUpgradeRootMinSubtreeGain N min level-gain times local branch size for root upgrade targets (default: parentUpgradeRootMinLevelGain)",
73
- " --parentUpgradeNonRootMinLevelGain N min tree-level gain for non-root upgrade targets (default: 2)",
74
- " --parentUpgradeMinFreeSlots N min advertised free slots for upgrade targets (default: 8)",
75
- " --parentUpgradeRootMinFreeSlots N min advertised free slots for root upgrade targets (default: parentUpgradeMinFreeSlots)",
76
- " --parentUpgradeMaxChildLoadRatio R max child load ratio after accepting upgrade child (default: 0.5)",
77
- " --parentUpgradeRootMaxChildLoadRatio R max root child load ratio after accepting upgrade child (default: min(parentUpgradeMaxChildLoadRatio, 0.4))",
78
- " --parentUpgradeCooldownMs MS cooldown after a successful proactive upgrade (default: 5000)",
79
- " --parentUpgradeFailedBackoffMinMs MS initial backoff after failed probe/shadow rounds (default: 5000)",
80
- " --parentUpgradeFailedBackoffMaxMs MS max backoff after failed probe/shadow rounds (default: 60000)",
81
- " --parentUpgradeQuietMs MS min quiet time since parent data before upgrades (default: 5000)",
82
- " --parentUpgradeRepairQuietMs MS min quiet time since repair before upgrades (default: parentUpgradeQuietMs)",
83
- " --parentUpgradeMaxPerPeer N max successful proactive upgrades per peer, 0 = unlimited (default: 2)",
84
- " --parentUpgradeRepairGuard 0|1 skip proactive upgrades while missing data (default: 1)",
85
- " --parentUpgradeDataGuard 0|1 wait for finite channel completion before upgrades (default: 1)",
86
- " --parentUpgradeMode MODE proactive upgrade mode (direct|probe|shadow, default: shadow)",
87
- " --parentUpgradeVerifyStaleRootCapacity 0|1 allow shadow probes against tracker-full root (default: 1 in shadow mode, otherwise 0)",
88
- " --parentUpgradeStaleRootProbeProbability R base sample for tracker-full root probes per peer (default: 0.0625)",
89
- " --parentProbeTimeoutMs MS timeout for probe-mode parent checks (default: 500)",
90
- " --parentProbeMaxPerRound N max probe-mode candidates per upgrade check (default: 2)",
91
- " --parentProbeMaxLagMessages N max sequence lag for probe-mode candidates (default: 0)",
92
- " --parentProbeRejectCooldownMs MS cooldown after rejected parent probes (default: 10000)",
93
- " --parentProbeRejectCooldownMaxMs MS max adaptive cooldown after rejected parent probes (default: 60000)",
94
- " --parentShadowObserveMs MS min healthy shadow observation window before promotion (default: 2000)",
95
- " --parentShadowMinObservations N min successful shadow observations before promotion (default: 2)",
96
- " --parentShadowDualPathMs MS keep old parent during active-flow shadow cutover until candidate data is observed (default: 5000 in shadow mode, otherwise 0)",
97
- " --parentShadowDualPathMinMessages N min candidate data messages before active-flow cutover (default: 32 in shadow mode, otherwise 1)",
98
- " --bootstrapEnsureIntervalMs MS min interval between bootstrap re-dials (-1 = FanoutTree default)",
99
- " --trackerQueryIntervalMs MS min interval between tracker queries (-1 = FanoutTree default)",
100
- " --joinAttemptsPerRound N max join candidates tried per retry round (-1 = FanoutTree default)",
101
- " --candidateCooldownMs MS cooldown applied to bad join candidates (-1 = FanoutTree default)",
102
- " --maxLatencySamples N reservoir sample size (default: 1000000)",
103
- " --profile 0|1 collect CPU/mem/event-loop delay stats (default: 0)",
104
- " --progress 0|1 log join progress + memory stats (default: 0)",
105
- " --progressEveryMs MS progress log interval (default: 5000)",
106
- " --dropDataFrameRate P drop rate for stream data frames (default: 0)",
107
- " --churnEveryMs MS churn interval (default: 0, off)",
108
- " --churnDownMs MS offline duration per churn (default: 0, off)",
109
- " --churnFraction F fraction to churn per event (default: 0, off)",
110
- " --assertMinJoinedPct PCT (default: 0)",
111
- " --assertMinDeliveryPct PCT (default: 0)",
112
- " --assertMinDeadlineDeliveryPct PCT (default: 0)",
113
- " --assertMaxUploadFracPct PCT max peak upload vs cap (default: 0, off)",
114
- " --assertMaxOverheadFactor X max data overhead factor vs ideal tree (default: 0, off)",
115
- " --assertMaxControlBpp X max control bytes per payload byte delivered (default: 0, off)",
116
- " --assertMaxTrackerBpp X max tracker bytes per payload byte delivered (default: 0, off)",
117
- " --assertMaxTrackerBytes N max tracker control bytes sent (default: 0, off)",
118
- " --assertMaxRepairBpp X max repair bytes per payload byte delivered (default: 0, off)",
119
- " --assertAttachP95Ms MS max p95 time-to-attach since join start (default: 0, off)",
120
- " --assertMaxTreeLevelP95 N max p95 tree depth/level (default: 0, off)",
121
- " --assertMaxFormationScore X max formationScore (default: 0, off)",
122
- " --assertMinReparentUpgradeTotal N min proactive parent upgrades (default: 0, off)",
123
- " --assertMinActiveShadowPromoteTotal N min active shadow promotions (default: 0, off)",
124
- " --assertMaxOrphans N max online orphans during publish+settle (default: 0, off)",
125
- " --assertMaxOrphanArea S max orphan-area (orphan-seconds) during publish+settle (default: 0, off)",
126
- " --assertRecoveryP95Ms MS max p95 recovery time after churn (default: 0, off)",
127
- " --assertMaxReparentsPerMin N max reparent events per minute (default: 0, off)",
128
- "",
129
- "Example:",
130
- " pnpm -C packages/transport/pubsub run bench -- fanout-tree-sim --preset live --nodes 2000 --bootstraps 1 --seed 1",
131
- ].join("\n");
132
- const PRESET_OPTIONS = {
133
- live: {
134
- bootstraps: 3,
135
- bootstrapMaxPeers: 1,
136
- msgRate: 30,
137
- msgSize: 1024,
138
- messages: 30 * 60,
139
- settleMs: 2_000,
140
- deadlineMs: 2_000,
141
- allowKick: true,
142
- bidPerByteRelay: 1,
143
- bidPerByteLeaf: 0,
144
- joinPhases: true,
145
- joinPhaseSettleMs: 2_000,
146
- repair: true,
147
- repairMaxBackfillMessages: 60,
148
- neighborRepair: true,
149
- neighborRepairPeers: 3,
150
- dropDataFrameRate: 0.01,
151
- churnEveryMs: 2_000,
152
- churnDownMs: 1_000,
153
- churnFraction: 0.005,
154
- },
155
- reliable: {
156
- bootstraps: 3,
157
- bootstrapMaxPeers: 1,
158
- msgRate: 30,
159
- msgSize: 1024,
160
- messages: 30 * 60,
161
- settleMs: 10_000,
162
- deadlineMs: 10_000,
163
- allowKick: true,
164
- bidPerByteRelay: 1,
165
- bidPerByteLeaf: 0,
166
- joinPhases: true,
167
- joinPhaseSettleMs: 2_000,
168
- repair: true,
169
- repairMaxBackfillMessages: 1024,
170
- neighborRepair: true,
171
- neighborRepairPeers: 4,
172
- dropDataFrameRate: 0.01,
173
- churnEveryMs: 2_000,
174
- churnDownMs: 1_000,
175
- churnFraction: 0.005,
176
- },
177
- "scale-5k": {
178
- nodes: 5000,
179
- bootstraps: 3,
180
- bootstrapMaxPeers: 1,
181
- subscribers: 4800,
182
- relayFraction: 0.25,
183
- msgRate: 30,
184
- msgSize: 1024,
185
- messages: 10,
186
- settleMs: 5_000,
187
- deadlineMs: 10_000,
188
- timeoutMs: 600_000,
189
- seed: 1,
190
- allowKick: true,
191
- bidPerByteRelay: 1,
192
- bidPerByteLeaf: 0,
193
- // Avoid join storms (and excess temporary connections) in single-process sims.
194
- joinConcurrency: 128,
195
- joinPhases: true,
196
- joinPhaseSettleMs: 2_000,
197
- joinReqTimeoutMs: 1_000,
198
- joinAttemptsPerRound: 2,
199
- trackerQueryIntervalMs: 10_000,
200
- bootstrapEnsureIntervalMs: 10_000,
201
- candidateCooldownMs: 5_000,
202
- repair: true,
203
- repairMaxBackfillMessages: 60,
204
- neighborRepair: true,
205
- neighborRepairPeers: 3,
206
- dropDataFrameRate: 0.01,
207
- // Keep in-memory sims bounded (avoid buffering OOM at 5k+ nodes).
208
- streamHighWaterMarkBytes: 8 * 1024,
209
- // Bench assertions (tune as we learn; these should be achievable on a dev machine).
210
- assertMinJoinedPct: 99.5,
211
- assertMinDeliveryPct: 99.9,
212
- assertMinDeadlineDeliveryPct: 97.0,
213
- assertMaxOverheadFactor: 1.3,
214
- assertMaxUploadFracPct: 110,
215
- // Control-plane budgets (bytes per delivered payload byte). Tune as we learn.
216
- assertMaxControlBpp: 0.5,
217
- assertMaxTrackerBpp: 0.3,
218
- assertMaxRepairBpp: 0.3,
219
- },
220
- "scale-10k": {
221
- nodes: 10_000,
222
- bootstraps: 3,
223
- bootstrapMaxPeers: 1,
224
- subscribers: 9_600,
225
- relayFraction: 0.25,
226
- msgRate: 30,
227
- msgSize: 1024,
228
- messages: 10,
229
- settleMs: 5_000,
230
- deadlineMs: 10_000,
231
- timeoutMs: 1_200_000,
232
- seed: 1,
233
- allowKick: true,
234
- bidPerByteRelay: 1,
235
- bidPerByteLeaf: 0,
236
- // Avoid join storms (and excess temporary connections) in single-process sims.
237
- joinConcurrency: 192,
238
- joinPhases: true,
239
- joinPhaseSettleMs: 2_000,
240
- joinReqTimeoutMs: 1_000,
241
- joinAttemptsPerRound: 2,
242
- trackerQueryIntervalMs: 10_000,
243
- bootstrapEnsureIntervalMs: 10_000,
244
- candidateCooldownMs: 5_000,
245
- repair: true,
246
- repairMaxBackfillMessages: 60,
247
- neighborRepair: true,
248
- neighborRepairPeers: 3,
249
- dropDataFrameRate: 0.01,
250
- // Keep in-memory sims bounded (avoid buffering OOM at 10k nodes).
251
- streamHighWaterMarkBytes: 8 * 1024,
252
- },
253
- "ci-small": {
254
- nodes: 25,
255
- bootstraps: 3,
256
- bootstrapMaxPeers: 1,
257
- subscribers: 20,
258
- relayFraction: 0.3,
259
- messages: 20,
260
- msgRate: 50,
261
- msgSize: 64,
262
- settleMs: 500,
263
- timeoutMs: 20_000,
264
- seed: 1,
265
- repair: true,
266
- },
267
- "ci-loss": {
268
- nodes: 40,
269
- bootstraps: 3,
270
- bootstrapMaxPeers: 1,
271
- subscribers: 30,
272
- relayFraction: 0.35,
273
- messages: 40,
274
- msgRate: 50,
275
- msgSize: 64,
276
- settleMs: 2_500,
277
- timeoutMs: 40_000,
278
- seed: 1,
279
- repair: true,
280
- neighborRepair: true,
281
- neighborRepairPeers: 3,
282
- dropDataFrameRate: 0.1,
283
- churnEveryMs: 200,
284
- churnDownMs: 100,
285
- churnFraction: 0.05,
286
- },
287
- };
288
- const parseNumber = (value) => Number(value);
289
- const parseString = (value) => String(value);
290
- const parseBool01 = (fallback) => (value) => String(value ?? fallback) === "1";
291
- const ARG_SPECS = [
292
- { flag: "--nodes", key: "nodes", parse: parseNumber },
293
- { flag: "--rootIndex", key: "rootIndex", parse: parseNumber },
294
- { flag: "--bootstraps", key: "bootstraps", parse: parseNumber },
295
- { flag: "--bootstrapMaxPeers", key: "bootstrapMaxPeers", parse: parseNumber },
296
- { flag: "--subscribers", key: "subscribers", parse: parseNumber },
297
- { flag: "--relayFraction", key: "relayFraction", parse: parseNumber },
298
- { flag: "--messages", key: "messages", parse: parseNumber },
299
- {
300
- flag: "--secondBatchMessages",
301
- key: "secondBatchMessages",
302
- parse: parseNumber,
303
- },
304
- {
305
- flag: "--secondBatchSettleMs",
306
- key: "secondBatchSettleMs",
307
- parse: parseNumber,
308
- },
309
- { flag: "--msgRate", key: "msgRate", parse: parseNumber },
310
- { flag: "--msgSize", key: "msgSize", parse: parseNumber },
311
- { flag: "--intervalMs", key: "intervalMs", parse: parseNumber },
312
- { flag: "--settleMs", key: "settleMs", parse: parseNumber },
313
- { flag: "--deadlineMs", key: "deadlineMs", parse: parseNumber },
314
- { flag: "--maxDataAgeMs", key: "maxDataAgeMs", parse: parseNumber },
315
- { flag: "--timeoutMs", key: "timeoutMs", parse: parseNumber },
316
- { flag: "--seed", key: "seed", parse: parseNumber },
317
- { flag: "--topic", key: "topic", parse: parseString },
318
- {
319
- flag: "--rootUploadLimitBps",
320
- key: "rootUploadLimitBps",
321
- parse: parseNumber,
322
- },
323
- { flag: "--rootMaxChildren", key: "rootMaxChildren", parse: parseNumber },
324
- {
325
- flag: "--relayUploadLimitBps",
326
- key: "relayUploadLimitBps",
327
- parse: parseNumber,
328
- },
329
- { flag: "--relayMaxChildren", key: "relayMaxChildren", parse: parseNumber },
330
- { flag: "--allowKick", key: "allowKick", parse: parseBool01("0") },
331
- { flag: "--bidPerByte", key: "bidPerByte", parse: parseNumber },
332
- { flag: "--bidPerByteRelay", key: "bidPerByteRelay", parse: parseNumber },
333
- { flag: "--bidPerByteLeaf", key: "bidPerByteLeaf", parse: parseNumber },
334
- { flag: "--repair", key: "repair", parse: parseBool01("1") },
335
- {
336
- flag: "--repairWindowMessages",
337
- key: "repairWindowMessages",
338
- parse: parseNumber,
339
- },
340
- {
341
- flag: "--repairMaxBackfillMessages",
342
- key: "repairMaxBackfillMessages",
343
- parse: parseNumber,
344
- },
345
- { flag: "--repairIntervalMs", key: "repairIntervalMs", parse: parseNumber },
346
- { flag: "--repairMaxPerReq", key: "repairMaxPerReq", parse: parseNumber },
347
- { flag: "--neighborRepair", key: "neighborRepair", parse: parseBool01("0") },
348
- {
349
- flag: "--neighborRepairPeers",
350
- key: "neighborRepairPeers",
351
- parse: parseNumber,
352
- },
353
- { flag: "--neighborMeshPeers", key: "neighborMeshPeers", parse: parseNumber },
354
- {
355
- flag: "--neighborAnnounceIntervalMs",
356
- key: "neighborAnnounceIntervalMs",
357
- parse: parseNumber,
358
- },
359
- {
360
- flag: "--neighborMeshRefreshIntervalMs",
361
- key: "neighborMeshRefreshIntervalMs",
362
- parse: parseNumber,
363
- },
364
- { flag: "--neighborHaveTtlMs", key: "neighborHaveTtlMs", parse: parseNumber },
365
- {
366
- flag: "--neighborRepairBudgetBps",
367
- key: "neighborRepairBudgetBps",
368
- parse: parseNumber,
369
- },
370
- {
371
- flag: "--neighborRepairBurstMs",
372
- key: "neighborRepairBurstMs",
373
- parse: parseNumber,
374
- },
375
- { flag: "--dialDelayMs", key: "dialDelayMs", parse: parseNumber },
376
- { flag: "--streamRxDelayMs", key: "streamRxDelayMs", parse: parseNumber },
377
- {
378
- flag: "--streamHighWaterMarkBytes",
379
- key: "streamHighWaterMarkBytes",
380
- parse: parseNumber,
381
- },
382
- { flag: "--joinConcurrency", key: "joinConcurrency", parse: parseNumber },
383
- { flag: "--joinPhases", key: "joinPhases", parse: parseBool01("0") },
384
- { flag: "--joinPhaseSettleMs", key: "joinPhaseSettleMs", parse: parseNumber },
385
- {
386
- flag: "--lateRootConnectAfterMs",
387
- key: "lateRootConnectAfterMs",
388
- parse: parseNumber,
389
- },
390
- {
391
- flag: "--lateRootDuringPublish",
392
- key: "lateRootDuringPublish",
393
- parse: parseBool01("0"),
394
- },
395
- {
396
- flag: "--lateRootMaxChildren",
397
- key: "lateRootMaxChildren",
398
- parse: parseNumber,
399
- },
400
- {
401
- flag: "--lateRootConnectFraction",
402
- key: "lateRootConnectFraction",
403
- parse: parseNumber,
404
- },
405
- { flag: "--joinReqTimeoutMs", key: "joinReqTimeoutMs", parse: parseNumber },
406
- {
407
- flag: "--candidateShuffleTopK",
408
- key: "candidateShuffleTopK",
409
- parse: parseNumber,
410
- },
411
- {
412
- flag: "--candidateScoringMode",
413
- key: "candidateScoringMode",
414
- parse: parseString,
415
- },
416
- {
417
- flag: "--parentUpgradeIntervalMs",
418
- key: "parentUpgradeIntervalMs",
419
- parse: parseNumber,
420
- },
421
- {
422
- flag: "--parentUpgradeLeafOnly",
423
- key: "parentUpgradeLeafOnly",
424
- parse: parseBool01("1"),
425
- },
426
- {
427
- flag: "--parentUpgradeMinLevelGain",
428
- key: "parentUpgradeMinLevelGain",
429
- parse: parseNumber,
430
- },
431
- {
432
- flag: "--parentUpgradeRootMinLevelGain",
433
- key: "parentUpgradeRootMinLevelGain",
434
- parse: parseNumber,
435
- },
436
- {
437
- flag: "--parentUpgradeRootMinSubtreeGain",
438
- key: "parentUpgradeRootMinSubtreeGain",
439
- parse: parseNumber,
440
- },
441
- {
442
- flag: "--parentUpgradeNonRootMinLevelGain",
443
- key: "parentUpgradeNonRootMinLevelGain",
444
- parse: parseNumber,
445
- },
446
- {
447
- flag: "--parentUpgradeMinFreeSlots",
448
- key: "parentUpgradeMinFreeSlots",
449
- parse: parseNumber,
450
- },
451
- {
452
- flag: "--parentUpgradeRootMinFreeSlots",
453
- key: "parentUpgradeRootMinFreeSlots",
454
- parse: parseNumber,
455
- },
456
- {
457
- flag: "--parentUpgradeMaxChildLoadRatio",
458
- key: "parentUpgradeMaxChildLoadRatio",
459
- parse: parseNumber,
460
- },
461
- {
462
- flag: "--parentUpgradeRootMaxChildLoadRatio",
463
- key: "parentUpgradeRootMaxChildLoadRatio",
464
- parse: parseNumber,
465
- },
466
- {
467
- flag: "--parentUpgradeStaleRootProbeProbability",
468
- key: "parentUpgradeStaleRootProbeProbability",
469
- parse: parseNumber,
470
- },
471
- {
472
- flag: "--parentUpgradeCooldownMs",
473
- key: "parentUpgradeCooldownMs",
474
- parse: parseNumber,
475
- },
476
- {
477
- flag: "--parentUpgradeFailedBackoffMinMs",
478
- key: "parentUpgradeFailedBackoffMinMs",
479
- parse: parseNumber,
480
- },
481
- {
482
- flag: "--parentUpgradeFailedBackoffMaxMs",
483
- key: "parentUpgradeFailedBackoffMaxMs",
484
- parse: parseNumber,
485
- },
486
- {
487
- flag: "--parentUpgradeQuietMs",
488
- key: "parentUpgradeQuietMs",
489
- parse: parseNumber,
490
- },
491
- {
492
- flag: "--parentUpgradeRepairQuietMs",
493
- key: "parentUpgradeRepairQuietMs",
494
- parse: parseNumber,
495
- },
496
- {
497
- flag: "--parentUpgradeMaxPerPeer",
498
- key: "parentUpgradeMaxPerPeer",
499
- parse: parseNumber,
500
- },
501
- {
502
- flag: "--parentUpgradeRepairGuard",
503
- key: "parentUpgradeRepairGuard",
504
- parse: parseBool01("1"),
505
- },
506
- {
507
- flag: "--parentUpgradeDataGuard",
508
- key: "parentUpgradeDataGuard",
509
- parse: parseBool01("1"),
510
- },
511
- { flag: "--parentUpgradeMode", key: "parentUpgradeMode", parse: parseString },
512
- {
513
- flag: "--parentUpgradeVerifyStaleRootCapacity",
514
- key: "parentUpgradeVerifyStaleRootCapacity",
515
- parse: parseBool01("0"),
516
- },
517
- {
518
- flag: "--parentProbeTimeoutMs",
519
- key: "parentProbeTimeoutMs",
520
- parse: parseNumber,
521
- },
522
- {
523
- flag: "--parentProbeMaxPerRound",
524
- key: "parentProbeMaxPerRound",
525
- parse: parseNumber,
526
- },
527
- {
528
- flag: "--parentProbeMaxLagMessages",
529
- key: "parentProbeMaxLagMessages",
530
- parse: parseNumber,
531
- },
532
- {
533
- flag: "--parentProbeRejectCooldownMs",
534
- key: "parentProbeRejectCooldownMs",
535
- parse: parseNumber,
536
- },
537
- {
538
- flag: "--parentProbeRejectCooldownMaxMs",
539
- key: "parentProbeRejectCooldownMaxMs",
540
- parse: parseNumber,
541
- },
542
- {
543
- flag: "--parentShadowObserveMs",
544
- key: "parentShadowObserveMs",
545
- parse: parseNumber,
546
- },
547
- {
548
- flag: "--parentShadowMinObservations",
549
- key: "parentShadowMinObservations",
550
- parse: parseNumber,
551
- },
552
- {
553
- flag: "--parentShadowDualPathMs",
554
- key: "parentShadowDualPathMs",
555
- parse: parseNumber,
556
- },
557
- {
558
- flag: "--parentShadowDualPathMinMessages",
559
- key: "parentShadowDualPathMinMessages",
560
- parse: parseNumber,
561
- },
562
- {
563
- flag: "--bootstrapEnsureIntervalMs",
564
- key: "bootstrapEnsureIntervalMs",
565
- parse: parseNumber,
566
- },
567
- {
568
- flag: "--trackerQueryIntervalMs",
569
- key: "trackerQueryIntervalMs",
570
- parse: parseNumber,
571
- },
572
- {
573
- flag: "--joinAttemptsPerRound",
574
- key: "joinAttemptsPerRound",
575
- parse: parseNumber,
576
- },
577
- {
578
- flag: "--candidateCooldownMs",
579
- key: "candidateCooldownMs",
580
- parse: parseNumber,
581
- },
582
- { flag: "--maxLatencySamples", key: "maxLatencySamples", parse: parseNumber },
583
- { flag: "--profile", key: "profile", parse: parseBool01("0") },
584
- { flag: "--progress", key: "progress", parse: parseBool01("0") },
585
- { flag: "--progressEveryMs", key: "progressEveryMs", parse: parseNumber },
586
- { flag: "--dropDataFrameRate", key: "dropDataFrameRate", parse: parseNumber },
587
- { flag: "--churnEveryMs", key: "churnEveryMs", parse: parseNumber },
588
- { flag: "--churnDownMs", key: "churnDownMs", parse: parseNumber },
589
- { flag: "--churnFraction", key: "churnFraction", parse: parseNumber },
590
- {
591
- flag: "--assertMinJoinedPct",
592
- key: "assertMinJoinedPct",
593
- parse: parseNumber,
594
- },
595
- {
596
- flag: "--assertMinDeliveryPct",
597
- key: "assertMinDeliveryPct",
598
- parse: parseNumber,
599
- },
600
- {
601
- flag: "--assertMinDeadlineDeliveryPct",
602
- key: "assertMinDeadlineDeliveryPct",
603
- parse: parseNumber,
604
- },
605
- {
606
- flag: "--assertMaxUploadFracPct",
607
- key: "assertMaxUploadFracPct",
608
- parse: parseNumber,
609
- },
610
- {
611
- flag: "--assertMaxOverheadFactor",
612
- key: "assertMaxOverheadFactor",
613
- parse: parseNumber,
614
- },
615
- {
616
- flag: "--assertMaxControlBpp",
617
- key: "assertMaxControlBpp",
618
- parse: parseNumber,
619
- },
620
- {
621
- flag: "--assertMaxTrackerBpp",
622
- key: "assertMaxTrackerBpp",
623
- parse: parseNumber,
624
- },
625
- {
626
- flag: "--assertMaxTrackerBytes",
627
- key: "assertMaxTrackerBytes",
628
- parse: parseNumber,
629
- },
630
- {
631
- flag: "--assertMaxRepairBpp",
632
- key: "assertMaxRepairBpp",
633
- parse: parseNumber,
634
- },
635
- { flag: "--assertAttachP95Ms", key: "assertAttachP95Ms", parse: parseNumber },
636
- {
637
- flag: "--assertMaxTreeLevelP95",
638
- key: "assertMaxTreeLevelP95",
639
- parse: parseNumber,
640
- },
641
- {
642
- flag: "--assertMaxFormationScore",
643
- key: "assertMaxFormationScore",
644
- parse: parseNumber,
645
- },
646
- {
647
- flag: "--assertMinReparentUpgradeTotal",
648
- key: "assertMinReparentUpgradeTotal",
649
- parse: parseNumber,
650
- },
651
- {
652
- flag: "--assertMinActiveShadowPromoteTotal",
653
- key: "assertMinActiveShadowPromoteTotal",
654
- parse: parseNumber,
655
- },
656
- { flag: "--assertMaxOrphans", key: "assertMaxOrphans", parse: parseNumber },
657
- {
658
- flag: "--assertMaxOrphanArea",
659
- key: "assertMaxOrphanArea",
660
- parse: parseNumber,
661
- },
662
- {
663
- flag: "--assertRecoveryP95Ms",
664
- key: "assertRecoveryP95Ms",
665
- parse: parseNumber,
666
- },
667
- {
668
- flag: "--assertMaxReparentsPerMin",
669
- key: "assertMaxReparentsPerMin",
670
- parse: parseNumber,
671
- },
672
- ];
673
- const parseArgs = (argv) => {
674
- const get = (key) => {
675
- const idx = argv.indexOf(key);
676
- if (idx === -1)
677
- return undefined;
678
- return argv[idx + 1];
679
- };
680
- const has = (key) => argv.includes(key);
681
- if (argv.includes("--help") || argv.includes("-h")) {
682
- console.log(HELP_TEXT);
683
- process.exit(0);
684
- }
685
- const preset = has("--preset") ? String(get("--preset")) : undefined;
686
- const presetOpts = (preset ? PRESET_OPTIONS[preset] : undefined) ?? {};
687
- const explicitOpts = {};
688
- for (const spec of ARG_SPECS) {
689
- if (!has(spec.flag))
690
- continue;
691
- explicitOpts[spec.key] = spec.parse(get(spec.flag));
692
- }
693
- const merged = {
694
- ...presetOpts,
695
- ...explicitOpts,
696
- };
697
- // Live workloads are deadline-oriented: drop forwarding of stale DATA once it
698
- // exceeds the deadline (unless explicitly overridden).
699
- if (preset === "live" && !("maxDataAgeMs" in explicitOpts)) {
700
- const d = Number(merged.deadlineMs ?? 0);
701
- if (d > 0)
702
- merged.maxDataAgeMs = d;
703
- }
704
- return resolveFanoutTreeSimParams(merged);
705
- };
706
- const ASSERTION_SPECS = [
707
- {
708
- param: "assertMinJoinedPct",
709
- label: "joinedPct",
710
- mode: "min",
711
- value: (result) => result.joinedPct,
712
- formatActual: (value) => value.toFixed(2),
713
- },
714
- {
715
- param: "assertMinDeliveryPct",
716
- label: "deliveredPct",
717
- mode: "min",
718
- value: (result) => result.deliveredPct,
719
- formatActual: (value) => value.toFixed(2),
720
- },
721
- {
722
- param: "assertMaxUploadFracPct",
723
- label: "maxUploadFracPct",
724
- mode: "max",
725
- value: (result) => result.maxUploadFracPct,
726
- formatActual: (value) => value.toFixed(2),
727
- },
728
- {
729
- param: "assertMinDeadlineDeliveryPct",
730
- label: "deliveredWithinDeadlinePct",
731
- mode: "min",
732
- value: (result) => result.deliveredWithinDeadlinePct,
733
- formatActual: (value) => value.toFixed(2),
734
- },
735
- {
736
- param: "assertMaxOverheadFactor",
737
- label: "overheadFactorData",
738
- mode: "max",
739
- value: (result) => result.overheadFactorData,
740
- formatActual: (value) => value.toFixed(3),
741
- },
742
- {
743
- param: "assertMaxControlBpp",
744
- label: "controlBpp",
745
- mode: "max",
746
- value: (result) => result.controlBpp,
747
- formatActual: (value) => value.toFixed(4),
748
- },
749
- {
750
- param: "assertMaxTrackerBpp",
751
- label: "trackerBpp",
752
- mode: "max",
753
- value: (result) => result.trackerBpp,
754
- formatActual: (value) => value.toFixed(4),
755
- },
756
- {
757
- param: "assertMaxTrackerBytes",
758
- label: "protocolControlBytesSentTracker",
759
- mode: "max",
760
- value: (result) => result.protocolControlBytesSentTracker,
761
- },
762
- {
763
- param: "assertMaxRepairBpp",
764
- label: "repairBpp",
765
- mode: "max",
766
- value: (result) => result.repairBpp,
767
- formatActual: (value) => value.toFixed(4),
768
- },
769
- {
770
- param: "assertAttachP95Ms",
771
- label: "attachP95",
772
- mode: "max",
773
- value: (result) => result.attachP95,
774
- formatActual: (value) => `${value.toFixed(1)}ms`,
775
- formatExpected: (value) => `${value}ms`,
776
- },
777
- {
778
- param: "assertMaxTreeLevelP95",
779
- label: "treeLevelP95",
780
- mode: "max",
781
- value: (result) => result.treeLevelP95,
782
- formatActual: (value) => value.toFixed(1),
783
- },
784
- {
785
- param: "assertMaxFormationScore",
786
- label: "formationScore",
787
- mode: "max",
788
- value: (result) => result.formationScore,
789
- formatActual: (value) => value.toFixed(2),
790
- },
791
- {
792
- param: "assertMinReparentUpgradeTotal",
793
- label: "reparentUpgradeTotal",
794
- mode: "min",
795
- value: (result) => result.reparentUpgradeTotal,
796
- },
797
- {
798
- param: "assertMinActiveShadowPromoteTotal",
799
- label: "publishActiveParentShadowPromoteTotal",
800
- mode: "min",
801
- value: (result) => result.publishActiveParentShadowPromoteTotal,
802
- },
803
- {
804
- param: "assertMaxOrphans",
805
- label: "maintMaxOrphans",
806
- mode: "max",
807
- value: (result) => result.maintMaxOrphans,
808
- },
809
- {
810
- param: "assertMaxOrphanArea",
811
- label: "maintOrphanArea",
812
- mode: "max",
813
- value: (result) => result.maintOrphanArea,
814
- formatActual: (value) => value.toFixed(1),
815
- },
816
- {
817
- param: "assertRecoveryP95Ms",
818
- label: "maintRecoveryP95Ms",
819
- mode: "max",
820
- value: (result) => result.maintRecoveryP95Ms,
821
- formatActual: (value) => `${value.toFixed(1)}ms`,
822
- formatExpected: (value) => `${value}ms`,
823
- },
824
- {
825
- param: "assertMaxReparentsPerMin",
826
- label: "maintReparentsPerMin",
827
- mode: "max",
828
- value: (result) => result.maintReparentsPerMin,
829
- formatActual: (value) => value.toFixed(2),
830
- },
831
- ];
832
- const runAssertions = (params, result) => {
833
- for (const spec of ASSERTION_SPECS) {
834
- const threshold = params[spec.param];
835
- if (threshold <= 0)
836
- continue;
837
- const actual = spec.value(result);
838
- const failed = spec.mode === "min"
839
- ? actual + 1e-9 < threshold
840
- : actual - 1e-9 > threshold;
841
- if (!failed)
842
- continue;
843
- const actualText = spec.formatActual
844
- ? spec.formatActual(actual)
845
- : `${actual}`;
846
- const expectedText = spec.formatExpected
847
- ? spec.formatExpected(threshold)
848
- : `${threshold}`;
849
- const operator = spec.mode === "min" ? "<" : ">";
850
- console.error(`ASSERT FAILED: ${spec.label} ${actualText} ${operator} ${expectedText}`);
851
- process.exit(2);
852
- }
853
- };
854
- const main = async () => {
855
- const params = parseArgs(process.argv.slice(2));
856
- const result = await runFanoutTreeSim(params);
857
- console.log(formatFanoutTreeSimResult(result));
858
- runAssertions(params, result);
859
- };
860
- try {
861
- await main();
862
- }
863
- catch (err) {
864
- console.error(err?.message ?? String(err));
865
- if (String(err?.message ?? "").includes("timed out")) {
866
- process.exit(124);
867
- }
868
- process.exit(1);
869
- }
870
- //# sourceMappingURL=fanout-tree-sim.js.map