Облачные функции для базы данных Firebase onWrite срабатывают дважды

Привет, я разрабатываю систему уведомлений, но у меня возникают проблемы с удалением обработанных данных уведомлений. Прослушиватель событий onWrite запускается дважды, что приводит к двум уведомлениям.

Можете ли вы помочь мне найти обходной путь, чтобы прослушиватель событий onWrite не запускался дважды? Важно удалить обработанные данные.

exports.sendMessageNotification = functions.database.ref('/notification/message/{recipientUid}/{senderUid}').onWrite(event => {
/* processing notification and sends FCM */

return admin.messaging().sendToDevice(tokens, payload).then(response => {
      // For each message check if there was an error.
      const toRemove = [];
      response.results.forEach((result, index) => {
        const error = result.error;
        if (error) {
          console.error('Failure sending notification to', tokens[index], error);
          // Cleanup the tokens who are not registered anymore.
          if (error.code === 'messaging/invalid-registration-token' ||
              error.code === 'messaging/registration-token-not-registered') {
            toRemove.push(tokensSnapshot.ref.child(tokens[index]).remove());
          }
        }
      });

      //Deletes processed notification
      console.log("Removing notification");
      const getNotificationPromise = admin.database().ref(`/notification/message/${recipientUid}/${senderUid}`).once('value');
      return Promise.all([getNotificationPromise]).then(results => {
        const notificationSnapshot = results[0];
        toRemove.push(notificationSnapshot.ref.remove());

        console.log("Removing tokens.")
        return Promise.all(toRemove);
      });
      //return Promise.all(tokensToRemove);
    });
});

})



Ответы (2)


Это распространенная ошибка. Вы выполняете обратную запись в то же расположение базы данных (путем удаления данных), которое было сопоставлено при первом запуске функции. Это означает, что удаление снова вызовет функцию для обработки этого второго изменения. В настоящее время это ожидаемое поведение.

Вам нужно будет придумать способ определить, что вторая запись произошла в ответ на удаление данных. Кроме того, в настоящее время вы выполняете слишком много работы по своей функции. Нет необходимости читать значение базы данных в '/notification/message/{recipientUid}/{senderUid}' — оно уже доставлено вам в событии, переданном функции. Обязательно ознакомьтесь с документацией о триггерах баз данных. Вы можете узнать, была ли функция запущена во второй раз, изучив данные события и вернувшись досрочно, если оно равно null, что означает, что оно было удалено.

Кроме того, вам не нужен Promise.all(), если вы имеете дело с одним обещанием. Просто используйте then() для этого единственного обещания, чтобы продолжить обработку, или верните это единственное обещание из then().

Возможно, вы захотите взглянуть на некоторые из примеров кода, которые показывают триггеры базы данных.

person Doug Stevenson    schedule 31.03.2017
comment
Спасибо за этот ответ, он действительно помог мне понять триггеры, особенно объект события. - person Lester; 31.03.2017
comment
Этот ответ помог мне узнать, что я могу получить снимок данных через const data = event.data; и добавить if внутри функции и вернуться, если нет значения if(data == undefined || !data.val()){return;} - person Lester; 31.03.2017

Если кто-то все еще не понимает этого, начиная с firebase-functions v0.5.9 ; вы можете использовать onCreate(), onUpdate() или onDelete(). Просто убедитесь, что ваша версия функций firebase обновлена, что вы можете сделать, перейдя в каталог функций и запустив

npm install --save firebase-functions 

Кроме того, это было рассмотрено в документации Firebase и sample, а также то, что если вы используете onWrite(), как объяснил Дуг выше, функция будет запускаться для всех событий в этом узле, т. е. write; обновить или удалить. Поэтому вы должны убедиться, что ваша функция не застревает в цикле. Что-то вроде:

   //if data is not new 
    if (event.data.previous.exists()) {return}
    // Exit when the data is deleted; needed cuz after deleting the node this function runs once more.
    if (!event.data.exists()) {return}

Ваше здоровье.

person TheeBen    schedule 18.08.2017
comment
В Android я использую push для создания нового узла и получения его ключа, а после этого использую setValue. Событие onCreate запускается для отправки, а event.data имеет значение null? :( - person JCarlosR; 21.08.2017
comment
Хм.. Не уверен, в чем проблема; потребуется дополнительная информация или, возможно, код, чтобы быть более полезным, но у меня есть функции, работающие с onCreate(), и он работает нормально. Возможно, сделайте это Q в SO и свяжите его здесь, и я мог бы посмотреть на него. - person TheeBen; 21.08.2017
comment
Извините, я думал, что нажатие запускает событие onCreate. Но он вызывается только тогда, когда мы устанавливаем значение для нового ключа. - person JCarlosR; 21.08.2017
comment
Да пока вы ничего не написали в db ничего бы не вызывалось. Вы можете инициировать процесс, если это то, что вы хотите, нажав сгенерированный ключ с логическим значением или чем-то еще, просто чтобы записать его в БД и, например, вызвать функцию. Ваше здоровье - person TheeBen; 21.08.2017