npaw-plugin-nwf
Version:
NPAW's Plugin
102 lines (101 loc) • 4.56 kB
TypeScript
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 {};