@torrent-tv/proxy 2.89.12 → 2.89.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/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
## 2.89.13
|
|
2
|
+
|
|
3
|
+
- **Fix**: The proxy no longer stops answering everybody when one output is priced near zero. Asking how many encoders an output may run counted them one at a time until the machine was full, so the number of steps grew as the price shrank. On the stand on 2026-10-04 a soundtrack priced at 5.4e-14 s of machine per second of film held the main thread in that count for minutes, past 3.3 billion steps: no data channel was served, `/healthz` did not answer, and every viewer stopped. The count is now found by doubling and halving, in at most a few hundred steps whatever the price. The price itself is wrong and is not corrected here.
|
|
4
|
+
|
|
1
5
|
## 2.89.12
|
|
2
6
|
|
|
3
7
|
- **Fix**: A connection that is gone is no longer probed. The delivery probe stopped only when a data channel reported `onClosed`, and a peer connection closed moments after its channels opened reports nothing: on the stand on 2026-10-04 a trial connection the page closed 70 ms after opening was still sent probes twice a second hours later, one timer left behind per such connection. When the transport watcher finds the connection gone, its probes now end with it.
|
package/package.json
CHANGED
|
@@ -356,17 +356,37 @@ export class EncodeAdmission {
|
|
|
356
356
|
return { runs: Number.POSITIVE_INFINITY, because: "the output is not priced" };
|
|
357
357
|
}
|
|
358
358
|
const others = this.#occupied(address);
|
|
359
|
+
const fits = (count) => this.#speedAt(others.costSec + EncodeAdmission.#added(load, others.files, count)) >= 1;
|
|
359
360
|
let runs = 0;
|
|
360
|
-
|
|
361
|
-
const next = runs + 1;
|
|
362
|
-
if (this.#speedAt(others.costSec + EncodeAdmission.#added(load, others.files, next)) < 1) {
|
|
363
|
-
break;
|
|
364
|
-
}
|
|
365
|
-
runs = next;
|
|
361
|
+
if (fits(1)) {
|
|
366
362
|
if (!(load.costSec > 0)) {
|
|
367
363
|
// Free to run: the per-output limit is the only bound.
|
|
368
364
|
return { runs: Number.POSITIVE_INFINITY, because: "it costs nothing measurable" };
|
|
369
365
|
}
|
|
366
|
+
// The largest count that still fits, found by doubling and halving: the
|
|
367
|
+
// speed only falls as encoders are added. Counting up one at a time took
|
|
368
|
+
// as many steps as the price is small — field 2026-10-04, a soundtrack
|
|
369
|
+
// priced at 5.4e-14 s per film second kept the main thread in this loop
|
|
370
|
+
// for minutes, past 3.3 billion steps, and every viewer of the proxy
|
|
371
|
+
// stopped receiving.
|
|
372
|
+
let low = 1;
|
|
373
|
+
let high = 2;
|
|
374
|
+
while (fits(high)) {
|
|
375
|
+
low = high;
|
|
376
|
+
high *= 2;
|
|
377
|
+
}
|
|
378
|
+
for (;;) {
|
|
379
|
+
const middle = Math.floor((low + high) / 2);
|
|
380
|
+
if (middle === low || middle === high) {
|
|
381
|
+
break;
|
|
382
|
+
}
|
|
383
|
+
if (fits(middle)) {
|
|
384
|
+
low = middle;
|
|
385
|
+
} else {
|
|
386
|
+
high = middle;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
runs = low;
|
|
370
390
|
}
|
|
371
391
|
if (runs >= floor) {
|
|
372
392
|
return { runs, because: "the machine" };
|
|
@@ -144,3 +144,27 @@ test("unpriced occupied work is neither spare pool headroom nor capacity for ano
|
|
|
144
144
|
assert.equal(answer.admitted, false);
|
|
145
145
|
assert.match(answer.reason, /capacity is unknown/);
|
|
146
146
|
});
|
|
147
|
+
|
|
148
|
+
test("a near-free output is answered at once, whatever its price", () => {
|
|
149
|
+
// Field 2026-10-04: a soundtrack priced at 5.4e-14 s per film second kept
|
|
150
|
+
// the main thread counting encoders one at a time for minutes.
|
|
151
|
+
const { admission } = machine({ costs: { sound: 5.36680075607109e-14 }, share: 0.6 });
|
|
152
|
+
const started = Date.now();
|
|
153
|
+
const { runs } = admission.placesFor("sound", 0);
|
|
154
|
+
assert.ok(Date.now() - started < 1_000, "answered without counting one at a time");
|
|
155
|
+
assert.ok(runs > 1e12 && Number.isFinite(runs), `as many as fit: ${runs}`);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test("the count is the largest that keeps every output at realtime", () => {
|
|
159
|
+
for (const cost of [0.5, 0.1, 0.03, 0.007, 0.0011]) {
|
|
160
|
+
for (const share of [null, 1, 0.37]) {
|
|
161
|
+
const { admission } = machine({ costs: { a: cost, b: PICTURE_1080 / 4 }, running: { b: 1 }, share });
|
|
162
|
+
const usable = share ?? 1;
|
|
163
|
+
let expected = 0;
|
|
164
|
+
while (usable / (PICTURE_1080 / 4 + (expected + 1) * cost) >= 1) {
|
|
165
|
+
expected += 1;
|
|
166
|
+
}
|
|
167
|
+
assert.equal(admission.placesFor("a", 0).runs, Math.max(expected, 0), `cost ${cost}, share ${share}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
});
|