Search code examples
node.jsexpressgoogle-cloud-platformgoogle-cloud-functionstimeout

NodeJS / Express function only invoked one time in Google Cloud functions


I'm creating a google cloud function that sends a daily email to users but the function is only invoked first time after deployment and then always fails with timeout even after 300 seconds (In general it takes 10 - 15 seconds to execute).

The function is doing DB finds, for loops and if conditions, so I think it's a problem of request / response from the app so it's prevent future invokation. Can you please help me modify the code below.

app.get('/emails', function(req, res){
    var books = [];
    Book.find({}, (err, res) => {
       if (err) {
           return err;
       }
       books = res;
   }).catch(err => console.log(err))
   User.find({}, (err, users) => {
    if (err) {
        return err;
    }

    const uniques = users.filter(
      (v, i, a) => a.findIndex((t) => t.ID === v.ID) === i
    );

    for(var i =0; i < uniques.length; i++) {
      var count = 0;
      for (var j = 0; j < books.length; j++) {
        if (books[j].UserID === uniques[i].ID && books[j].State === "OK") {
          count += 1;
        }
      }
      var EmailPayload = {
          // nodemailer object here
    }
    if (count > 0) {
      Email.sendEmailIndex(EmailPayload);
    }
    } 
  }).catch(err => {
    console.log(err)
  })
  });

Solution

  • The function is being invoked by GET /emails which expects a response. Without a response, the function will keep running until it times out because Google doesn't know it finished.

    To fix this, conclude with a basic 200 after the script is done. It's also sensible to respond in case of failure to cover all cases and make sure there's always a response sent back.

    app.get('/emails', function(req, res) {
        var books = [];
        Book.find({}, (err, res) => {
            if (err) {
                return err;
            }
            books = res;
    
            return User.find({}, (err, users) => {
                if (err) {
                    return err;
                }
    
                const uniques = users.filter(
                    (v, i, a) => a.findIndex((t) => t.ID === v.ID) === i
                );
    
                for (var i = 0; i < uniques.length; i++) {
                    var count = 0;
                    for (var j = 0; j < books.length; j++) {
                        if (books[j].UserID === uniques[i].ID && books[j].State === "OK") {
                            count += 1;
                        }
                    }
                    var EmailPayload = {
                        // nodemailer object here
                    }
                    if (count > 0) {
                        Email.sendEmailIndex(EmailPayload);
                    }
                }
    
                // Send a response to conclude function execution
                res.status(200).send({
                    message: 'Emails sent'
                });
            });
        }).catch(err => {
            console.log(err);
    
            // Send a response to conclude function execution
            res.status(500).send({
                message: 'Failed to send emails',
                error: err
            });
        })
    });