Firebase Functions times out when using puppeteer's browser.newPage()

Viewed 329

I have seen others having relatively minor performance problems with puppeteer running on Firebase Functions. In my case, Firebase times out before I can do anything with puppeteer, even with the memory and timeoutSeconds cranked all the way up.

Code:

import * as functions from "firebase-functions";
import puppeteer from "puppeteer";

const runScreenshot = async () => {
  console.log("Launching puppeteer...");
  console.time("launch");
  const browser = await puppeteer.launch();
  console.timeEnd("launch");

  console.log("Awaiting browser.newPage()...");
  console.time("newPage");
  const page = await browser.newPage();
  console.timeEnd("newPage");

  console.log("Setting viewport...");
  console.time("setViewport");
  page.setViewport({ width: 2000, height: 4000, deviceScaleFactor: 4 });
  console.timeEnd("setViewport");

  console.log("Goto page...");
  console.time("goto");
  await page.goto(
    "https://public.tableau.com/views/NCDHHS_COVID-19_Dashboard_Summary/NCDHHS_DASHBOARD_SUMMARY"
  );
  console.timeEnd("goto");

  console.log("Waiting for selector...");
  console.time("selector");
  await page.waitForSelector("#tab-dashboard-region");
  console.timeEnd("selector");

  // Wait for the spinner to go away
  await new Promise((r) => setTimeout(r, 100));

  console.time("href");
  const href = await page.$("#tab-dashboard-region");
  console.timeEnd("href");

  console.time("screenshot");
  href && (await href.screenshot({ path: "tmp/test.png" }));
  console.timeEnd("screenshot");

  return browser.close();
};

export const screenshot = functions
  .runWith({ memory: "2GB", timeoutSeconds: 540 })
  .pubsub.schedule("44 21 * * *")
  .timeZone("America/New_York")
  .onRun(() => {
    return runScreenshot();
  });

Here's the resulting Firebase Functions log. It takes a few seconds to run puppeteer.launch(), and then browser.newPage() won't finish at all in the 8 minutes it has before the timeout.


9:44:04.012 PM
screenshot
Function execution started
9:44:04.555 PM
screenshot
Launching puppeteer...
9:44:09.427 PM
screenshot
launch: 4870.195ms
9:44:09.427 PM
screenshot
Awaiting browser.newPage()...
9:53:04.014 PM
screenshot
Function execution took 540004 ms, finished with status: 'timeout'

Complete reproducible example: https://github.com/danbockapps/firebase-puppeteer

2 Answers

The function you passed to onRun doesn't return anything. In that case, it will terminate almost immediately, and little to nothing else will happen. The asynchronous work is not guaranteed to complete.

What you should do instead is return promise that resovles only after all the async work completes.Since downloadScreenshot itself returns a promise, you can just return it:

export const screenshot = functions
  .runWith({ memory: '2GB', timeoutSeconds: 540 })
  .pubsub.schedule('27 16 * * *')
  .timeZone('America/New_York')
  .onRun(() => { return downloadScreenshot('tmp/downloaded_image.png') })

See the documentation for more information.

Related