1

I have developed a function that take an one page HTML, convert it to PDF using htmp-pdf package and upload to firebase storage. When I run this function from my pc it takes less than 5 seconds, but when I run it from firebase function it takes up to 6 minutes. This is the code:

  pdf.create(estadoCuenta, { orientation: 'portrait', type: 'pdf', timeout: '360000' }).toStream(function(err, stream) {
      console.log('Nombre del archivo generado: ' + file.name);
      stream.pipe(file.createWriteStream({
      metadata: {
      contentType: 'application/pdf',
      metadata: {
          origin: 'created by ...'
          }
      },
      public: true,
      validation: "md5"
      }))
      .on('error', function(err) {
          console.log('error en la carga de archivo: ' + err);
      })
      .on('finish', function() {
              // The file upload is complete.
              let estadoCuentaPDF = file.name.replace('/','%2F');
              console.log('estadoCuentaPDF:' + estadoCuentaPDF);
      });
    });

Variable "estadoCuenta" contains the html to convert.

This is my package.json:

{
  "name": "dialogflowFirebaseFulfillment",
  "description": "This is the default fulfillment for a Dialogflow agents using Cloud Functions for Firebase",
  "version": "0.0.1",
  "author": "xxx",
  "repository": "https://yyy@bitbucket.org/zzz/www.git",
  "license": "xxx",
  "private": true,
  "scripts": {
    "start": "firebase serve --only functions:dialogflowFirebaseFulfillment",
    "deploy": "firebase deploy --only functions:dialogflowFirebaseFulfillment"
  },
  "engines": {
    "node": "10"
  },
  "dependencies": {
    "-": "0.0.1",
    "@google-cloud/firestore": "^3.7.5",
    "actions-on-google": "^2.10.0",
    "cors": "^2.8.5",
    "dialogflow": "^1.2.0",
    "dialogflow-fulfillment": "^0.6.1",
    "dotenv": "^8.2.0",
    "envfile": "^6.9.0",
    "express": "^4.17.1",
    "firebase-admin": "^8.0.0",
    "firebase-functions": "^3.6.1",
    "firebase-tools": "^7.4.0",
    "html-pdf": "^2.2.0",
    "isomorphic-fetch": "^2.2.1",
    "node-env-file": "^0.1.8",
    "nodejs-base64": "^1.0.3",
    "nodemailer": "^6.3.0",
    "pg": "^7.18.2",
    "string-similarity": "^3.0.0",
    "twilio": "^3.31.1",
    "unirest": "^0.6.0"
  }
}
Nimantha
  • 4,731
  • 5
  • 15
  • 38
  • It would be helpful for you to include a reproducible example.... the `package.json` so that we may understand which NPM package and the surrounding code. It's curious that your timeout appears to match your "6 minutes". Does the function succeed? How large is the HTML that you're submitting? How large is the PDF? Cloud Functions defaults to a reasonably low memory size (256MB?) and may be a single core (couldn't find immediately). There's also a challenge with global|local scope for instances. All of these may impact throughput. – DazWilkin Sep 01 '20 at 16:52
  • Hi, this is my package.json: – Bluepoint Developer Sep 02 '20 at 23:26
  • Thanks. I'll find some time tomorrow to try it. This type of task is, in principle, well-suited to Functions. Let's try to determine the constraint before trying App Engine – DazWilkin Sep 03 '20 at 00:43

2 Answers2

1

This is because the computational resources assigned to a function can't be comparable with your local device.

The CPU speed on cloud functions is directly proportional to the Memory assigned as is mentioned in this document, please try to set up 2GB Memory to your function.

With more processing resources your function will be faster.

If the function is not responding in an acceptable time for you, the next step is to use another serverless product such as App Engine or Cloud Run that allows you to set more resources to your application.

These changes will be affect your bill.

Jan Hernandez
  • 3,294
  • 2
  • 8
  • 15
0

I tweaked your example and it works for me with a very small HTML file and a 256MB memory allocation. I retained your packages to try to repro your usage. I used an inline and very small HTML document, I used Node.JS 12 and I replaced e.g. file with fs. I'm not much of a Node.JS developer so apologies for the poor code.

/* jshint esversion: 6 */
/* globals exports,require */

const pdf = require("html-pdf");
const fs = require("fs");

const estadoCuenta = `<html><head><title>Stackoverflow: 63688028</title></head><body><div id="content">Hello Freddie!</div></body></html>`;

const convertor = (req, res) => {

    // Write it
    const options = { orientation: 'portrait', type: 'pdf', timeout: '360000' };
    pdf.create(estadoCuenta, options).toStream(function (err, stream) {
        var writeStream = fs.createWriteStream("/tmp/file.pdf");
        stream.pipe(writeStream).on('error', function (err) {
            console.log(`[stream.pipe] Error: ${err}`);
        }).on('finish', function () {
            // Read it
            var readStream = fs.createReadStream('/tmp/file.pdf');
            var filename = encodeURIComponent("converted.pdf");

            res.setHeader('Content-disposition', `inline; filename="${filename}"`);
            res.setHeader('Content-type', 'application/pdf');

            readStream.pipe(res);
            console.log(`Done`);
        });
    });

};

exports.convertor = convertor;

For local debugging, I appended the following:

const express = require("express");
const app = express();
const port = 3000;

app.get("/", convertor);

app.listen(port, () => {
    console.log(`Listening: http://localhost:${port}`);
});

There are many dependencies in the package.json and I assume you need them all. You don't need to import express when you're deploying to Cloud Functions.

NOTE html-pdf includes a severe vuln:

=== npm audit security report ===                        
                                                                                
                                                                                
                                 Manual Review                                  
             Some vulnerabilities require your attention to resolve             
                                                                                
          Visit https://go.npm.me/audit-guide for additional guidance           
                                                                                
                                                                                
  Critical        Arbitrary File Read                                           
                                                                                
  Package         html-pdf                                                      
                                                                                
  Patched in      No patch available                                            
                                                                                
  Dependency of   html-pdf                                                      
                                                                                
  Path            html-pdf                                                      
                                                                                
  More info       https://npmjs.com/advisories/1095                             
                                                                                
found 1 critical severity vulnerability in 750 scanned packages
  1 vulnerability requires manual review. See the full report for details.
DazWilkin
  • 12,847
  • 5
  • 24
  • 48