@decentnetwork/lan 0.1.191 → 0.1.192
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.
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -49,6 +49,7 @@ export interface MultiExitRouterOptions {
|
|
|
49
49
|
log?: (msg: string) => void;
|
|
50
50
|
}
|
|
51
51
|
export declare const BUILTIN_REGION_DOMAINS: Record<string, string[]>;
|
|
52
|
+
export declare function isUsefulRecoverySample(peakBps: number, averageBps: number, minBps?: number): boolean;
|
|
52
53
|
interface HlsExitScore {
|
|
53
54
|
speedEwma: number | null;
|
|
54
55
|
flooredAtMs: number | null;
|
|
@@ -149,6 +149,16 @@ const NEVER_LEARN = /(^|\.)(google|gstatic|googleapis|googleusercontent|ggpht|gv
|
|
|
149
149
|
// (≈0.5 Mbit/s) keeps every usable exit in and sits safely ABOVE the 40 KB/s
|
|
150
150
|
// deprioritize floor so a floored exit still drops out.
|
|
151
151
|
const NEAR_DEAD_EXIT_BPS = 60_000;
|
|
152
|
+
// Foreground HLS needs a materially stronger exit than ordinary browsing.
|
|
153
|
+
// CCTV's 720p variant is 1.8 Mbit/s; an exit below ~1.2 Mbit/s cannot deliver
|
|
154
|
+
// one four-second segment quickly enough to be useful even with a small live
|
|
155
|
+
// buffer. Keep such exits available for last-resort failover, but do not count
|
|
156
|
+
// them as HLS delivery capacity or let a slow redemption probe re-enable them.
|
|
157
|
+
// This is bytes/sec (150 KB/s = 1.2 Mbit/s).
|
|
158
|
+
const HLS_DELIVERY_BPS = 150_000;
|
|
159
|
+
export function isUsefulRecoverySample(peakBps, averageBps, minBps = HLS_DELIVERY_BPS) {
|
|
160
|
+
return peakBps >= minBps && averageBps >= minBps;
|
|
161
|
+
}
|
|
152
162
|
// A buffered segment fetch is given a deadline sized to deliver its body at
|
|
153
163
|
// roughly this bitrate; an exit that can't keep up is abandoned and the next is
|
|
154
164
|
// tried. Live HLS needs each ~4s segment in a few seconds, so this is set near
|
|
@@ -217,7 +227,7 @@ function floorExit(exit) {
|
|
|
217
227
|
/** Feed one tunnel's rate meter into the exit's speed EWMA (peak-window based).
|
|
218
228
|
* Only transfers past SPEED_MIN_BYTES count — smaller ones never reveal path
|
|
219
229
|
* capacity on a high-RTT link. */
|
|
220
|
-
function sampleSpeed(exit, meter, durMs) {
|
|
230
|
+
function sampleSpeed(exit, meter, durMs, recoveryMinBps = NEAR_DEAD_EXIT_BPS) {
|
|
221
231
|
exit.bytesDown += meter.total;
|
|
222
232
|
if (meter.total < SPEED_MIN_BYTES)
|
|
223
233
|
return;
|
|
@@ -229,7 +239,12 @@ function sampleSpeed(exit, meter, durMs) {
|
|
|
229
239
|
// replaces it with the fresh measurement instead of blending the floor into
|
|
230
240
|
// the result. Only a useful-speed transfer proves recovery; a completed but
|
|
231
241
|
// still-too-slow probe remains floored for another redemption interval.
|
|
232
|
-
|
|
242
|
+
// Peak-window speed is useful for ranking paths, but HLS redemption must
|
|
243
|
+
// also prove whole-segment average throughput. A probe observed at a 0.9
|
|
244
|
+
// Mbit/s peak but only 0.6 Mbit/s average caused six-way prefetch/hedge
|
|
245
|
+
// congestion when it rejoined a 1.8 Mbit/s stream.
|
|
246
|
+
const averageBps = durMs > 0 ? (meter.total / durMs) * 1000 : 0;
|
|
247
|
+
if (isUsefulRecoverySample(bps, averageBps, recoveryMinBps)) {
|
|
233
248
|
exit.speedEwma = bps;
|
|
234
249
|
exit.flooredAtMs = null;
|
|
235
250
|
}
|
|
@@ -247,7 +262,7 @@ function sampleSpeed(exit, meter, durMs) {
|
|
|
247
262
|
* exit is degraded, keep the original pool as a last resort. Exported for the
|
|
248
263
|
* policy regression tests. */
|
|
249
264
|
export function selectHlsPrimaryPool(pool) {
|
|
250
|
-
const proven = pool.filter((e) => e.flooredAtMs === null && e.speedEwma !== null && e.speedEwma >=
|
|
265
|
+
const proven = pool.filter((e) => e.flooredAtMs === null && e.speedEwma !== null && e.speedEwma >= HLS_DELIVERY_BPS);
|
|
251
266
|
if (proven.length > 0)
|
|
252
267
|
return proven;
|
|
253
268
|
const unproven = pool.filter((e) => e.flooredAtMs === null && e.speedEwma === null);
|
|
@@ -255,7 +270,10 @@ export function selectHlsPrimaryPool(pool) {
|
|
|
255
270
|
}
|
|
256
271
|
/** Dynamic prefetch concurrency follows only the foreground-safe pool. */
|
|
257
272
|
export function countHlsDeliveryExits(pool) {
|
|
258
|
-
|
|
273
|
+
// Two adjacent live segments in flight keep the buffer moving without the
|
|
274
|
+
// runaway 6-8 request fan-out seen when every marginal exit increased both
|
|
275
|
+
// prefetch concurrency and whole-segment hedging.
|
|
276
|
+
return Math.min(2, Math.max(1, selectHlsPrimaryPool(pool).length));
|
|
259
277
|
}
|
|
260
278
|
function fmtMbps(bps) {
|
|
261
279
|
return bps === null ? "?" : `${((bps * 8) / 1e6).toFixed(1)}Mbps`;
|
|
@@ -1274,7 +1292,7 @@ export function startMultiExitRouter(opts) {
|
|
|
1274
1292
|
nextExit(`empty ${status}`);
|
|
1275
1293
|
return;
|
|
1276
1294
|
}
|
|
1277
|
-
sampleSpeed(exit, meter, durMs);
|
|
1295
|
+
sampleSpeed(exit, meter, durMs, HLS_DELIVERY_BPS);
|
|
1278
1296
|
if (label) {
|
|
1279
1297
|
const kb = (body.length / 1024).toFixed(0);
|
|
1280
1298
|
const mbps = durMs > 0 ? ((body.length * 8) / durMs / 1000).toFixed(1) : "?";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decentnetwork/lan",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.192",
|
|
4
4
|
"description": "Private virtual LAN for self-hosted services and AI agents, built on Elastos Carrier. NAT-traversal, name service, ACL, all over a peer-to-peer mesh — no public IP required.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|