hardkoded / puppeteer-sharp

Headless Chrome .NET API
https://www.puppeteersharp.com
MIT License
3.44k stars 452 forks source link

Pre-download Chromium with code during deployment to prevent slow cold-starts #2212

Open maikelvanhaaren opened 1 year ago

maikelvanhaaren commented 1 year ago

First of all, thank you for the effort you're putting into this package!

We recently made a transition from using NodeJS to .NET (Linux) for our consumption-based Azure Function, for various reasons. In our previous setup with NodeJS, we utilized puppeteer, and now we're using puppeteer-sharp. I've noticed a difference in the way these two packages handle the downloading of Chromium.

In the NodeJS setup, Chromium was downloaded during the npm install process, whereas with puppeteer-sharp, it is downloaded at runtime. This disparity, combined with the nature of serverless Azure Functions, occasionally leads to errors, especially during a cold start, when Chromium hasn't been downloaded yet.

Is there a way we can address this issue? One potential solution could be bundling Chromium together with the Azure Functions code so that it doesn't need to be downloaded at runtime.

kblok commented 1 year ago

I wrote this post a few years ago. I hope it still works. But the solution would be using containers in azure functions.

maikelvanhaaren commented 1 year ago

Check, thanks for the link. That also seems like a potential solution for this by including chromium within the container.

I was experimenting today by upgrading the Azure Function from Dynamic to an enterprise plan so I can have minimal one instance warm at all times in the hope to reduce the cold-start errors. On the dynamic SKU, it's all working but on the enterprise plan with the same code, puppeteer-sharp fails at launching the browser (error while loading shared libraries: libgobject-2.0.so.0: cannot open shared object file: No such file or directory).

What do you think about an option where you have the possibility to bring-your-own-chromium (BYOC)? Something like:

await browserFetcher.SetupPredownloadedChromium(package: "../chromium-linux.zip").ConfigureAwait(false);

var chromiumExecutablePath = browserFetcher.RevisionInfo(BrowserFetcher.DefaultChromiumRevision).ExecutablePath;
var launchOptions = new LaunchOptions
{
    Headless = true,
    Args = new[] { "--no-sandbox --font-render-hinting=none" },
    ExecutablePath = chromiumExecutablePath
};

await using var browser = await Puppeteer.LaunchAsync(launchOptions).ConfigureAwait(false);
kblok commented 1 year ago

I think I tried that in the past, back in 2019. Maybe give it chance to see if that works?

maikelvanhaaren commented 1 year ago

Is there a branch open with your attempt? I'm happy to give it a try.

kblok commented 1 year ago

No. I discarded that attempt.

maikelvanhaaren commented 1 year ago

OK, check. I'm gonna give it a try and see what I can come up with so we have some more concrete things to discuss 😄