@torrent-tv/proxy 2.86.0 → 2.87.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.
- package/CHANGELOG.md +2142 -1915
- package/CLAUDE.md +322 -197
- package/assets/calibration/NOTICE.md +2 -2
- package/bin/cli.js +684 -616
- package/biome.json +409 -233
- package/docs/browser-proxy-contract.md +94 -0
- package/docs/container-architecture.md +218 -213
- package/docs/download-architecture.md +7 -7
- package/docs/encode-architecture.md +287 -11
- package/docs/encode-run-state.md +2 -2
- package/docs/logs.md +97 -97
- package/docs/{disk-architecture.md → storage-architecture.md} +107 -1
- package/openspec/changes/adaptive-bitrate/design.md +2 -2
- package/openspec/changes/adaptive-bitrate/proposal.md +1 -1
- package/openspec/changes/adaptive-bitrate/tasks.md +1 -1
- package/openspec/changes/chunked-request-bodies/design.md +1 -1
- package/openspec/changes/chunked-request-bodies/proposal.md +1 -1
- package/openspec/changes/chunked-request-bodies/tasks.md +1 -1
- package/openspec/changes/cold-start/design.md +1 -1
- package/openspec/changes/disk-cap/proposal.md +1 -1
- package/openspec/changes/proxy-observability/proposal.md +2 -2
- package/openspec/changes/track-selection/proposal.md +1 -1
- package/openspec/changes/transcode-quality/proposal.md +1 -1
- package/package.json +2 -2
- package/research/delivery-wedge-stand-ladder-2026-09-13.md +329 -329
- package/research/double-spawn-2026-09-10.md +171 -171
- package/routes/api/playback-plan/post.js +2 -2
- package/routes/api/sources/files/get.js +1 -1
- package/routes/api/sources/stats/get.js +1 -1
- package/routes/api/sources/warm/post.js +10 -3
- package/routes/api/subtitles/get.js +230 -230
- package/routes/api/transcode-sessions/fragment-far/post.js +3 -3
- package/routes/api/transcode-sessions/net-report/post.js +129 -94
- package/routes/api/transcode-sessions/post.js +160 -139
- package/routes/api/transcode-sessions/progress/get.js +3 -3
- package/routes/api/transcode-sessions/release/post.js +3 -3
- package/routes/api/transcode-sessions/seek/post.js +12 -3
- package/routes/stream/get.js +2 -2
- package/routes/transcode/audio-file/get.js +15 -6
- package/routes/transcode/audio-warm/get.js +9 -6
- package/routes/transcode/session-file/get.js +52 -268
- package/routes/transcode/variant-file/get.js +46 -7
- package/routes/transcode/variant-warm/get.js +19 -6
- package/scripts/render-run-graph.js +1 -1
- package/server.js +720 -465
- package/services/encode/CoverageMap.js +428 -428
- package/services/encode/CushionReport.js +345 -0
- package/services/encode/EncodeAdmission.js +332 -0
- package/services/{orchestrators → encode}/EncodeOrchestrator.js +307 -29
- package/services/encode/EncodeRun.js +16 -9
- package/services/encode/EncodeRuns.js +1229 -0
- package/services/encode/Encoder.js +18 -2
- package/services/encode/EncoderSelection.js +38 -0
- package/services/encode/LocalObservations.js +359 -0
- package/services/encode/NvencEncoder.js +5 -0
- package/services/encode/OutputOpening.js +755 -0
- package/services/encode/OutputTimes.js +579 -0
- package/services/encode/Renditions.js +2162 -0
- package/services/encode/RunProgress.js +84 -0
- package/services/encode/SoftwareEncoder.js +12 -7
- package/services/encode/args.js +402 -186
- package/services/encode/calibration.js +361 -0
- package/services/encode/contention.js +128 -128
- package/services/encode/encode-run-state.js +16 -20
- package/services/encode/fingerprint.js +322 -0
- package/services/{hwaccel.js → encode/hwaccel.js} +82 -42
- package/services/{learned-median.js → encode/learned-median.js} +11 -0
- package/services/encode/output/EncodedOutput.js +57 -0
- package/services/encode/output/Output.js +131 -0
- package/services/{output/LiveOutputs.js → encode/output/OutputCatalog.js} +141 -40
- package/services/encode/output/OutputSpec.js +390 -0
- package/services/{output → encode/output}/Timeline.js +12 -37
- package/services/encode/output/index.js +20 -0
- package/services/encode/output/ladder.js +32 -0
- package/services/{output → encode/output}/playlists.js +3 -0
- package/services/encode/output-key-format.js +21 -0
- package/services/encode/quality/EncodeCost.js +1083 -0
- package/services/encode/quality/HostLoad.js +525 -0
- package/services/encode/quality/HostTimings.js +249 -0
- package/services/encode/quality/OutputQualityState.js +28 -0
- package/services/encode/quality/QualityController.js +935 -0
- package/services/encode/quality/QualityOffer.js +416 -0
- package/services/encode/quality/drain-threat.js +47 -0
- package/services/encode/quality/link-budget.js +254 -0
- package/services/encode/quality/output-format.js +266 -0
- package/services/encode/quality/serving-output.js +216 -0
- package/services/encode/quality/visible-rung.js +57 -0
- package/services/encode/run-command.js +23 -20
- package/services/encode/run-inputs.js +42 -0
- package/services/{segment-formats → encode/segment-formats}/fmp4.js +63 -0
- package/services/{segment-formats → encode/segment-formats}/index.js +3 -3
- package/services/encode/segment-formats/init-compat.js +324 -0
- package/services/{segment-formats → encode/segment-formats}/mp4-boxes.js +8 -3
- package/services/encode/throughput.js +97 -0
- package/services/{orchestrators → media}/ContainerOrchestrator.js +45 -7
- package/services/media/KeyframeTables.js +316 -0
- package/services/{source → media}/SourceFile.js +7 -55
- package/services/{torrent-worker/subtitle-cues.js → media/SubtitleCues.js} +103 -132
- package/services/media/SubtitleOrchestrator.js +167 -0
- package/services/{audio-inventory.js → media/audio-inventory.js} +7 -1
- package/services/{container → media/container}/AviContainer.js +266 -266
- package/services/{container → media/container}/Container.js +596 -601
- package/services/media/container/ContainerFactory.js +56 -0
- package/services/media/container/KeyframeTable.js +272 -0
- package/services/{container → media/container}/MatroskaContainer.js +1485 -1485
- package/services/{container → media/container}/Mp4Container.js +1244 -1244
- package/services/{container → media/container}/SubtitleFileContainer.js +322 -322
- package/services/media/keyframe-probe.js +157 -0
- package/services/media/media-info-probe.js +93 -0
- package/services/{playback-planner.js → media/playback-planner.js} +82 -39
- package/services/{tracks → media/tracks}/AudioTrack.js +131 -131
- package/services/{tracks → media/tracks}/language-detect.js +174 -174
- package/services/server/OutputLifecycle.js +418 -0
- package/services/server/SegmentServing.js +1146 -0
- package/services/server/ViewerRequests.js +522 -0
- package/services/server/controllers/PlaybackController.js +36 -0
- package/services/{controllers → server/controllers}/SubtitleController.js +17 -33
- package/services/server/http-responses.js +60 -0
- package/services/server/transcode-session-files.js +325 -0
- package/services/server/wire-outputs.js +983 -0
- package/services/storage/Diagnostics.js +202 -0
- package/services/storage/MachineBudget.js +235 -0
- package/services/{piece-store → storage}/allowance.js +107 -107
- package/services/{core-dumps.js → storage/core-dumps.js} +1 -1
- package/services/{files → storage/files}/CompletedFiles.js +116 -1
- package/services/{disk → storage}/free.js +33 -0
- package/services/storage/machine-memory.js +56 -0
- package/services/{memory-report.js → storage/memory-report.js} +575 -596
- package/services/{piece-store → storage/piece-store}/piece-disk-store.js +0 -7
- package/services/{piece-store → storage/piece-store}/shared-piece-store.js +28 -21
- package/services/{encode → storage/segment-store}/SegmentStore.js +395 -8
- package/services/storage/wire.js +175 -0
- package/services/{download → torrent/download}/SwarmSelection.js +2 -2
- package/services/{download → torrent/download}/registry.js +2 -2
- package/services/torrent/files.js +1 -1
- package/services/torrent/piece-store-of.js +29 -0
- package/services/{torrent-pool.js → torrent/torrent-pool.js} +26 -119
- package/services/{torrent-worker → torrent/worker}/background-fill.js +2 -2
- package/services/{torrent-worker → torrent/worker}/channel.js +271 -250
- package/services/{torrent-worker → torrent/worker}/client.js +77 -58
- package/services/torrent/worker/held-bytes.js +155 -0
- package/services/{torrent-worker → torrent/worker}/install-webrtc-shim.js +1 -1
- package/services/{torrent-worker → torrent/worker}/piece-reader.js +21 -30
- package/services/{torrent-worker → torrent/worker}/pool-adapter.js +101 -137
- package/services/{torrent-worker → torrent/worker}/protocol.js +26 -30
- package/services/torrent/worker/resume-warm.js +107 -0
- package/services/{torrent-worker → torrent/worker}/webrtc-shim.js +2 -2
- package/services/{torrent-worker → torrent/worker}/worker.js +68 -159
- package/services/{data-channel-handler.js → transport/data-channel-handler.js} +1356 -1356
- package/services/{delivery-probe.js → transport/delivery-probe.js} +842 -842
- package/services/{health-collector.js → transport/health-collector.js} +6 -35
- package/services/{packet-witness.js → transport/packet-witness.js} +808 -784
- package/services/{port-mapper.js → transport/port-mapper.js} +1 -1
- package/services/{usrsctp-state.js → transport/usrsctp-state.js} +1 -1
- package/services/{webrtc-manager.js → transport/webrtc-manager.js} +1 -1
- package/services/viewer/Assignments.js +340 -0
- package/services/{priority → viewer}/PriorityMap.js +390 -390
- package/services/{priority → viewer}/PriorityOrchestrator.js +325 -320
- package/services/viewer/Viewer.js +657 -483
- package/services/viewer/Viewers.js +368 -257
- package/services/viewer/active-output.js +37 -54
- package/services/viewer/audio-start.js +4 -3
- package/services/viewer/buffer-trend.js +81 -0
- package/services/viewer/choices.js +647 -0
- package/services/viewer/positions.js +126 -120
- package/services/viewer/report-intake.js +46 -45
- package/test/asset-paths.test.js +28 -0
- package/test/audio-inventory.test.js +3 -3
- package/test/audio-track-bitrate.test.js +40 -0
- package/test/auto-quality-step.test.js +707 -515
- package/test/available-share.test.js +1 -1
- package/test/background-fill.test.js +1 -1
- package/test/client-log-file.test.js +110 -110
- package/test/completed-files.test.js +1 -1
- package/test/concurrent-cost.test.js +13 -3
- package/test/container-media-info.test.js +3 -3
- package/test/core-dumps.test.js +1 -1
- package/test/coverage-follows-the-disk.test.js +46 -9
- package/test/cue-push-carries-what-the-walk-found.test.js +58 -0
- package/test/cut-times-timeline.test.js +2 -2
- package/test/cuts-follow-published-grid.test.js +9 -8
- package/test/data-channel-frame.test.js +1 -1
- package/test/dead-torrent-handle.test.js +1 -1
- package/test/declared-rates.test.js +2 -2
- package/test/declared-tracks.test.js +2 -2
- package/test/decode-cost-fit.test.js +1 -1
- package/test/decode-cost.test.js +34 -25
- package/test/delivery-probe.test.js +1 -1
- package/test/demand-register.test.js +4 -4
- package/test/dht-bootstrap.test.js +1 -1
- package/test/drain-threat.test.js +65 -0
- package/test/duplicate-add.test.js +1 -1
- package/test/encode-admission.test.js +146 -0
- package/test/encode-cost.test.js +199 -10
- package/test/encode-orchestrator.test.js +39 -1
- package/test/encode-plan-viewers.test.js +2 -2
- package/test/encode-run.test.js +2 -2
- package/test/encode-slope.test.js +1 -1
- package/test/encoded-output.test.js +39 -0
- package/test/encoder-readings.test.js +1 -1
- package/test/every-claimant-is-told-its-share.test.js +152 -0
- package/test/failed-encoder-still-serves-what-exists.test.js +354 -0
- package/test/failed-start-breaker.test.js +81 -30
- package/test/fastest-wires.test.js +1 -1
- package/test/ffmpeg-stream-counts.test.js +1 -1
- package/test/file-edges.test.js +3 -3
- package/test/fmp4-self-contained.test.js +1 -1
- package/test/fmp4-timestamps.test.js +1 -1
- package/test/fmp4-track-completeness.test.js +1 -1
- package/test/format-keyed-outputs.test.js +78 -0
- package/test/health-metrics.test.js +3 -2
- package/test/held-request-width.test.js +22 -23
- package/test/helpers/encode-run.js +136 -136
- package/test/helpers/held-file.js +29 -0
- package/test/helpers/manager.js +57 -0
- package/test/helpers/output-spec.js +63 -0
- package/test/host-load.test.js +1 -1
- package/test/index-accuracy-is-about-the-file.test.js +128 -0
- package/test/init-compat.test.js +219 -0
- package/test/input-lost-quiets-the-plan.test.js +1 -1
- package/test/input-unavailable.test.js +1 -1
- package/test/keeping-period.test.js +4 -4
- package/test/keyframe-index-accuracy.test.js +97 -45
- package/test/keyframe-table-budget.test.js +28 -33
- package/test/keyframes-belong-to-the-file.test.js +174 -57
- package/test/learned-median.test.js +1 -1
- package/test/letting-go-asks-who-is-watching.test.js +191 -0
- package/test/limit-row-by-area.test.js +60 -0
- package/test/link-budget.test.js +111 -0
- package/test/link-probe.test.js +56 -56
- package/test/local-encoding-data.test.js +273 -0
- package/test/look-ahead-coverage.test.js +1 -1
- package/test/manager-surface.test.js +294 -0
- package/test/matroska-blocks.test.js +0 -0
- package/test/matroska-cues-track.test.js +1 -1
- package/test/memory-budget.test.js +2 -2
- package/test/mp4-composition-times.test.js +0 -0
- package/test/mp4-subtitles.test.js +1 -1
- package/test/one-authority.test.js +45 -41
- package/test/one-budget.test.js +149 -0
- package/test/one-output-one-session.test.js +204 -0
- package/test/{disk-space.test.js → one-resource-divided.test.js} +30 -10
- package/test/one-serving-output-per-height.test.js +458 -0
- package/test/orchestrator-wired.test.js +46 -45
- package/test/output-catalog.test.js +39 -0
- package/test/output-shape.test.js +4 -51
- package/test/output-spec.test.js +251 -157
- package/test/output-speed.test.js +17 -15
- package/test/outputs-being-prepared.test.js +40 -0
- package/test/packet-witness-ring.test.js +2 -2
- package/test/packet-witness.test.js +1 -1
- package/test/piece-disk-store.test.js +2 -2
- package/test/piece-from-whole-file.test.js +1 -1
- package/test/piece-lru.test.js +1 -1
- package/test/piece-reader.test.js +2 -2
- package/test/piece-store-eviction.test.js +1 -1
- package/test/piece-store-never-refuses.test.js +1 -1
- package/test/piece-store-reservations.test.js +1 -1
- package/test/piece-store-slow-disk.test.js +1 -1
- package/test/piece-tail.test.js +1 -1
- package/test/plan-host-timings.test.js +1 -1
- package/test/plan-is-steady.test.js +225 -225
- package/test/priority-map-download.test.js +4 -4
- package/test/priority-map-emptied.test.js +204 -205
- package/test/priority-map-per-output.test.js +215 -209
- package/test/priority-map.test.js +1 -1
- package/test/probe-wedge-certainty.test.js +1 -1
- package/test/produced-copy-choice.test.js +15 -60
- package/test/quality-offer.test.js +162 -0
- package/test/quality-variants.test.js +1229 -1167
- package/test/rate-control-in-the-output.test.js +180 -0
- package/test/read-survives-withdrawal.test.js +2 -2
- package/test/read-window.test.js +10 -3
- package/test/resume-warm.test.js +1 -1
- package/test/run-inputs.test.js +80 -0
- package/test/run-position-follows-published-grid.test.js +13 -11
- package/test/run-progress.test.js +65 -0
- package/test/same-height-switch.test.js +664 -0
- package/test/seek-frees-the-viewers-requests.test.js +217 -0
- package/test/seek-landing.test.js +13 -13
- package/test/seek-target-not-superseded.test.js +28 -29
- package/test/segment-closed-on-both-branches.test.js +96 -0
- package/test/segment-cut-times.test.js +1 -1
- package/test/segment-hold-supersede.test.js +28 -35
- package/test/segment-judged-by-its-own-header.test.js +221 -0
- package/test/segment-serve-wiring.test.js +130 -39
- package/test/segment-store-eviction.test.js +22 -7
- package/test/segment-store.test.js +98 -14
- package/test/segment-true-start.test.js +1 -1
- package/test/segments-are-shared.test.js +17 -16
- package/test/send-chunk-bytes.test.js +1 -1
- package/test/serving-existing-output.test.js +102 -0
- package/test/serving-output.test.js +102 -0
- package/test/shared-piece-store.test.js +1 -1
- package/test/source-file.test.js +1 -1
- package/test/stale-request-after-seek.test.js +17 -21
- package/test/startup-readings.test.js +10 -5
- package/test/subtitle-cue-framing.test.js +5 -5
- package/test/subtitle-cue-source.test.js +36 -38
- package/test/subtitle-cue-walk.test.js +11 -10
- package/test/subtitle-defaults.test.js +1 -1
- package/test/subtitle-language.test.js +4 -4
- package/test/subtitle-track-numbering.test.js +5 -4
- package/test/supply-margin.test.js +1 -1
- package/test/swarm-follows-readers.test.js +3 -3
- package/test/swarm-reach.test.js +1 -1
- package/test/swarm-selection.test.js +3 -3
- package/test/tail-duplication.test.js +1 -1
- package/test/the-channel-has-both-ends.test.js +91 -0
- package/test/the-torrent-thread-serves-bytes.test.js +107 -0
- package/test/thread-pool.test.js +3 -3
- package/test/timeline.test.js +1 -12
- package/test/torrent-cost.test.js +1 -1
- package/test/tracks-begin-together.test.js +38 -24
- package/test/tunnel-renewal.test.js +1 -1
- package/test/two-viewers-one-picture.test.js +120 -87
- package/test/upload-hurry.test.js +3 -3
- package/test/usrsctp-state.test.js +1 -1
- package/test/video-facts.test.js +1 -1
- package/test/viewer-assignments.test.js +143 -0
- package/test/viewer-outputs.test.js +60 -52
- package/test/viewer-presence.test.js +22 -25
- package/test/viewer-requests-admission.test.js +89 -0
- package/test/viewer-states.test.js +189 -202
- package/test/viewer-subtitle-subscription.test.js +1 -1
- package/test/viewer.test.js +139 -136
- package/test/viewing-generations.test.js +390 -0
- package/test/visible-rung.test.js +37 -0
- package/test/wait-ledger.test.js +1 -1
- package/test/webrtc-shim.test.js +3 -3
- package/test/wedge-certainty.test.js +2 -2
- package/test/withdraw-piece-claim.test.js +2 -2
- package/test/worker-channel.test.js +1 -31
- package/utils/client-log-file.js +151 -151
- package/scripts/read-container-index.mjs +0 -108
- package/services/container/ContainerFactory.js +0 -122
- package/services/controllers/PlaybackController.js +0 -33
- package/services/disk/DiskSpace.js +0 -150
- package/services/disk/wire.js +0 -61
- package/services/hls-session-manager.js +0 -9608
- package/services/orchestrators/SubtitleOrchestrator.js +0 -160
- package/services/output/Output.js +0 -94
- package/services/output/OutputSpec.js +0 -202
- package/services/output/index.js +0 -1
- package/services/output/ladder.js +0 -26
- package/services/produced-index.js +0 -222
- package/services/quality/EncodeCost.js +0 -555
- package/services/torrent-worker/container-tracks.js +0 -414
- package/services/viewer/link-readings.js +0 -56
- package/test/produced-index.test.js +0 -142
- /package/{services → bin}/thread-pool.js +0 -0
- /package/services/{available-share.js → encode/available-share.js} +0 -0
- /package/services/{decode-cost-fit.js → encode/decode-cost-fit.js} +0 -0
- /package/services/{encoder-readings.js → encode/encoder-readings.js} +0 -0
- /package/services/{host-load.js → encode/host-load.js} +0 -0
- /package/services/{output → encode/output}/cut-grid.js +0 -0
- /package/services/{output → encode/output}/rates.js +0 -0
- /package/services/{segment-formats → encode/segment-formats}/mpegts.js +0 -0
- /package/services/{torrent-cost.js → encode/torrent-cost.js} +0 -0
- /package/services/{container → media/container}/ebml-reader.js +0 -0
- /package/services/{ffmpeg-banner.js → media/ffmpeg-banner.js} +0 -0
- /package/services/{tracks → media/tracks}/ContainerTrack.js +0 -0
- /package/services/{tracks → media/tracks}/ImageSubtitleTrack.js +0 -0
- /package/services/{tracks → media/tracks}/SubtitleTrack.js +0 -0
- /package/services/{tracks → media/tracks}/TextSubtitleTrack.js +0 -0
- /package/services/{tracks → media/tracks}/VideoTrack.js +0 -0
- /package/services/{tracks → media/tracks}/index.js +0 -0
- /package/services/{files → storage/files}/piece-from-whole-file.js +0 -0
- /package/services/{disk → storage}/keep.js +0 -0
- /package/services/{piece-store → storage/piece-store}/piece-lru.js +0 -0
- /package/services/{disk → storage}/returns.js +0 -0
- /package/services/{demand → torrent/demand}/DemandRegister.js +0 -0
- /package/services/{demand → torrent/demand}/Urgency.js +0 -0
- /package/services/{demand → torrent/demand}/Window.js +0 -0
- /package/services/{demand → torrent/demand}/index.js +0 -0
- /package/services/{demand → torrent/demand}/pieces.js +0 -0
- /package/services/{download → torrent/download}/withdraw-claim.js +0 -0
- /package/services/{supply-margin.js → torrent/supply-margin.js} +0 -0
- /package/services/{torrent-source-key.js → torrent/torrent-source-key.js} +0 -0
- /package/services/{torrent-worker → torrent/worker}/fastest-wires.js +0 -0
- /package/services/{torrent-worker → torrent/worker}/handle-state.js +0 -0
- /package/services/{nat-classifier.js → transport/nat-classifier.js} +0 -0
- /package/services/{registry-api.js → transport/registry-api.js} +0 -0
- /package/services/{tunnel-client.js → transport/tunnel-client.js} +0 -0
- /package/services/{priority → viewer}/WaitLedger.js +0 -0
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file One owner of the disk, read by everything that takes any of it.
|
|
3
|
-
*
|
|
4
|
-
* Three things on this proxy write to the same disk and, until now, each
|
|
5
|
-
* decided for itself how much it could take:
|
|
6
|
-
*
|
|
7
|
-
* - the segments an encoder produces, bounded by a quarter of what was free
|
|
8
|
-
* plus a floor of 2 GB, both numbers chosen out of nothing;
|
|
9
|
-
* - the pieces the memory store spills, bounded by nothing at all — 14 400 MB
|
|
10
|
-
* written in one fifty-minute viewing, field 2026-08-31;
|
|
11
|
-
* - the diagnostics we keep on purpose (core dumps, heap snapshots, packet
|
|
12
|
-
* captures), each bounded by a COUNT and none by a size: on the addon host
|
|
13
|
-
* two dumps and five snapshots came to 3.2 GB.
|
|
14
|
-
*
|
|
15
|
-
* Each read the free space as though it were the only claimant, so three
|
|
16
|
-
* ceilings each stood for the whole disk. This is the one place that reads it,
|
|
17
|
-
* and what it hands out is a share.
|
|
18
|
-
*
|
|
19
|
-
* THE RULE IS THE ONE MEMORY ALREADY USES, on the other reading: what is free
|
|
20
|
-
* now, plus what we already hold, less what everything that is not us has been
|
|
21
|
-
* seen to need. Nothing is a fraction chosen by hand.
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
import { OtherDemand, divideAllowance } from "../piece-store/allowance.js";
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* A consumer of the disk.
|
|
28
|
-
*
|
|
29
|
-
* @typedef {object} DiskConsumer
|
|
30
|
-
* @property {string} name - What it is called in the reading.
|
|
31
|
-
* @property {() => number} held - What it holds right now, in bytes.
|
|
32
|
-
* @property {() => number} wanted - What it would take if it could. A consumer
|
|
33
|
-
* that cannot say asks for what it holds, which is the honest statement of a
|
|
34
|
-
* thing that only grows when something arrives.
|
|
35
|
-
* @property {(allowanceBytes: number) => void} allow - Told its share.
|
|
36
|
-
*/
|
|
37
|
-
|
|
38
|
-
export class DiskSpace {
|
|
39
|
-
/** @type {Map<string, DiskConsumer>} */
|
|
40
|
-
#consumers = new Map();
|
|
41
|
-
|
|
42
|
-
#otherDemand = new OtherDemand();
|
|
43
|
-
|
|
44
|
-
#readFree;
|
|
45
|
-
|
|
46
|
-
#logger;
|
|
47
|
-
|
|
48
|
-
/** The last division, for the reading. @type {{ name: string, held: number, allowed: number }[]} */
|
|
49
|
-
#last = [];
|
|
50
|
-
|
|
51
|
-
#freeBytes = 0;
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* @param {object} params
|
|
55
|
-
* @param {() => Promise<number | null>} params.readFree - What the machine
|
|
56
|
-
* says is free on the disk these consumers share.
|
|
57
|
-
* @param {{ info: (line: string) => void, warn?: (line: string) => void }} [params.logger]
|
|
58
|
-
*/
|
|
59
|
-
constructor({ readFree, logger = null }) {
|
|
60
|
-
this.#readFree = readFree;
|
|
61
|
-
this.#logger = logger;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Register a consumer. Registering twice under one name replaces it.
|
|
66
|
-
*
|
|
67
|
-
* @param {DiskConsumer} consumer
|
|
68
|
-
* @returns {void}
|
|
69
|
-
*/
|
|
70
|
-
register(consumer) {
|
|
71
|
-
this.#consumers.set(consumer.name, consumer);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* @param {string} name
|
|
76
|
-
* @returns {void}
|
|
77
|
-
*/
|
|
78
|
-
forget(name) {
|
|
79
|
-
this.#consumers.delete(name);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Read the disk, divide it, and tell each consumer its share.
|
|
84
|
-
*
|
|
85
|
-
* Called on the same timer that revises memory: a disk that fills while a
|
|
86
|
-
* film is playing must lower the ceilings, not keep ones taken when it was
|
|
87
|
-
* empty. That is the mistake memory made until 2026-08-28, and every disk
|
|
88
|
-
* ceiling on this proxy made until now.
|
|
89
|
-
*
|
|
90
|
-
* @returns {Promise<{ freeBytes: number, allowanceBytes: number, shares: { name: string, held: number, allowed: number }[] }>}
|
|
91
|
-
*/
|
|
92
|
-
async revise() {
|
|
93
|
-
const consumers = [...this.#consumers.values()];
|
|
94
|
-
if (consumers.length === 0) {
|
|
95
|
-
return { freeBytes: 0, allowanceBytes: 0, shares: [] };
|
|
96
|
-
}
|
|
97
|
-
const held = consumers.reduce((sum, consumer) => sum + Math.max(0, consumer.held()), 0);
|
|
98
|
-
const free = await this.#readFree();
|
|
99
|
-
// WITHOUT A READING, NOTHING IS ALLOWED TO GROW. A disk whose free space
|
|
100
|
-
// cannot be read is not a disk with room; answering "unbounded" there is how
|
|
101
|
-
// the spill file came to have no limit in the first place.
|
|
102
|
-
this.#freeBytes = Number.isFinite(free) && free !== null ? Math.max(0, free) : 0;
|
|
103
|
-
const reserve = this.#otherDemand.note(this.#freeBytes, held);
|
|
104
|
-
const allowance = Math.max(0, this.#freeBytes + held - reserve);
|
|
105
|
-
const shares = divideAllowance(
|
|
106
|
-
consumers.map((consumer) => Math.max(0, consumer.wanted())),
|
|
107
|
-
allowance
|
|
108
|
-
);
|
|
109
|
-
this.#last = consumers.map((consumer, position) => ({
|
|
110
|
-
name: consumer.name,
|
|
111
|
-
held: consumer.held(),
|
|
112
|
-
allowed: shares[position]
|
|
113
|
-
}));
|
|
114
|
-
for (const [position, consumer] of consumers.entries()) {
|
|
115
|
-
consumer.allow(shares[position]);
|
|
116
|
-
}
|
|
117
|
-
// SAID, every pass, in the series beside the memory reading. "Why is there
|
|
118
|
-
// no room" was a question no log could answer: the ceilings were worked out
|
|
119
|
-
// in three places and not one of them was printed beside the others.
|
|
120
|
-
this.#logger?.info?.(this.describe());
|
|
121
|
-
return { freeBytes: this.#freeBytes, allowanceBytes: allowance, shares: this.#last };
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* One line: what the disk has, what each consumer holds, and what it may.
|
|
126
|
-
*
|
|
127
|
-
* Said because "why is there no room" is otherwise a question no log can
|
|
128
|
-
* answer — the three ceilings were computed in three places and none of them
|
|
129
|
-
* was printed beside the others.
|
|
130
|
-
*
|
|
131
|
-
* @returns {string}
|
|
132
|
-
*/
|
|
133
|
-
describe() {
|
|
134
|
-
if (this.#last.length === 0) {
|
|
135
|
-
return "disk: nothing has claimed any yet";
|
|
136
|
-
}
|
|
137
|
-
const parts = this.#last.map(
|
|
138
|
-
(share) => `${share.name} ${megabytes(share.held)} of ${megabytes(share.allowed)}`
|
|
139
|
-
);
|
|
140
|
-
return `disk: ${megabytes(this.#freeBytes)} free; ${parts.join(", ")}`;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* @param {number} bytes
|
|
146
|
-
* @returns {string}
|
|
147
|
-
*/
|
|
148
|
-
function megabytes(bytes) {
|
|
149
|
-
return `${Math.round(Math.max(0, bytes) / (1024 * 1024))}MB`;
|
|
150
|
-
}
|
package/services/disk/wire.js
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file Who takes disk, and how each of them is told its share.
|
|
3
|
-
*
|
|
4
|
-
* Kept apart from the owner because the owner knows nothing about this proxy —
|
|
5
|
-
* it reads a number, divides it and hands out shares — and kept out of the
|
|
6
|
-
* session manager because a list of claimants is not a fact about a session.
|
|
7
|
-
*
|
|
8
|
-
* Two claimants today. The diagnostics are a third and are not here yet: they
|
|
9
|
-
* are bounded by a COUNT and never by a size, which on the addon host let two
|
|
10
|
-
* core dumps and five heap snapshots come to 3.2 GB, and they cannot simply be
|
|
11
|
-
* thrown away when space is short — a dump is the only evidence of the death it
|
|
12
|
-
* records. That needs a rule of its own.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import { DiskSpace } from "./DiskSpace.js";
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Build the owner of the disk and register everything that takes any of it.
|
|
19
|
-
*
|
|
20
|
-
* @param {object} params
|
|
21
|
-
* @param {{ root: string, stats: () => { bytes: number } }} params.segmentStore
|
|
22
|
-
* @param {{ held: () => number, allow: (bytes: number) => unknown }} [params.spill] -
|
|
23
|
-
* The pieces the memory store spills. They live on the torrent thread, so
|
|
24
|
-
* this is a pair of closures over the channel rather than the pool itself.
|
|
25
|
-
* @param {(directory: string) => Promise<number | null>} params.readFree
|
|
26
|
-
* @param {{ info: Function, warn?: Function }} [params.logger]
|
|
27
|
-
* @returns {{ revise: () => Promise<unknown>, segmentBytes: () => number, describe: () => string }}
|
|
28
|
-
* What the segments may hold is asked for rather than pushed: zero until the
|
|
29
|
-
* first revision, and zero stops growth rather than licensing it.
|
|
30
|
-
*/
|
|
31
|
-
export function wireDiskSpace({ segmentStore, spill, readFree, logger }) {
|
|
32
|
-
const space = new DiskSpace({ readFree: () => readFree(segmentStore.root), logger });
|
|
33
|
-
let segmentBytes = 0;
|
|
34
|
-
space.register({
|
|
35
|
-
name: "segments",
|
|
36
|
-
held: () => segmentStore.stats().bytes,
|
|
37
|
-
// The whole of every film anybody is watching. There is no smaller honest
|
|
38
|
-
// answer, so it asks for everything and is cut in proportion like the rest.
|
|
39
|
-
wanted: () => Number.MAX_SAFE_INTEGER,
|
|
40
|
-
allow: (bytes) => {
|
|
41
|
-
segmentBytes = bytes;
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
if (typeof spill?.allow === "function") {
|
|
45
|
-
// The share travels the channel that already carries everything else, and
|
|
46
|
-
// the reply says what they hold — one exchange, both directions.
|
|
47
|
-
space.register({
|
|
48
|
-
name: "spilled pieces",
|
|
49
|
-
held: () => spill.held?.() ?? 0,
|
|
50
|
-
wanted: () => Number.MAX_SAFE_INTEGER,
|
|
51
|
-
allow: (bytes) => {
|
|
52
|
-
void spill.allow(bytes);
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
return {
|
|
57
|
-
revise: () => space.revise(),
|
|
58
|
-
segmentBytes: () => segmentBytes,
|
|
59
|
-
describe: () => space.describe()
|
|
60
|
-
};
|
|
61
|
-
}
|