Sparticuz / chromium

Chromium (x86-64) for Serverless Platforms
MIT License
1.01k stars 69 forks source link

[BUG] TimeoutError: Timed out after waiting 30000ms #291

Open giri0996 opened 2 months ago

giri0996 commented 2 months ago

Environment

Expected Behavior

Generate the pdf and upload to an s3 bucket

Current Behavior

The same code base was working before in nodejs 14.x lambda runtime having chrome-aws-lambda 6.0.0 puppeteer/puppeteer-core 6.0.0.

In Node18.x/20.x lambda runtimegetting a TimeoutError during pdf generation.

Failed to print PDF on time.

TimeoutError: Timed out after waiting 30000ms
    at /var/task/node_modules/puppeteer-core/lib/cjs/puppeteer/common/util.js:289:19
    at /var/task/node_modules/puppeteer-core/lib/cjs/third_party/rxjs/rxjs.js:3986:35
    at OperatorSubscriber2._this._next (/var/task/node_modules/puppeteer-core/lib/cjs/third_party/rxjs/rxjs.js:1055:13)
    at Subscriber2.next (/var/task/node_modules/puppeteer-core/lib/cjs/third_party/rxjs/rxjs.js:678:16)
    at AsyncAction2.<anonymous> (/var/task/node_modules/puppeteer-core/lib/cjs/third_party/rxjs/rxjs.js:4863:24)
    at AsyncAction2._execute (/var/task/node_modules/puppeteer-core/lib/cjs/third_party/rxjs/rxjs.js:1974:16)
    at AsyncAction2.execute (/var/task/node_modules/puppeteer-core/lib/cjs/third_party/rxjs/rxjs.js:1963:26)
    at AsyncScheduler2.flush (/var/task/node_modules/puppeteer-core/lib/cjs/third_party/rxjs/rxjs.js:2235:30)
    at listOnTimeout (node:internal/timers:569:17)
    at process.processTimers (node:internal/timers:512:7) {
  [cause]: undefined
}

Steps to Reproduce

const url = require('url');

const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');

const puppeteer = require('puppeteer-core');

const chromium = require('@sparticuz/chromium');

const timeoutPromise = async function(promise, ms = 60000) {
    let id;

    // Create a promise that rejects in <ms> milliseconds
    let timeout = new Promise((resolve, reject) => {
        id = setTimeout(() => {
            reject('Timed out in ' + ms + 'ms.')
        }, ms)
    });

    return Promise.race([promise, timeout]).then((result) => {
        clearTimeout(id);

        return result;
    });
};

exports.generatePdfHandler = async function(event, context) {
    /*
    This is the entry point for the Lambda function.

    */
    const s3Client = new S3Client();

    const browser = await puppeteer.launch({
        executablePath: await chromium.executablePath(),
        headless: chromium.headless,
        args: [
            '--no-sandbox',
            '--single-process',
            '--homedir=/tmp',
            '--data-path=/tmp/data-path',
            '--disk-cache-dir=/tmp/cache-dir'
        ]
    });
    const page = await browser.newPage();
    // Set viewport size so that CSS won't break in print mode. This was tested
    // to be working, anything below or above this standard resolution did not
    // work.
    await page.setViewport({width: 1024, height: 768});
    // Disable navigation timeout so that we allow more than 30 seconds to load
    // a report.
    await page.setDefaultNavigationTimeout(0);

    console.log(`Fetching report from ${event.reportUrl}`);
    try {
        await page.goto(event.reportUrl, {waitUntil: 'networkidle0'});
    } catch (err) {
        console.log('Failed to load report.');
        console.log(err);
        await browser.close();
        return;
    }
    console.log('Report page fully loaded.');

    // Verify that an actual report was rendered, making sure we have charts
    // in the DOM.
    let hasReports = false;
    try {
        hasReports = (await page.$eval('ca-reports', el => el.childElementCount)) > 0;
    } catch (err) {
        console.log('Could not find parent ca-reports element');
    }
    if (!hasReports) {
        console.log('Could not find any charts in the rendered report.');
        let screenshot = await page.screenshot({fullPage: true});
        let screenshotKeyName = event.pdfKeyName.replace('.pdf', '-error.png');
        const params = {
            Bucket: event.bucketName,
            ACL: 'private',
            Key: screenshotKeyName,
            Body: screenshot,
            ContentType: 'image/png',
            ServerSideEncryption: 'aws:kms',
            SSEKMSKeyId: process.env.PDF_KMS_KEY_ID
        };
        const command = new PutObjectCommand(params);
        await s3Client.send(command);
        console.log(`Saving Error screenshot to s3://${event.bucketName}/${screenshotKeyName}`);
        await browser.close();
        return;
    }

    const printToPdf = async function () {
        return page.pdf({printBackground: true, landscape: false});
    };

    await timeoutPromise(printToPdf(), (context.getRemainingTimeInMillis() || 2000) - 2000).then(async (pdf) => {
        try {
            const params = {
                Bucket: event.bucketName,
                ACL: 'private',
                Key: event.pdfKeyName,
                Body: pdf,
                ContentType: 'application/pdf',
                ContentDisposition: 'attachment',
                ServerSideEncryption: 'aws:kms',
                SSEKMSKeyId: process.env.PDF_KMS_KEY_ID
            };
            const command = new PutObjectCommand(params);
            await s3Client.send(command);
            console.log(`PDF successfully saved as s3://${event.bucketName}/${event.pdfKeyName}`);

            await browser.close();
            return;
        } catch (err) {
            console.log('Failed to upload PDF.');
            console.log(err);
            await browser.close();
            return;
        }
        await browser.close();
        return;
    }).catch(async (err) => {
        console.log('Failed to print PDF on time.');
        console.log(err);
        await browser.close();
        return;
    });
};

Lambda Memory - 4096 MB, Ephemeral storage - 512 MB, Timeout - 6 MIN

Can some one help me to figure out what is wrong here?

Madebyspeedster commented 1 month ago

Hi, does it happens on specific pages?

giri0996 commented 1 month ago

I have mostly seen this behaviour on pdf's with large file sizes around 50 MB, page count approx 750

Madebyspeedster commented 1 month ago

oh, it's kinda heavy, I've asked, I'm getting same error, but in my case it's pretrty simple page. Page has style tag with CSS inside, weird part is - when I use

 @media print {
        .non-pdf-link {
          display: none !important;
        }
        .pdf-link {
          display: flex !important;
        }
        .main-content {
          zoom: 145%;
        }
      }

page.pdf(...options) throw Error waiting timeout, even if I increase it still occurs. and in case I've removed media print styles, it works)

Madebyspeedster commented 1 month ago

I've updated a lib and issue has gone =)

giri0996 commented 1 month ago

Could you tell me what was updated here for the issue to be gone?

Madebyspeedster commented 1 month ago

Hi, I have just upgraded the library to the latest version, following the guidelines outlined in the documentation.

image

giri0996 commented 2 weeks ago

I tried with the latest library puppeteer-core == 23.6.1 and chromium Version: 130.0.0, but still receiving the same timeout error

`TimeoutError: Timed out after waiting 30000ms at /var/task/node_modules/puppeteer-core/lib/cjs/puppeteer/common/util.js:258:19 at /var/task/node_modules/puppeteer-core/lib/cjs/third_party/rxjs/rxjs.js:3986:35 at OperatorSubscriber2._this._next (/var/task/node_modules/puppeteer-core/lib/cjs/third_party/rxjs/rxjs.js:1055:13) at Subscriber2.next (/var/task/node_modules/puppeteer-core/lib/cjs/third_party/rxjs/rxjs.js:678:16) at AsyncAction2. (/var/task/node_modules/puppeteer-core/lib/cjs/third_party/rxjs/rxjs.js:4863:24) at AsyncAction2._execute (/var/task/node_modules/puppeteer-core/lib/cjs/third_party/rxjs/rxjs.js:1974:16) at AsyncAction2.execute (/var/task/node_modules/puppeteer-core/lib/cjs/third_party/rxjs/rxjs.js:1963:26) at AsyncScheduler2.flush (/var/task/node_modules/puppeteer-core/lib/cjs/third_party/rxjs/rxjs.js:2235:30) at listOnTimeout (node:internal/timers:581:17) at process.processTimers (node:internal/timers:519:7) {

} `

ajmal-ahmed commented 4 days ago

I've faced this issue and got this working with the following versions

"@sparticuz/chromium": "^130.0.0",
"puppeteer-chromium-resolver": "^23.0.0",
"puppeteer-core": "^23.7.1"

Here is my code


"use strict";
const puppeteer = require("puppeteer-core");
const PCR = require("puppeteer-chromium-resolver");

exports.generatePdfFromUrl = async (urlToPrint) => {
    try {
        const stats = await PCR();
        const browser = await puppeteer.launch({
            headless: true,
            args: ['--no-sandbox'],
            executablePath: stats.executablePath,
            defaultViewport: { width: 1920, height: 945, deviceScaleFactor: 4 },
        });

        const options = {
            format: 'A4',
            headerTemplate: '',
            footerTemplate: '',
            displayHeaderFooter: false,
            margin: {
                top: '0px',
                bottom: '0px',
            },
            printBackground: true,
        };

        const page = await browser.newPage();
        await page.setDefaultNavigationTimeout(60000); // Set timeout to 60 seconds

        await page.goto(urlToPrint, {
            waitUntil: 'networkidle0',
        });

        // Wait for 5 seconds to ensure all content is loaded
        await new Promise(resolve => setTimeout(resolve, 5000));
        const buffer = await page.pdf(options);

        await page.close();
        await browser.close();

        return buffer;
    } catch (error) {
        console.error(error);
        throw error;
    }
};