Skip to content

Commit

Permalink
Remove ContentSteering logic to just keep better Cdn priorization for…
Browse files Browse the repository at this point in the history
… now
  • Loading branch information
peaBerberian committed Oct 13, 2022
1 parent ff1055f commit 167c5ec
Show file tree
Hide file tree
Showing 38 changed files with 34 additions and 1,225 deletions.
90 changes: 0 additions & 90 deletions src/Content_Steering.md

This file was deleted.

226 changes: 9 additions & 217 deletions src/core/fetchers/cdn_prioritizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,39 +15,16 @@
*/

import config from "../../config";
import { formatError } from "../../errors";
import log from "../../log";
import Manifest from "../../manifest";
import {
ICdnMetadata,
IContentSteeringMetadata,
} from "../../parsers/manifest";
import { ISteeringManifest } from "../../parsers/SteeringManifest";
import { ICdnMetadata } from "../../parsers/manifest";
import { IPlayerError } from "../../public_types";
import { ITransportPipelines } from "../../transports";
import arrayFindIndex from "../../utils/array_find_index";
import arrayIncludes from "../../utils/array_includes";
import EventEmitter from "../../utils/event_emitter";
import createSharedReference, {
ISharedReference,
} from "../../utils/reference";
import SyncOrAsync, {
ISyncOrAsyncValue,
} from "../../utils/sync_or_async";
import TaskCanceller, {
CancellationError,
CancellationSignal,
} from "../../utils/task_canceller";
import SteeringManifestFetcher from "./steering_manifest";
import { CancellationSignal } from "../../utils/task_canceller";

/**
* Class signaling the priority between multiple CDN available for any given
* resource.
*
* It might rely behind the hood on a fetched document giving priorities such as
* a Content Steering Manifest and also on issues that appeared with some given
* CDN in the [close] past.
*
* This class might perform requests and schedule timeouts by itself to keep its
* internal list of CDN priority up-to-date.
* When it is not needed anymore, you should call the `dispose` method to clear
Expand All @@ -56,16 +33,6 @@ import SteeringManifestFetcher from "./steering_manifest";
* @class CdnPrioritizer
*/
export default class CdnPrioritizer extends EventEmitter<ICdnPrioritizerEvents> {
/**
* Metadata parsed from the last Content Steering Manifest loaded.
*
* `null` either if there's no such Manifest or if it is currently being
* loaded for the first time.
*/
private _lastSteeringManifest : ISteeringManifest | null;

private _defaultCdnId : string | undefined;

/**
* Structure keeping a list of CDN currently downgraded.
* Downgraded CDN immediately have a lower priority than any non-downgraded
Expand All @@ -90,90 +57,12 @@ export default class CdnPrioritizer extends EventEmitter<ICdnPrioritizerEvents>
};

/**
* TaskCanceller allowing to abort the process of loading and refreshing the
* Content Steering Manifest.
* Set to `null` when no such process is pending.
*/
private _steeringManifestUpdateCanceller : TaskCanceller | null;

private _readyState : ISharedReference<"not-ready" | "ready" | "disposed">;

/**
* @param {Object} manifest
* @param {Object} transport
* @param {Object} destroySignal
*/
constructor(
manifest : Manifest,
transport : ITransportPipelines,
destroySignal : CancellationSignal
) {
constructor(destroySignal : CancellationSignal) {
super();
this._lastSteeringManifest = null;
this._downgradedCdnList = { metadata: [], timeouts: [] };
this._steeringManifestUpdateCanceller = null;
this._defaultCdnId = manifest.contentSteering?.defaultId;

const steeringManifestFetcher = transport.steeringManifest === null ?
null :
new SteeringManifestFetcher(transport.steeringManifest,
{ maxRetryOffline: undefined,
maxRetryRegular: undefined });

let currentContentSteering = manifest.contentSteering;

manifest.addEventListener("manifestUpdate", () => {
const prevContentSteering = currentContentSteering;
currentContentSteering = manifest.contentSteering;
if (prevContentSteering === null) {
if (currentContentSteering !== null) {
if (steeringManifestFetcher === null) {
log.warn("CP: Steering manifest declared but no way to fetch it");
} else {
log.info("CP: A Steering Manifest is declared in a new Manifest");
this._autoRefreshSteeringManifest(steeringManifestFetcher,
currentContentSteering);
}
}
} else if (currentContentSteering === null) {
log.info("CP: A Steering Manifest is removed in a new Manifest");
this._steeringManifestUpdateCanceller?.cancel();
this._steeringManifestUpdateCanceller = null;
} else if (prevContentSteering.url !== currentContentSteering.url ||
prevContentSteering.proxyUrl !== currentContentSteering.proxyUrl)
{
log.info("CP: A Steering Manifest's information changed in a new Manifest");
this._steeringManifestUpdateCanceller?.cancel();
this._steeringManifestUpdateCanceller = null;
if (steeringManifestFetcher === null) {
log.warn("CP: Steering manifest changed but no way to fetch it");
} else {
this._autoRefreshSteeringManifest(steeringManifestFetcher,
currentContentSteering);
}
}
}, destroySignal);

if (manifest.contentSteering !== null) {
if (steeringManifestFetcher === null) {
log.warn("CP: Steering Manifest initially present but no way to fetch it.");
this._readyState = createSharedReference("ready");
} else {
const readyState = manifest.contentSteering.queryBeforeStart ? "not-ready" :
"ready";
this._readyState = createSharedReference(readyState);
this._autoRefreshSteeringManifest(steeringManifestFetcher,
manifest.contentSteering);
}
} else {
this._readyState = createSharedReference("ready");
}
destroySignal.register(() => {
this._readyState.setValue("disposed");
this._readyState.finish();
this._steeringManifestUpdateCanceller?.cancel();
this._steeringManifestUpdateCanceller = null;
this._lastSteeringManifest = null;
for (const timeout of this._downgradedCdnList.timeouts) {
clearTimeout(timeout);
}
Expand All @@ -195,38 +84,20 @@ export default class CdnPrioritizer extends EventEmitter<ICdnPrioritizerEvents>
* @param {Array.<string>} everyCdnForResource - Array of ALL available CDN
* able to reach the wanted resource - even those which might not be used in
* the end.
* @returns {Object} - Array of CDN that can be tried to reach the
* @returns {Array.<Object>} - Array of CDN that can be tried to reach the
* resource, sorted by order of CDN preference, according to the
* `CdnPrioritizer`'s own list of priorities.
*
* This value is wrapped in a `ISyncOrAsyncValue` as in relatively rare
* scenarios, the order can only be known once the steering Manifest has been
* fetched.
*/
public getCdnPreferenceForResource(
everyCdnForResource : ICdnMetadata[]
) : ISyncOrAsyncValue<ICdnMetadata[]> {
) : ICdnMetadata[] {
if (everyCdnForResource.length <= 1) {
// The huge majority of contents have only one CDN available.
// Here, prioritizing make no sense.
return SyncOrAsync.createSync(everyCdnForResource);
return everyCdnForResource;
}

if (this._readyState.getValue() === "not-ready") {
const val = new Promise<ICdnMetadata[]>((res, rej) => {
this._readyState.onUpdate((readyState) => {
if (readyState === "ready") {
res(this._innerGetCdnPreferenceForResource(everyCdnForResource));
} else if (readyState === "disposed") {
rej(new CancellationError());
}
});
});
return SyncOrAsync.createAsync(val);
}
return SyncOrAsync.createSync(
this._innerGetCdnPreferenceForResource(everyCdnForResource)
);
return this._innerGetCdnPreferenceForResource(everyCdnForResource);
}

/**
Expand All @@ -245,8 +116,7 @@ export default class CdnPrioritizer extends EventEmitter<ICdnPrioritizerEvents>
}

const { DEFAULT_CDN_DOWNGRADE_TIME } = config.getCurrent();
const downgradeTime = this._lastSteeringManifest?.lifetime ??
DEFAULT_CDN_DOWNGRADE_TIME;
const downgradeTime = DEFAULT_CDN_DOWNGRADE_TIME;
this._downgradedCdnList.metadata.push(metadata);
const timeout = window.setTimeout(() => {
const newIndex = indexOfMetadata(this._downgradedCdnList.metadata, metadata);
Expand Down Expand Up @@ -280,33 +150,7 @@ export default class CdnPrioritizer extends EventEmitter<ICdnPrioritizerEvents>
private _innerGetCdnPreferenceForResource(
everyCdnForResource : ICdnMetadata[]
) : ICdnMetadata[] {
let cdnBase;
if (this._lastSteeringManifest !== null) {
const priorities = this._lastSteeringManifest.priorities;
const inSteeringManifest = everyCdnForResource.filter(available =>
available.id !== undefined && arrayIncludes(priorities, available.id));
if (inSteeringManifest.length > 0) {
cdnBase = inSteeringManifest;
}
}

// (If using the SteeringManifest gave nothing, or if it just didn't exist.) */
if (cdnBase === undefined) {
// (If a default CDN was indicated, try to use it) */
if (this._defaultCdnId !== undefined) {
const indexOf = arrayFindIndex(everyCdnForResource, (x) =>
x.id !== undefined && x.id === this._defaultCdnId);
if (indexOf >= 0) {
const elem = everyCdnForResource.splice(indexOf, 1)[0];
everyCdnForResource.unshift(elem);
}
}

if (cdnBase === undefined) {
cdnBase = everyCdnForResource;
}
}
const [allowedInOrder, downgradedInOrder] = cdnBase
const [allowedInOrder, downgradedInOrder] = everyCdnForResource
.reduce((acc : [ICdnMetadata[], ICdnMetadata[]], elt : ICdnMetadata) => {
if (this._downgradedCdnList.metadata.some(c => c.id === elt.id &&
c.baseUrl === elt.baseUrl))
Expand All @@ -320,58 +164,6 @@ export default class CdnPrioritizer extends EventEmitter<ICdnPrioritizerEvents>
return allowedInOrder.concat(downgradedInOrder);
}

private _autoRefreshSteeringManifest(
steeringManifestFetcher : SteeringManifestFetcher,
contentSteering : IContentSteeringMetadata
) {
if (this._steeringManifestUpdateCanceller === null) {
const steeringManifestUpdateCanceller = new TaskCanceller();
this._steeringManifestUpdateCanceller = steeringManifestUpdateCanceller;
}
const canceller : TaskCanceller = this._steeringManifestUpdateCanceller;
steeringManifestFetcher.fetch(contentSteering.url,
(err : IPlayerError) => this.trigger("warnings", [err]),
canceller.signal)
.then((parse) => {
const parsed = parse((errs) => this.trigger("warnings", errs));
const prevSteeringManifest = this._lastSteeringManifest;
this._lastSteeringManifest = parsed;
if (parsed.lifetime > 0) {
const timeout = window.setTimeout(() => {
canceller.signal.deregister(onTimeoutEnd);
this._autoRefreshSteeringManifest(steeringManifestFetcher, contentSteering);
}, parsed.lifetime * 1000);
const onTimeoutEnd = () => {
clearTimeout(timeout);
};
canceller.signal.register(onTimeoutEnd);
}
if (this._readyState.getValue() === "not-ready") {
this._readyState.setValue("ready");
}
if (canceller.isUsed) {
return;
}
if (prevSteeringManifest === null ||
prevSteeringManifest.priorities.length !== parsed.priorities.length ||
prevSteeringManifest.priorities
.some((val, idx) => val !== parsed.priorities[idx]))
{
this.trigger("priorityChange", null);
}
})
.catch((err) => {
if (err instanceof CancellationError) {
return;
}
const formattedError = formatError(err, {
defaultCode: "NONE",
defaultReason: "Unknown error when fetching and parsing the steering Manifest",
});
this.trigger("warnings", [formattedError]);
});
}

/**
* @param {number} index
*/
Expand Down
Loading

0 comments on commit 167c5ec

Please sign in to comment.