Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pullRegion to streams to make the ingest node sticky #2127

Merged
merged 7 commits into from
Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions packages/api/src/controllers/stream.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
import serverPromise, { TestServer } from "../test-server";
import { semaphore, sleep } from "../util";
import { generateUniquePlaybackId } from "./generate-keys";
import { extractRegionFrom } from "./stream";

const uuidRegex = /[0-9a-f]+(-[0-9a-f]+){4}/;

Expand Down Expand Up @@ -712,6 +713,28 @@ describe("controllers/stream", () => {
const document = await db.stream.get(stream.id);
expect(db.stream.addDefaultFields(document)).toEqual(updatedStream);
});
it("should extract region from redirected playback url", async () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be good to have some flv urls in here to verify nothing breaks:
e.g /flv/<playback-id>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As pointed out below, we don't use it for /flv

expect(
extractRegionFrom(
"https://sto-prod-catalyst-0.lp-playback.studio:443/hls/video+not-used-playback/index.m3u8"
)
).toBe("sto");
expect(
extractRegionFrom(
"https://mos2-prod-catalyst-0.lp-playback.studio:443/hls/video+not-used-playback/index.m3u8"
)
).toBe("mos2");
expect(
extractRegionFrom(
"https://fra-staging-staging-catalyst-0.livepeer.monster:443/hls/video+not-used-playback/index.m3u8"
)
).toBe("fra");
expect(
extractRegionFrom(
"https://fra-staging-staging-catalyst-0.livepeer.monster:443/hls/video+other-playback/index.m3u8"
)
).toBe(null);
});
});

it("should create a stream, delete it, and error when attempting additional delete or replace", async () => {
Expand Down
42 changes: 41 additions & 1 deletion packages/api/src/controllers/stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import logger from "../logger";
import { authorizer } from "../middleware";
import { validatePost } from "../middleware";
import { geolocateMiddleware } from "../middleware";
import { fetchWithTimeout } from "../util";
import { CliArgs } from "../parse-cli";
import {
DetectionWebhookPayload,
Expand Down Expand Up @@ -235,6 +236,39 @@ async function triggerManyIdleStreamsWebhook(ids: string[], queue: Queue) {
);
}

async function resolvePullRegion(
stream: NewStreamPayload,
ingest: string
): Promise<string> {
if (process.env.NODE_ENV === "test") {
return null;
}
const url = new URL(
pathJoin(ingest, `hls`, "not-used-playback", `index.m3u8`)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't we need to handle flv as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this call is not for the "any redirect", this is just to discover which should be the ingest node.

So, kind-of hack the system and use catalyst-api redirect balancing (effectively using MistUtilLoad) to divide on the ingest node.

);
const { lat, lon } = stream.pull?.location ?? {};
if (lat && lon) {
url.searchParams.set("lat", lat.toString());
url.searchParams.set("lon", lon.toString());
}
const playbackUrl = url.toString();
const response = await fetchWithTimeout(playbackUrl, { redirect: "manual" });
leszko marked this conversation as resolved.
Show resolved Hide resolved
if (response.status < 300 || response.status >= 400) {
// not a redirect response, so we can't determine the region
return null;
}
const redirectUrl = response.headers.get("location");
return extractRegionFrom(redirectUrl);
}

// Extracts region from redirected node URL, e.g. "sto" from "https://sto-prod-catalyst-0.lp-playback.studio:443/hls/video+foo/index.m3u8"
export function extractRegionFrom(playbackUrl: string): string {
const regionRegex =
/https?:\/\/(\w+)-.+-catalyst.+not-used-playback\/index.m3u8/;
const matches = playbackUrl.match(regionRegex);
return matches ? matches[1] : null;
}

export function getHLSPlaybackUrl(ingest: string, stream: DBStream) {
return pathJoin(ingest, `hls`, stream.playbackId, `index.m3u8`);
}
Expand Down Expand Up @@ -1001,6 +1035,8 @@ app.put(
const { key = "pull.source", waitActive } = toStringValues(req.query);
const rawPayload = req.body as NewStreamPayload;

const ingest = await getIngestBase(req);

if (!rawPayload.pull) {
return res.status(400).json({
errors: [`stream pull configuration is required`],
Expand Down Expand Up @@ -1046,9 +1082,13 @@ app.put(
}
const streamExisted = streams.length === 1;

const pullRegion = await resolvePullRegion(rawPayload, ingest);

let stream: DBStream;
if (!streamExisted) {
stream = await handleCreateStream(req);
stream.pullRegion = pullRegion;
await db.stream.replace(stream);
} else {
const oldStream = streams[0];
const sleepFor = terminateDelay(oldStream);
Expand All @@ -1063,6 +1103,7 @@ app.put(
...oldStream,
...EMPTY_NEW_STREAM_PAYLOAD, // clear all fields that should be set from the payload
suspended: false,
pullRegion,
...payload,
};
await db.stream.replace(stream);
Expand All @@ -1073,7 +1114,6 @@ app.put(
}

if (!stream.isActive || streamExisted) {
const ingest = await getIngestBase(req);
await triggerCatalystPullStart(stream, getHLSPlaybackUrl(ingest, stream));
}

Expand Down
2 changes: 2 additions & 0 deletions packages/api/src/schema/db-schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,8 @@ components:
example: 1587667174725
pullLockedBy:
type: string
pullRegion:
type: string
playbackId:
unique: true
mistHost:
Expand Down
1 change: 1 addition & 0 deletions packages/api/src/store/stream-table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,7 @@ const adminOnlyFields = [
"createdByTokenId",
"pullLockedAt",
"pullLockedBy",
"pullRegion",
];

const privateFields = [
Expand Down
Loading