UNPKG

npaw-plugin-nwf

Version:
102 lines (101 loc) 4.56 kB
import type Cdn from '../Loaders/Cdn'; import type { FirstChunkProbeRegistry, FirstChunkProbeSettings } from './FirstChunkProbe'; /** * @file Central peak-bandwidth sampler. Mirrors Android `ActiveSwitcher`'s * peak-sampler scope (plugin-android@video-balancer-24-2-on-main). * * One sampler per balancer instance. Each in-flight CDN request carries a * BandwidthSampleTag that records observedBytes / readCount. The sampler * ticks every `tickMs` after a `warmupMs` delay, groups active tags by Cdn, * sums the bytes, and publishes a peak bandwidth (bits/sec) onto the Cdn. * * Peak is consumed only as a fallback when the EMA bandwidth is null. It is * also published at call close (`publishOnClose`) when both byte and read * thresholds are met. */ export interface PeakSamplerSettings { /** Initial delay before the first tick fires. */ warmupMs: number; /** Sampling cadence after the warm-up. */ tickMs: number; /** Minimum xhr.onprogress events for a tag to qualify before warm-up. */ minReadsForEarlySample: number; /** Minimum bytes a tag must have observed to qualify for sampling at all. */ minBytesPerCall: number; } export declare const DEFAULT_PEAK_SAMPLER_SETTINGS: PeakSamplerSettings; export interface BandwidthSampleTag { cdn: Cdn; startMs: number; observedBytes: number; readCount: number; } type NowFn = () => number; type SettingsSource = Partial<PeakSamplerSettings> | (() => Partial<PeakSamplerSettings> | undefined); /** * Per-balancer-instance central sampler. Started lazily on the first * registerActiveCall and stopped when the last call unregisters. * * Settings are read live: pass a getter at construction time and the sampler * resolves the current PeakSamplerSettings on every operation. This way * customData updates that mutate `BalancerOptions.peakSampler` take effect * without re-instantiating the sampler. */ export default class PeakBandwidthSampler { private readonly settingsSource; private readonly nowFn; private readonly tags; private warmupTimer; private tickTimer; private running; private armedTickMs; private firstChunkRegistry?; private firstChunkSettingsSource?; constructor(settings?: SettingsSource, now?: NowFn); /** * Wire a first-chunk probe registry into the sampler. Called once at * balancer construction. The settings source is read live (same pattern * as the peak-sampler settings) so customData updates apply at the next * tick. */ attachFirstChunkProbe(registry: FirstChunkProbeRegistry, settings?: Partial<FirstChunkProbeSettings> | (() => Partial<FirstChunkProbeSettings> | undefined)): void; private getFirstChunkSettings; /** Resolve the current effective settings (defaults + caller overrides). */ getSettings(): PeakSamplerSettings; /** * Register a freshly-dispatched call. Creates a sample tag rooted at the * caller-supplied Cdn. Returns the tag so the caller can update its byte * counter on every progress event. */ registerActiveCall(xhr: XMLHttpRequest, cdn: Cdn): BandwidthSampleTag; /** * Notify that a call ended. Publishes a final peak sample on close when * both byte and read thresholds are met (Android `publishOnClose`). Stops * the central timer when no calls remain. */ unregisterActiveCall(xhr: XMLHttpRequest): void; /** Update a tag's byte / read counters. Called from xhr.onprogress. */ recordProgress(xhr: XMLHttpRequest, observedBytes: number): void; /** Immediate snapshot of the live tag for a given XHR (tests / diagnostics). */ getTag(xhr: XMLHttpRequest): BandwidthSampleTag | undefined; /** Number of in-flight tracked calls. Tests + diagnostics. */ size(): number; /** Force a tick. Test hook; in production the timer fires it. */ tick(): void; /** * First-chunk probe abort evaluation (Android ActiveSwitcher.evaluateFirstChunkAborts). * For each registered probe with enough samples + a known Content-Length, * compute observedBps vs the requiredBps to clear the remaining bytes * within the remaining time. Abort when observedBps * safetyFactor falls * short. Idempotent via the `cancelled` flag. */ private evaluateFirstChunkAborts; /** Stop the timer and clear all tags. Used at balancer destroy. */ destroy(): void; private publishOnClose; private isReadyForSampling; private startTimer; private stopTimer; private static defaultNow; } export {};