Skip to content

Commit

Permalink
Merge pull request #2991 from remotion-dev/lambda-no-process-exit
Browse files Browse the repository at this point in the history
  • Loading branch information
JonnyBurger authored Oct 9, 2023
2 parents 8d6f767 + d64f672 commit 2cbac9e
Show file tree
Hide file tree
Showing 15 changed files with 195 additions and 97 deletions.
8 changes: 4 additions & 4 deletions packages/example/runlambda.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ cd lambda
npm run buildlambda
cd ..
cd example
pnpm exec remotion lambda functions rmall -f
pnpm exec remotion lambda functions deploy --memory=4000 --timeout=900 --disk=10000
pnpm exec remotion lambda sites create --site-name=testbed-v6 --log=verbose --enable-folder-expiry
# pnpm exec remotion lambda render testbed-v6 OffthreadRemoteVideo --log=verbose --delete-after="1-day"
bunx remotion lambda functions rmall -f
bunx remotion lambda functions deploy --memory=3000 --disk=10000
bunx remotion lambda sites create --site-name=testbed-v6 --log=verbose --enable-folder-expiry
bunx remotion lambda render testbed-v6 OffthreadRemoteVideo --log=verbose --delete-after="1-day"
2 changes: 1 addition & 1 deletion packages/example/src/Root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ export const Index: React.FC = () => {
width={1920}
height={1080}
fps={30}
durationInFrames={30 * 60 * 60}
durationInFrames={30 * 60}
/>
<Composition
id="video-testing-webm"
Expand Down
26 changes: 14 additions & 12 deletions packages/lambda/src/functions/compositions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,22 @@ export const compositionsHandler = async (

const region = getCurrentRegionInFunction();

const [bucketName, browserInstance] = await Promise.all([
lambdaParams.bucketName ??
internalGetOrCreateBucket({
const browserInstancePromise = getBrowserInstance(
lambdaParams.logLevel,
false,
lambdaParams.chromiumOptions,
);
const bucketNamePromise = lambdaParams.bucketName
? Promise.resolve(lambdaParams.bucketName)
: internalGetOrCreateBucket({
region,
enableFolderExpiry: null,
customCredentials: null,
}).then((b) => b.bucketName),
getBrowserInstance(
lambdaParams.logLevel,
false,
lambdaParams.chromiumOptions ?? {},
),
]);
}).then((b) => b.bucketName);

const bucketName = await bucketNamePromise;
const serializedInputPropsWithCustomSchema = await decompressInputProps({
bucketName,
bucketName: await bucketNamePromise,
expectedBucketOwner: options.expectedBucketOwner,
region: getCurrentRegionInFunction(),
serialized: lambdaParams.inputProps,
Expand All @@ -64,7 +64,7 @@ export const compositionsHandler = async (

const compositions = await RenderInternals.internalGetCompositions({
serveUrlOrWebpackUrl: realServeUrl,
puppeteerInstance: browserInstance,
puppeteerInstance: (await browserInstancePromise).instance,
serializedInputPropsWithCustomSchema,
envVariables: lambdaParams.envVariables ?? {},
timeoutInMilliseconds: lambdaParams.timeoutInMilliseconds,
Expand All @@ -78,6 +78,8 @@ export const compositionsHandler = async (
offthreadVideoCacheSizeInBytes: lambdaParams.offthreadVideoCacheSizeInBytes,
});

(await browserInstancePromise).instance.forgetEventLoop();

return Promise.resolve({
compositions,
type: 'success' as const,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ let _browserInstance: Await<ReturnType<typeof openBrowser>> | null;

export const getBrowserInstance: typeof original = async () => {
_browserInstance = await openBrowser('chrome');
return _browserInstance;
return {instance: _browserInstance, configurationString: 'chrome'};
};
102 changes: 74 additions & 28 deletions packages/lambda/src/functions/helpers/get-browser-instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,27 @@ import {RenderInternals} from '@remotion/renderer';
import type {Await} from '../../shared/await';
import {executablePath} from './get-chromium-executable-path';

let _browserInstance: Await<ReturnType<typeof openBrowser>> | null;
type LaunchedBrowser = {
instance: Await<ReturnType<typeof openBrowser>>;
configurationString: string;
};

const makeConfigurationString = (
options: ChromiumOptions,
logLevel: LogLevel,
): string => {
return [
`web-security-${Boolean(options.disableWebSecurity)}`,
`multi-process-${Boolean(options.enableMultiProcessOnLinux)}`,
`ignore-certificate-errors-${Boolean(options.ignoreCertificateErrors)}`,
`log-level-${logLevel}`,
`gl-${options.gl ?? null}`,
`userAgent-${options.userAgent ?? null}`,
`headless-${options.headless ?? false}`,
].join('/');
};

let _browserInstance: LaunchedBrowser | null;

let launching = false;

Expand All @@ -27,44 +47,70 @@ export const getBrowserInstance = async (
logLevel: LogLevel,
indent: boolean,
chromiumOptions: ChromiumOptions,
): ReturnType<typeof openBrowser> => {
): Promise<LaunchedBrowser> => {
const actualChromiumOptions: ChromiumOptions = {
...chromiumOptions,
// Override the `null` value, which might come from CLI with swANGLE
gl: chromiumOptions.gl ?? 'swangle',
};

if (launching) {
RenderInternals.Log.info('Already waiting for browser launch...');
await waitForLaunched();
if (!_browserInstance) {
throw new Error('expected to launch');
}

return _browserInstance;
}

if (_browserInstance) {
return _browserInstance;
}
const configurationString = makeConfigurationString(
actualChromiumOptions,
logLevel,
);

launching = true;
if (!_browserInstance) {
RenderInternals.Log.info(
'Cold Lambda function, launching new Lambda function',
);
launching = true;

const execPath = executablePath();
const execPath = executablePath();

const actualChromiumOptions: ChromiumOptions = {
...chromiumOptions,
// Override the `null` value, which might come from CLI with swANGLE
gl: chromiumOptions.gl ?? 'swangle',
};
const instance = await RenderInternals.internalOpenBrowser({
browser: 'chrome',
browserExecutable: execPath,
chromiumOptions: actualChromiumOptions,
forceDeviceScaleFactor: undefined,
indent: false,
viewport: null,
logLevel,
});
instance.on('disconnected', () => {
console.log('Browser disconnected / crashed');
_browserInstance?.instance
?.close(true, logLevel, indent)
.catch(() => undefined);
_browserInstance = null;
});
_browserInstance = {
instance,
configurationString,
};

_browserInstance = await RenderInternals.internalOpenBrowser({
browser: 'chrome',
browserExecutable: execPath,
chromiumOptions: actualChromiumOptions,
forceDeviceScaleFactor: undefined,
indent: false,
viewport: null,
logLevel,
});
_browserInstance.on('disconnected', () => {
console.log('Browser disconnected / crashed');
_browserInstance?.close(true, logLevel, indent).catch(() => undefined);
launching = false;
return _browserInstance;
}

if (_browserInstance.configurationString !== configurationString) {
RenderInternals.Log.info(
'Warm Lambda function, but Browser configuration changed. Killing old browser instance.',
);
_browserInstance.instance.rememberEventLoop();
await _browserInstance.instance.close(true, logLevel, indent);
_browserInstance = null;
});
launching = false;
return getBrowserInstance(logLevel, indent, chromiumOptions);
}

RenderInternals.Log.info('Warm Lambda function, reusing browser instance');
_browserInstance.instance.rememberEventLoop();
return _browserInstance;
};
6 changes: 0 additions & 6 deletions packages/lambda/src/functions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,12 +209,6 @@ const routine = async (

responseStream.write(JSON.stringify(res));
responseStream.end();
} finally {
responseStream.on('close', () => {
if (!process.env.VITEST) {
process.exit(0);
}
});
}
};

Expand Down
5 changes: 3 additions & 2 deletions packages/lambda/src/functions/launch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ const innerLaunchHandler = async ({

const startedDate = Date.now();

const browserInstance = await getBrowserInstance(
const browserInstance = getBrowserInstance(
params.logLevel,
false,
params.chromiumOptions,
Expand All @@ -133,7 +133,7 @@ const innerLaunchHandler = async ({
const comp = await validateComposition({
serveUrl: params.serveUrl,
composition: params.composition,
browserInstance,
browserInstance: (await browserInstance).instance,
serializedInputPropsWithCustomSchema,
envVariables: params.envVariables ?? {},
timeoutInMilliseconds: params.timeoutInMilliseconds,
Expand Down Expand Up @@ -355,6 +355,7 @@ const innerLaunchHandler = async ({
);

reqSend.end();
(await browserInstance).instance.forgetEventLoop();

const fps = comp.fps / params.everyNthFrame;
const postRenderData = await mergeChunksAndFinishRender({
Expand Down
6 changes: 4 additions & 2 deletions packages/lambda/src/functions/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ const renderHandler = async (
const browserInstance = await getBrowserInstance(
params.logLevel,
false,
params.chromiumOptions ?? {},
params.chromiumOptions,
);

const outputPath = RenderInternals.tmpDir('remotion-render-');
Expand Down Expand Up @@ -172,7 +172,7 @@ const renderHandler = async (
renderId: params.renderId,
}).catch((err) => reject(err));
},
puppeteerInstance: browserInstance,
puppeteerInstance: browserInstance.instance,
serveUrl: params.serveUrl,
jpegQuality: params.jpegQuality ?? RenderInternals.DEFAULT_JPEG_QUALITY,
envVariables: params.envVariables ?? {},
Expand Down Expand Up @@ -269,6 +269,8 @@ const renderHandler = async (
customCredentials: null,
}),
]);
browserInstance.instance.forgetEventLoop();
RenderInternals.Log.verbose('Done!');
return {};
};

Expand Down
31 changes: 17 additions & 14 deletions packages/lambda/src/functions/still.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,25 +75,25 @@ const innerStillHandler = async ({

const start = Date.now();

const [bucketName, browserInstance] = await Promise.all([
const browserInstancePromise = getBrowserInstance(
lambdaParams.logLevel,
false,
lambdaParams.chromiumOptions,
);
const bucketNamePromise =
lambdaParams.bucketName ??
internalGetOrCreateBucket({
region: getCurrentRegionInFunction(),
enableFolderExpiry: null,
customCredentials: null,
}).then((b) => b.bucketName),
getBrowserInstance(
lambdaParams.logLevel,
false,
lambdaParams.chromiumOptions ?? {},
),
]);
internalGetOrCreateBucket({
region: getCurrentRegionInFunction(),
enableFolderExpiry: null,
customCredentials: null,
}).then((b) => b.bucketName);

const outputDir = RenderInternals.tmpDir('remotion-render-');

const outputPath = path.join(outputDir, 'output');

const region = getCurrentRegionInFunction();
const bucketName = await bucketNamePromise;
const serializedInputPropsWithCustomSchema = await decompressInputProps({
bucketName,
expectedBucketOwner,
Expand All @@ -118,9 +118,10 @@ const innerStillHandler = async ({
offthreadVideoCacheSizeInBytes: lambdaParams.offthreadVideoCacheSizeInBytes,
});

const browserInstance = await browserInstancePromise;
const composition = await validateComposition({
serveUrl,
browserInstance,
browserInstance: browserInstance.instance,
composition: lambdaParams.composition,
serializedInputPropsWithCustomSchema,
envVariables: lambdaParams.envVariables ?? {},
Expand Down Expand Up @@ -184,7 +185,7 @@ const innerStillHandler = async ({
imageFormat: lambdaParams.imageFormat as StillImageFormat,
serializedInputPropsWithCustomSchema,
overwrite: false,
puppeteerInstance: browserInstance,
puppeteerInstance: browserInstance.instance,
jpegQuality:
lambdaParams.jpegQuality ?? RenderInternals.DEFAULT_JPEG_QUALITY,
chromiumOptions: lambdaParams.chromiumOptions,
Expand Down Expand Up @@ -245,6 +246,8 @@ const innerStillHandler = async ({
diskSizeInMb: MAX_EPHEMERAL_STORAGE_IN_MB,
});

browserInstance.instance.forgetEventLoop();

return {
type: 'success' as const,
output: getOutputUrlFromMetadata(
Expand Down
Loading

2 comments on commit 2cbac9e

@vercel
Copy link

@vercel vercel bot commented on 2cbac9e Oct 9, 2023

Choose a reason for hiding this comment

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

@vercel
Copy link

@vercel vercel bot commented on 2cbac9e Oct 9, 2023

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

bugs – ./packages/bugs

bugs-git-main-remotion.vercel.app
bugs.remotion.dev
bugs-five.vercel.app
bugs-remotion.vercel.app

Please sign in to comment.