0

As described in the firebase docs, it is required to

"resolve functions that perform asynchronous processing (also known as "background functions") by returning a JavaScript promise." (https://firebase.google.com/docs/functions/terminate-functions?hl=en).

otherwise it might happen, that

"the Cloud Functions instance running your function does not shut down before your function successfully reaches its terminating condition or state. (https://firebase.google.com/docs/functions/terminate-functions?hl=en)

In this case I am trying to adapt a demo-code for pdf-generation written by Volodymyr Golosay on https://medium.com/firebase-developers/how-to-generate-and-store-a-pdf-with-firebase-7faebb74ccbf.

The demo uses 'https.onRequest' as trigger and fulfillis the termination requirement with 'response.send(result)'. In the adaption I need to use a 'document.onCreate' trigger and therefor need to find a different termination.

In other functions I can fulfill this requirement by using async/await, but here I am struggling to get a stable function with good performance. The shown function logs after 675 ms "finished with status: 'ok' ", but around 2 minutes later it logs again that the pdf-file is saved now (see screenshot of the logger). screenshot of logger showing bad function termination

What should I do to terminate the function properly?

// adapting the demo code by Volodymyr Golosay published on https://medium.com/firebase-developers/how-to-generate-and-store-a-pdf-with-firebase-7faebb74ccbf
// library installed -> npm install pdfmake
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
const db = admin.firestore();

const Printer = require('pdfmake');
const fonts = require('pdfmake/build/vfs_fonts.js');
const fontDescriptors = {
  Roboto: {
    normal: Buffer.from(fonts.pdfMake.vfs['Roboto-Regular.ttf'], 'base64'),
    bold: Buffer.from(fonts.pdfMake.vfs['Roboto-Medium.ttf'], 'base64'),
    italics: Buffer.from(fonts.pdfMake.vfs['Roboto-Italic.ttf'], 'base64'),
    bolditalics: Buffer.from(fonts.pdfMake.vfs['Roboto-Italic.ttf'], 'base64'),
  }
};


exports.generateDemoPdf = functions
// trigger by 'document.onCreate', while demo uses 'https.onRequest'
.firestore   
  .document('collection/{docId}')
  .onCreate(async (snap, context) => {     

    const printer = new Printer(fontDescriptors);
    const chunks = [];
    // define the content of the pdf-file
    const docDefinition = {
        content: [{
            text: 'PDF text is here.',
            fontSize: 19 }
        ]
    };

  
  const pdfDoc = printer.createPdfKitDocument(docDefinition);

  pdfDoc.on('data', (chunk) => {
    chunks.push(chunk);
  });

  pdfDoc.on('end', async () => {
    const result = Buffer.concat(chunks);
    
    // Upload generated file to the Cloud Storage
    const docId = "123456789"
    const bucket = admin.storage().bucket();
    const fileRef = bucket.file(`${docId}.pdf`, { 
        metadata: { 
            contentType: 'application/pdf'
        } 
    });
    await fileRef.save(result);    
    console.log('result is saved'); 
    // NEEDS PROPER TERMINATION HERE?? NEEDS TO RETURN A PROMISE?? FIREBASE DOCS: https://firebase.google.com/docs/functions/terminate-functions?hl=en
    // the demo with 'https.onRequest' uses the following line to terminate the function properly:
    // response.send(result);     
  });  

  pdfDoc.on('error', (err) => {    
    return functions.logger.log('An error occured!');
  });

  pdfDoc.end();
});
  • I suspect you *did* terminate properly - that's the nature of promises. Your function "terminated" with a 200 status, returning a PROMISE for the results of the PDF save. When the PDF save actually terminates later, the result is logged and the promise resolved. This behavior is WHY you return the promise. – LeadDreamer Jan 18 '22 at 19:30
  • Does this answer your question? [Why is my PDF not saving intermittently in my Node function?](https://stackoverflow.com/a/69458478/3068190). In particular, you should implement the last code block in that answer to make sure the upload to Cloud Storage completes before the function finishes. – samthecodingman Jan 19 '22 at 11:53
  • Yes, with the code block in this answer, the performance of the cloud-function improved from ~ 2 min to only 4 seconds! Thank you. – stayfoolish Jan 20 '22 at 10:20

2 Answers2

1

I think everything is fine in your code. It seems it takes 1m 34s to render the file and save it to storage.

Cloud function will be terminated automatically when all micro and macro tasks are done. Right after you last await.

To check how long does it takes and does it terminate right after saving, you can run the firebase emulator on your local machine.

You will see logs in the terminal and simultaneously watch on storage.

  • Do I need to install the jquery library in order to run the $.on() methods correctly? I was just reading about eventListeners ( https://stackoverflow.com/a/30880803/17251491) and wonder if this might be a gap in my setup. – stayfoolish Jan 19 '22 at 10:22
  • In cloud functions, you don't need any DOM eventListeners. jQuery is a library for DOM manipulation. – Volodymyr Golosay Jan 20 '22 at 12:22
0

I suspect you did terminate properly - that's the nature of promises. Your function "terminated" with a 200 status, returning a PROMISE for the results of the PDF save. When the PDF save actually terminates later, the result is logged and the promise resolved. This behavior is WHY you return the promise.

LeadDreamer
  • 3,303
  • 2
  • 15
  • 18