0

I am building an application where people can host events. It allows for users to follow the events and all the event followers can chat in a group. I am storing the FCM token for push notifications in a user collection in Firestore. But when I send the notification my current logic is not quite optimal and it takes several minutes for the notification to send to each user as I am first getting all user's tokens for each user's document and then combing those tokens in a list and send push notification to each token using a loop in my cloud function. I think it takes time for sending the messages and the group chat does not seem to be real-time because of that computation.

What I thought is to store the FCM tokens of each user inside every event he follows. So that when there is a chat message the list of tokens is fetched from the specific event and they are sent a multicast notification. But here again, I am not sure if it is a good approach because I will need to keep track of refreshed tokens and update the tokens in each document where it exists. Let's say a user has joined 50 events and on app launch, his FCM token got refreshed now I will have to run a cloud function that will see if the token is renewed it should loop through all of those 50 events and update his token.

I want to know what can be the best approach for this use case. Below is the code that I am using:

exports.sendNotification = functions.firestore
  .document("event/{eventid}/{chat}/{chatid}")
  .onCreate((snap, context) => {
    processData(snap, context);
    return null;
  });

async function processData(snap, context) {
  const doc = snap.data();
  const eventID = context.params.eventid;
  const senderID = doc.sender;
  var senderName = doc.senderName;
  await admin
    .firestore()
    .collection("event")
    .doc(eventID)
    .get()
    .then(value => {
      var joinedBy = value.data()["joinedBy"];
      joinedBy.forEach(item => {
        getTokens(uid, senderID, doc, eventName, senderName);
      });
    });
}

async function getTokens(uid, senderID, doc, eventName, senderName) {
  await admin
    .firestore()
    .collection("people")
    .doc(uid)
    .get()
    .then(value => {
      var token = value.data()["pushToken"];
      if (uid != senderID) {
        if (token) {
          const payload = {
            notification: {
              title: `${eventName} : ${senderName}`,
              body: doc.text,
              badge: "1",
              sound: "default"
            },
          };
          sendMessage(token, payload);
        } else {
          console.log("Can not find pushToken target user");
        }
      } else {
        console.log("uid and sender id is same so notification not sent");
      }
    });
}
Lyba Kashif
  • 115
  • 2
  • 7
  • Instead of describing your approach in words and asking us for improvements, can you show us the [minimal code that reproduces the performance problem](http://stackoverflow.com/help/mcve) you're asking about? – Frank van Puffelen Jul 25 '22 at 14:21
  • Thanks for your response @FrankvanPuffelen. I have updated my question and added the code snippet. – Lyba Kashif Jul 25 '22 at 16:09
  • And which part of this code are you concerned with? Keep in mind, while your use-case is probably important to you, we can much better help if you tell us about the relevant API calls that are not doing what you expect them to do, or where you are worried about their scalability. – Frank van Puffelen Jul 26 '22 at 03:46
  • @FrankvanPuffelen this is the current functionality where the notification is sent after gathering all the tokens by looking into each user's document who has joined the event. I used topic subscription for push notifications in other apps they work really fast and I receive notifications instantly. But when I used FCM tokens I think this computation of tokens in each user document is taking time due to which the notifications are delayed. So in process data, the `joinedBy` is a list of users who have joined this particular event and then it loops through that list. – Lyba Kashif Jul 26 '22 at 07:28
  • So to avoid FCM tokens computation on runtime I thought to maintain a list of tokens for each user in every event itself so that I get the tokens list right away and send a multicast message. But then I am a little confused that if I do this will it be a good approach? Because I will need to set up a mechanism to update the refreshed tokens of users in each event document when applicable. In the case of many users, I think it will not be a good solution but I can not think of any other alternative for such a scenario for faster notifications. – Lyba Kashif Jul 26 '22 at 07:41
  • Ah, I think I understand now. I'll give some pointers below. – Frank van Puffelen Jul 26 '22 at 14:00

1 Answers1

0

One thing to consider in your current approach is whether you really first need to read all tokens for the message and only then starting calling FCM. Might things get faster if you call FCM right away on each set of tokens you read, and thus perform some of the reads and FCM calls in parallel?


Another consideration is that you say that FCM's built-in topics work really fast for you. Internally FCM's topic subsystem associates a bulk of device IDs with the topic, and then reads the tokens for the topic as needed.

Is this something you can mimic for your app? For example, can you store the tokens in larger groups inside fewer documents, thus reducing the number of read operations you need to do.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807