Home > Net >  FCM very slow and unreliable when sending to a group of recipients through Cloud Function
FCM very slow and unreliable when sending to a group of recipients through Cloud Function

Time:01-26

I have the following Function that:

  1. Listens for document (text message) creation
  2. Grab IDs of members of a group chat
  3. Get the FCM Tokens for each member
  4. With a for-loop, send messages to group members
exports.sendChatMessage = functions.firestore
  .document("chats/{mealID}/messages/{messageID}")
  .onCreate((snap, context) => {
    const data = snap.data();
    const mealID = context.params.mealID;
    const senderID = data.senderID;
    const senderName = data.senderName;
    const messageContent = data.content;

    var docRef = db.collection("chats").doc(mealID);
    docRef
      .get()
      .then((doc) => {
        if (doc.exists) {
          const docData = doc.data();
          const mealName = docData.name;
          const userStatus = docData.userStatus;
          var users = docData.to;
          var eligibleUsers = users.filter(
            (user) => userStatus[user] == "accepted"
          );
          eligibleUsers.push(docData.from);

          // get fcmTokens from eligibleUsers and send the messagme
          db.collection("users")
            .where("uid", "in", eligibleUsers)
            .get()
            .then((snapshot) => {
              var fcmTokens = [];
              var thumbnailPicURL = "";
              // get thumbnailpic of the sender and collect fcmTokens
              snapshot.forEach((doc) => {
                if (doc.data().uid == senderID) {
                  thumbnailPicURL =
                    doc.data().thumbnailPicURL == null
                      ? "https://i.imgur.com/8wSudUk.png"
                      : doc.data().thumbnailPicURL;
                } else {
                  fcmTokens.push(doc.data().fcmToken);
                }
              });

              // send the message fcmTokens
              fcmTokens.forEach((token) => {
                if (token != "") {
                  const fcmMessage = {
                    message: {
                      token: token,
                      notification: {
                        title: mealName,
                        body: senderName   ": "   messageContent,
                        image: thumbnailPicURL,
                      },
                      apns: {
                        payload: {
                          aps: {
                            category: "MESSAGE_RECEIVED",
                          },
                          MEAL_ID: mealID,
                        },
                      },
                    },
                  };
                  tokenManger.sendFcmMessage(fcmMessage);
                }
              });
              return true;
            });
        } else {
          // doc.data() will be undefined in this case
          console.log("No such document!");
          return false;
        }
      })
      .catch((error) => {
        console.log("Error getting document:", error);
        return false;
      });
    return true;
  });

My send function comes from a helper file that uses the Cloud Function console output

I also noticed that the console says the cloud function finishes execution BEFORE sendFcmMessage logs success messages.

I did some research online, it appears that it might have something to do with the usage of Promise but I wasn't sure if that's the sole reason or it has something to do with my for-loop.

CodePudding user response:

This doesn't have to do with the usage of Promise, but with the not usage of Promise ...you're sending asynchronous, while the function exists ...await the promised result, maybe? There no other way of changing the order of events in the log (I'd assume that it still should send). When setting the priority of a notification to high, it should be delivered rather sooner than later; the default value may be normal (if there would be an android payload).

Wrap the sending of the message in a Promise, too... with resolve() and reject():

return new Promise(function (resolve, reject) { ... });

Then you can await it's result, just alike the token function does that.
When adding further logging, it should become clear when what happens.


The business logic has one major flaw: it's not chunking fcmTokens into chunks of 500 tokens each. Alike that, lots of processing is being performend, without any actual purpose - up to 500 times more processing, which may lead to these delays; token should be an array for certain.

CodePudding user response:

I found out that the culprit is my queries to db. Like @samthecodingman commented, I was creating floating Promises.

Originally, I have codes like:

db.collection("users")
            .where("uid", "in", eligibleUsers)
            .get()
            .then((snapshot) => {...}

All I needed to do is to return that call:

return db.collection("users")
            .where("uid", "in", eligibleUsers)
            .get()
            .then((snapshot) => {...}

Although it's still not instant delivery, it's much faster now.

  •  Tags:  
  • Related