1

I am having this two errors with my daily scheduled function, I am trying to check every day if it is pay day and add the corresponding amount to the debt of each user.

Error: Process exited with code 16
at process.on.code (invoker.js:271)
at process.emit (events.js:198)
at process.EventEmitter.emit (domain.js:448)
at process.exit (per_thread.js:168)
at Object.logAndSendError (/layers/google.nodejs.functions-framework/functions-framework/node_modules/@google-cloud/functions-framework/build/src/logger.js:37)
at process.on.err (invoker.js:268)
at process.emit (events.js:198)
at process.EventEmitter.emit (domain.js:448)
at emitPromiseRejectionWarnings (internal/process/promises.js:140)
at process._tickCallback (next_tick.js:69)

And:

Error: 4 DEADLINE_EXCEEDED: Deadline exceeded
at Object.callErrorFromStatus (/workspace/node_modules/@grpc/grpc-js/build/src/call.js:30)
at Object.onReceiveStatus (/workspace/node_modules/@grpc/grpc-js/build/src/client.js:175)
at Object.onReceiveStatus (/workspace/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:341)
at Object.onReceiveStatus (/workspace/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:304)
at Http2CallStream.outputStatus (/workspace/node_modules/@grpc/grpc-js/build/src/call-stream.js:116)
at Http2CallStream.maybeOutputStatus (/workspace/node_modules/@grpc/grpc-js/build/src/call-stream.js:155)
at Http2CallStream.endCall (/workspace/node_modules/@grpc/grpc-js/build/src/call-stream.js:141)
at Http2CallStream.cancelWithStatus (/workspace/node_modules/@grpc/grpc-js/build/src/call-stream.js:457)
at Timeout.DeadlineFilter.timer.setTimeout (deadline-filter.js:59)
at ontimeout (timers.js:436)

This is my function:

import * as functions from "firebase-functions"; import * as admin from "firebase-admin";

/// Checks every day if the debt pay day is equal to the setting and add the corresponding amount to the debt

export const checkForMonthChange = functions.pubsub
  .schedule("every day 00:00")
  .onRun(async (context) => {
    try {
      const db = admin.firestore();
      const dayOfMonth = admin.firestore.Timestamp.now().toDate().getDate();
      const neighborhoods = await db.collection("_settings").get();
      const promises = [];

      neighborhoods.forEach(async (doc) => {
        const dayToPay = doc.data().day_of_month_to_pay;

        if (dayOfMonth === dayToPay) {
          const neighborhoodToUpdateName = doc.ref.id;
          const neighborhoodToUpdate = await db
            .collection(neighborhoodToUpdateName)
            .get();

          neighborhoodToUpdate.forEach(async (neighbor) => {
            const userPlan = await neighbor.data().plan;
            const amountToAdd = (
              await db
                .collection("_settings")
                .doc(neighborhoodToUpdateName)
                .collection("payment_plans")
                .doc(userPlan)
                .get()
            ).data().monthly_pay;
            console.log(userPlan);
            console.log(amountToAdd);

            promises.push(
              neighbor.ref.update({
                debt: neighbor.data()?.debt + amountToAdd,
              })
            );
          });
        }
      });

      if (Promise.all.length !== 0) {
        console.log(`Debt Increased Day of Month: ${dayOfMonth}`);
        return Promise.all(promises);
      } else {
        return null;
      }
    } catch (error) {
      console.log(`Error in every day schedule: ${error}`);
    }
  });

What I am doing wrong?

Edit: I am seeing these 2 logs in the console

console.log(userPlan);
console.log(amountToAdd);

Thanks in advance

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Miguel Chavez
  • 161
  • 1
  • 2
  • 14

1 Answers1

3

Use of await inside a forEach lambda function does not work the way you expect. It won't pause the execution of the loop. The way you have it written right now, the forEach loop will execute many times immediately, once for each neighborhood. The code will go on to check the promises array (before any of the loops have executed), which will always be empty.

You will need to make sure that your code returns a promise that resolves only after all the async work inside the forEach loop is actually complete. See this other question for a full discussion about that. The problem is due to the fact that forEach accepts a function, which is declared async, which immediately returns a promise.

Or, you can avoid using forEach and instead use a for loop that runs code in the async context of the greater function, so that you can use await the way you would expect.

for (const doc of neighborhoods.docs) {
    // use await here to pause the entire function
}
Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441