Как доставляются сообщения

Чтобы устранить неполадки с доставкой сообщений, воспользуйтесь инструментом устранения неполадок FCM и прочитайте эту запись блога, в которой рассказывается о возможных причинах проблем. Вы также можете перейти на панель мониторинга статуса FCM, чтобы узнать, нет ли сбоев в работе сервиса.

FCM также предоставляет три набора инструментов, которые помогут вам оценить эффективность отправки сообщений и стратегии:

  • Firebase отчетов о доставке сообщений в консоли
  • Агрегированные показатели доставки Android SDK из Data API Firebase Cloud Messaging
  • Полный экспорт данных в Google BigQuery

Экспорт данных в BigQuery и вкладка Отчеты в консоли Firebase работают только при наличии Google Analytics. Вы можете включить Google Analytics в консоли Firebase на вкладке Настройки > Интеграции. Для работы агрегированных данных о показе Google Analytics не требуется.

Обратите внимание, что многие показатели на этой странице могут обновляться с задержкой до 24 часов из-за пакетной обработки данных.

Отчеты о доставке сообщений

В консоли Firebase перейдите на вкладку DevOps & Engagement (DevOps и взаимодействие) > Messaging (Обмен сообщениями) > Reports (Отчеты), чтобы посмотреть следующие данные о сообщениях, отправленных в SDK FCM для платформ Android или Apple, в том числе с помощью конструктора уведомлений и API FCM:

  • Отправлено – сообщение с данными или уведомление поставлено в очередь на доставку или успешно передано стороннему сервису, например APNs, для доставки. Обратите внимание, что статистика по отправленным письмам может обновляться с задержкой в несколько часов. Подробнее о сроке хранения сообщений…
  • "Получено" (доступно только на устройствах Android) – приложение получило сообщение с данными или уведомление. Эти данные доступны, если на принимающем устройстве Android установлен SDK FCM версии 18.0.1 или более поздней.
  • Показы (доступно только для уведомлений на устройствах Android) – уведомление было показано на устройстве, когда приложение работало в фоновом режиме.
  • Открытия – пользователь открыл уведомление. Регистрируется только для уведомлений, полученных, когда приложение работает в фоновом режиме.

Эти данные доступны для всех сообщений с полезной нагрузкой уведомлений и всех помеченных сообщений с данными. Подробнее о добавлении ярлыков аналитики в сообщения…

При просмотре отчетов о сообщениях можно задать диапазон дат для отображаемых данных и экспортировать их в формате CSV. Вы также можете отфильтровать данные по следующим критериям:

  • Платформа (iOS или Android).
  • Приложение
  • Специальные ярлыки аналитики

Добавление ярлыков аналитики к сообщениям

Ярлыки очень полезны для анализа, поскольку позволяют фильтровать статистику доставки по ярлыкам или их наборам. Вы можете добавить ярлык к любому письму, отправленному с помощью API HTTP версии 1, задав поле fcmOptions.analyticsLabel в объекте message или в полях AndroidFcmOptions или ApnsFcmOptions, относящихся к определенной платформе.

Ярлыки Аналитики – это текстовые строки в формате ^[a-zA-Z0-9-_.~%]{1,50}$. Ярлыки могут содержать строчные и заглавные буквы, цифры и следующие символы:

  • -
  • ~
  • %

Максимальная длина – 50 символов. В день можно указать до 100 уникальных ярлыков. Сообщения с ярлыками, добавленными сверх этого лимита, не включаются в отчеты.

На вкладке Отчеты в разделе сообщений консоли Firebase можно найти список всех существующих ярлыков и применить их по отдельности или в сочетании, чтобы отфильтровать отображаемую статистику.

Агрегированные данные о показе, полученные с помощью FCM Data API

Firebase Cloud Messaging Data API позволяет получать информацию, которая помогает понять результаты запросов сообщений, предназначенных для приложений Android. API предоставляет агрегированные данные со всех устройств Android, на которых включен сбор данных в проекте. В частности, вы можете узнать, какой процент сообщений был доставлен без задержек, а также сколько сообщений было задержано или отклонено на транспортном уровне Android. Анализ этих данных может выявить общие тенденции в доставке сообщений и помочь вам найти эффективные способы повышения производительности ваших запросов на отправку. Информацию о доступности диапазонов дат в отчетах можно найти в разделе Сроки агрегирования данных.

API предоставляет все данные, доступные для определенного приложения. Ознакомьтесь со справочной документацией по API.

Как разбиты данные?

Данные о доставке разбиты по приложениям, датам и ярлыкам аналитики. Вызов API возвращает данные для каждого сочетания даты, приложения и ярлыка аналитики. Например, один объект JSON androidDeliveryData будет выглядеть так:

 {
  "appId": "1:23456789:android:a93a5mb1234efe56",
  "date": {
    "year": 2021,
    "month": 1,
    "day": 1
  },
  "analyticsLabel": "foo",
  "data": {
    "countMessagesAccepted": "314159",
    "messageOutcomePercents": {
      "delivered": 71,
      "pending": 15
    },
   "deliveryPerformancePercents": {
      "deliveredNoDelay": 45,
      "delayedDeviceOffline": 11
    }
  }

Как интерпретировать показатели

Данные о доставке показывают процент сообщений, соответствующих каждому из следующих показателей: Одно сообщение может соответствовать нескольким показателям. Из-за ограничений, связанных со сбором данных и уровнем детализации, на котором мы агрегируем показатели, некоторые результаты сообщений вообще не представлены в показателях, поэтому сумма процентов ниже не будет равна 100 %.

Количество принятых сообщений

Единственное количество, включенное в набор данных, – это количество сообщений, которые были приняты FCM для доставки на устройства Android. Все проценты рассчитываются на основе этого значения. Обратите внимание, что в статистику не входят сообщения, отправленные пользователям, которые отключили сбор данных об использовании и диагностике на своих устройствах.

Процент результатов сообщений

Поля, включенные в объект MessageOutcomePercents, содержат информацию о результатах запросов сообщений. Категории являются взаимоисключающими. С его помощью можно получить ответы на такие вопросы, как "Доставляются ли мои сообщения?" и "Почему сообщения отклоняются?".

Например, высокое значение поля droppedTooManyPendingMessages может указывать на то, что экземпляры приложения получают большое количество нескладываемых сообщений, превышающее ограничение FCM в 100 ожидающих сообщений. Чтобы избежать этого, убедитесь, что ваше приложение обрабатывает вызовы onDeletedMessages, и рассмотрите возможность отправки сворачиваемых сообщений. Аналогично, высокий процент droppedDeviceInactive может быть сигналом к обновлению токенов регистрации на сервере, удалению устаревших токенов и отмене их подписки на темы. Рекомендации по этому вопросу можно найти в статье Как управлять токенами регистрации FCM.

Процентные показатели эффективности доставки

Поля в объекте DeliveryPerformancePercents содержат информацию об успешно доставленных сообщениях. С его помощью можно получить ответы на такие вопросы, как "Были ли задержки в доставке моих писем?" и "Почему письма задерживаются?". Например, высокое значение delayedMessageThrottled явно указывает на то, что вы превышаете максимальные ограничения для одного устройства и вам следует скорректировать частоту отправки сообщений.

Процентные показатели в статистике сообщений

Этот объект содержит дополнительную информацию обо всех отправленных сообщениях. Поле priorityLowered содержит процент принятых писем, приоритет которых был снижен с HIGH до NORMAL. Если это значение высокое, попробуйте отправлять меньше сообщений с высоким приоритетом или убедитесь, что при отправке сообщения с высоким приоритетом всегда показывается уведомление. Подробнее о приоритете сообщений…

Чем эти данные отличаются от данных, экспортированных в BigQuery?

Экспорт BigQuery предоставляет отдельные журналы сообщений о принятии сообщений серверной частью FCM и доставке сообщений в SDK на устройстве (шаги 2 и 4 архитектуры FCM). Эти данные позволяют убедиться, что отдельные сообщения были приняты и доставлены. Подробнее об экспорте данных в BigQuery…

В отличие от него, Firebase Cloud Messaging Data API предоставляет агрегированные сведения о том, что происходит на транспортном уровне Android (шаг 3 в архитектуре FCM). Эти данные позволяют отслеживать доставку сообщений из серверной части FCM в Android SDK. Это особенно полезно для выявления тенденций, объясняющих, почему письма задерживались или отклонялись во время передачи.

В некоторых случаях эти два набора данных могут не совпадать по следующим причинам:

  • Агрегированные показатели рассчитываются только на основе части всех сообщений.
  • Агрегированные показатели округляются
  • Мы не показываем показатели, которые не соответствуют пороговому значению конфиденциальности
  • Часть результатов сообщений отсутствует из-за оптимизации управления большим объемом трафика.

Ограничения API

Хронология агрегированных данных

API возвращает статистику за семь дней, но с задержкой до пяти дней. Например, 20 января будут доступны данные за период с 9 по 15 января, но не за 16 января и более поздние даты. Кроме того, данные предоставляются по мере возможности. В случае сбоя в работе FCM мы постараемся устранить его как можно скорее, но не будем восстанавливать данные за период сбоя. При более масштабных сбоях данные могут быть недоступны в течение недели или более.

Охват данных

Показатели, предоставляемые Firebase Cloud Messaging Data API, позволяют выявлять общие тенденции в доставке сообщений. Однако они не охватывают все возможные сценарии. Ниже перечислены известные результаты, которые не отражаются в показателях.

Сообщения с истекшим сроком действия

Если время жизни (TTL) истекает после окончания указанной даты в журнале, сообщение не будет считаться droppedTtlExpired в эту дату.

Сообщения на неактивных устройствах

Сообщения, отправленные на неактивные устройства, могут быть или не быть в наборе данных в зависимости от того, каким образом они были переданы. Это может привести к неточностям в полях droppedDeviceInactive и pending.

Сообщения для устройств с определенными пользовательскими настройками

Если пользователь отключил сбор данных об использовании и диагностике на своем устройстве, его сообщения не будут учитываться.

Округление и минимальные значения

FCM намеренно округляет и исключает подсчеты, когда объемы недостаточно велики.

Экспорт данных в BigQuery

Вы можете экспортировать данные о сообщениях в BigQuery для дальнейшего анализа. BigQuery позволяет анализировать данные с помощью SQL BigQuery, экспортировать их в другое облачное хранилище или использовать для собственных моделей машинного обучения. Экспорт в BigQuery включает все доступные данные о сообщениях, независимо от их типа и от того, отправлены ли они с помощью API или Компоновщика уведомлений.

Для сообщений, отправленных на устройства с указанными ниже минимальными версиями SDK, можно включить экспорт данных о доставке сообщений для вашего приложения:FCM

  • Android 20.1.0 или более поздней версии.
  • iOS 8.6.0 или более поздней версии.
  • Firebase Web SDK версии 12.14.0 или более поздней.

Чтобы начать, свяжите проект с BigQuery в консоли Firebase.

  1. Выберите действие. Вы можете:

    • Перейдите в раздел DevOps & Engagement (DevOps и вовлечение) > Messaging (Обмен сообщениями) > the Notifications composer (Компоновщик уведомлений), а затем нажмите Access BigQuery (Доступ к BigQuery) внизу страницы.

    • Перейдите на вкладку Settings (Настройки) > Integrations (Интеграции). Затем в BigQuery нажмите Связать.

      На этой странице представлены варианты экспорта FCM для всех приложений с поддержкой FCM в проекте.

  2. Следуйте инструкциям на экране, чтобы включить BigQuery.

Подробнее о том, как связать BigQuery с Firebase…

Если вы включите экспорт данных из BigQuery в Cloud Messaging:

  • Firebase экспортирует данные в BigQuery. Примечание. Первоначальное распространение данных для экспорта может занять до 48 часов.

  • После создания набора данных местоположение изменить нельзя, но можно скопировать набор данных в другое место или вручную переместить (воссоздать) набор данных в другое местоположение. Подробнее о том, как изменить местоположение набора данных…

  • Firebase настраивает регулярную синхронизацию данных из проекта Firebase с BigQuery. Ежедневные операции экспорта начинаются в 4:00 по тихоокеанскому времени и обычно завершаются в течение 24 часов.

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

Чтобы отключить экспорт BigQuery, отмените связь с проектом в консоли Firebase.

Как включить экспорт данных о доставке писем

iOS+

На устройствах iOS с SDK FCM 8.6.0 или более поздней версии можно включить экспорт данных о доставке сообщений приложения. FCM поддерживает экспорт данных как для оповещений, так и для фоновых уведомлений. По умолчанию экспорт данных отключен на уровне приложения. Если вы включите его программно на уровне экземпляра приложения, то сможете запрашивать у конечных пользователей разрешение на анализ данных о доставке сообщений (рекомендуется). Если заданы оба значения, значение на уровне экземпляра приложения переопределяет значение на уровне приложения.

Прежде чем включить эти параметры, необходимо создать связь FCM-BigQuery для проекта, как описано в статье Экспорт данных в BigQuery.

Как включить экспорт данных о доставке для уведомлений об оповещениях

Поскольку расширения приложений для сервиса уведомлений могут запускаться только уведомлениями об оповещениях, вам необходимо добавить в приложение расширение сервиса уведомлений и вызвать этот API внутри расширения сервиса, чтобы включить отслеживание сообщений. Ознакомьтесь с документацией Apple по изменению контента в новых уведомлениях.

Для каждого полученного уведомления необходимо выполнить следующий вызов:

Swift

// For alert notifications, call the API inside the service extension:
class NotificationService: UNNotificationServiceExtension {
  override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
    Messaging.serviceExtension().exportDeliveryMetrics(withMessageInfo:request.content.userInfo)
  }
}

Objective-C

// For alert notifications, call the API inside the service extension:
@implementation NotificationService
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request
                   withContentHandler:(void (^)(UNNotificationContent *_Nonnull))contentHandler {
  [[FIRMessaging extensionHelper] exportDeliveryMetricsToBigQueryWithMessageInfo:request.content.userInfo];
}
@end

Если вы создаете запросы на отправку с помощью HTTP v1 API, убедитесь, что в объекте полезной нагрузки указано значение `mutable-content = 1`.

Включить экспорт данных о доставке для фоновых уведомлений

Для фоновых сообщений, полученных, когда приложение работает в активном или фоновом режиме, вы можете вызвать API экспорта данных в обработчике сообщений с данными основного приложения. Этот вызов необходимо выполнять для каждого полученного уведомления:

Swift

// For background notifications, call the API inside the
// UIApplicationDelegate or NSApplicationDelegate method:
func application(_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
  Messaging.serviceExtension().exportDeliveryMetricsToBigQuery(withMessageInfo:userInfo)
}

Objective-C

// For background notifications, call the API inside the
// UIApplicationDelegate or NSApplicationDelegate method:
@implementation AppDelegate
- (void)application:(UIApplication *)application
    didReceiveRemoteNotification:(NSDictionary *)userInfo
          fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
  [[FIRMessaging extensionHelper] exportDeliveryMetricsToBigQueryWithMessageInfo:userInfo];
}
@end

Android

На устройствах Android с FCM SDK версии 20.1.0 или более поздней можно включить экспорт данных о доставке сообщений в приложении. По умолчанию экспорт данных отключен на уровне приложения. Если включить его на уровне экземпляра приложения, вы сможете запрашивать у конечных пользователей разрешение на анализ данных о доставке сообщений (рекомендуется). Если заданы оба значения, то значение на уровне экземпляра приложения переопределяет значение на уровне приложения.

Прежде чем включать эти параметры, необходимо создать связь FCM с BigQuery для проекта, как описано в разделе Экспорт данных в BigQuery.

Как включить экспорт данных о доставке для экземпляров приложений

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

FirebaseMessaging.getInstance().setDeliveryMetricsExportToBigQuery(true);

Как включить экспорт данных о доставке для приложения

Если вы хотите включить экспорт на уровне приложения, не вызывайте метод setDeliveryMetricsExportToBigQuery и добавьте следующее свойство в объект приложения в манифесте приложения:

<application>
  <meta-data android:name="delivery_metrics_exported_to_big_query_enabled"
      android:value="true" />
</application>

Веб-приложение

FCM SDK для веб-приложений версии 12.14.0 или более поздней позволяет экспортировать данные о показе. По умолчанию экспорт данных отключен на уровне приложения. Если вы включите его программно на уровне экземпляра приложения, то сможете запрашивать у конечных пользователей разрешение на анализ данных о доставке сообщений (рекомендуется). Если заданы оба значения, то значение на уровне экземпляра приложения переопределяет значение на уровне приложения. Когда конечный пользователь дает согласие или отказывается от сбора данных, приложение должно установить экспериментальный флаг включения или отключения для каждого экземпляра приложения, как показано ниже.

// userConsent holds the decision of the user to give big query export consent.
const userConsent = ...;

const messaging = getMessagingInSw(app);

experimentalSetDeliveryMetricsExportedToBigQueryEnabled(messaging, userConsent);

Какие данные экспортируются в BigQuery?

Обратите внимание, что таргетинг на устаревшие токены или неактивные регистрации может привести к завышению некоторых из этих показателей.

Схема экспортируемой таблицы:

_PARTITIONTIME ВРЕМЕННЫЕ МЕТКИ Этот псевдостолбец содержит временную метку начала дня (в формате UTC), в который были загружены данные. Для раздела YYYYMMDD этот псевдостолбец содержит значение TIMESTAMP('YYYY-MM-DD').
event_timestamp ВРЕМЕННЫЕ МЕТКИ Временная метка события, зарегистрированная сервером.
project_number INTEGER Номер проекта, из которого было отправлено письмо.
message_id STRING Идентификатор сообщения позволяет определить сообщение. Идентификатор сообщения генерируется на основе идентификатора приложения и временной метки, поэтому в некоторых случаях он может быть не уникальным в глобальном масштабе.
instance_id STRING Уникальный идентификатор приложения, в которое отправлено сообщение (если доступен). Это может быть идентификатор экземпляра или идентификатор установки Firebase.
message_type STRING Тип сообщения. Это может быть уведомление или сообщение с данными. Тема используется для идентификации исходного сообщения для отправки темы или кампании. Последующие сообщения являются уведомлениями или сообщениями с данными.
sdk_platform STRING Платформа приложения получателя
app_name STRING Название пакета для приложений Android или идентификатор пакета для приложений iOS.
collapse_key STRING Ключ свертывания определяет группу сообщений, которые можно свернуть. Если устройство не подключено, в очередь на доставку помещается только последнее сообщение с определенным ключом collapse_key.
прием в приоритетном порядке INTEGER Приоритет сообщения. 5 – обычный приоритет, 10 – высокий.
ttl INTEGER Этот параметр определяет, как долго (в секундах) сообщение должно храниться в FCM, если устройство находится в офлайн-режиме.
тема STRING Название темы, в которую было отправлено сообщение (если применимо).
bulk_id INTEGER Идентификатор массовой рассылки позволяет определить группу связанных сообщений, например отправленных в рамках одной рассылки на определенную тему.
event STRING Тип события. Возможные значения:
  • MESSAGE_ACCEPTED – сообщение получено сервером FCM и запрос действителен;
  • MESSAGE_DELIVERED – сообщение доставлено в FCM SDK приложения на устройстве. По умолчанию это поле не распространяется. Чтобы включить ее, следуйте инструкциям в setDeliveryMetricsExportToBigQuery(boolean).
  • MISSING_REGISTRATIONS – запрос был отклонен из-за отсутствия регистрации.
  • UNAUTHORIZED_REGISTRATION – сообщение отклонено, поскольку отправитель не авторизован для отправки на регистрацию.
  • MESSAGE_RECEIVED_INTERNAL_ERROR – при обработке запроса на переписку произошла неизвестная ошибка;
  • MISMATCH_SENDER_ID – запрос на отправку сообщения отклонен из-за несоответствия между идентификатором отправителя, отправляющего сообщение, и идентификатором, объявленным для конечной точки.
  • QUOTA_EXCEEDED – запрос на отправку сообщения был отклонен из-за недостаточной квоты.
  • INVALID_REGISTRATION – запрос на отправку сообщения был отклонен из-за недопустимой регистрации.
  • INVALID_PACKAGE_NAME: запрос на отправку сообщения был отклонен из-за недопустимого названия пакета.
  • INVALID_APNS_CREDENTIAL – запрос на отправку сообщения был отклонен из-за недействительного сертификата APNS;
  • INVALID_PARAMETERS: запрос на отправку сообщения был отклонен из-за недопустимых параметров.
  • PAYLOAD_TOO_LARGE – запрос на отправку сообщения отклонен, поскольку полезная нагрузка превышает лимит.
  • AUTHENTICATION_ERROR – запрос на отправку сообщения отклонен из-за ошибки аутентификации (проверьте ключ API, используемый для отправки сообщения);
  • INVALID_TTL: запрос на отправку сообщения отклонен из-за недопустимого значения TTL.
analytics_label STRING При использовании HTTP v1 API ярлык аналитики можно задать при отправке сообщения, чтобы пометить его для целей аналитики.

Что можно делать с экспортированными данными

В следующих разделах приведены примеры запросов, которые можно выполнять в BigQuery к экспортированным данным FCM.

Количество отправленных сообщений по приложениям

SELECT app_name, COUNT(1)
FROM `project ID.firebase_messaging.data`
WHERE
  _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD')
  AND event = 'MESSAGE_ACCEPTED'
  AND message_id != ''
GROUP BY 1;

Подсчет уникальных экземпляров приложения, на которые настроен таргетинг сообщений

SELECT COUNT(DISTINCT instance_id)
FROM `project ID.firebase_messaging.data`
WHERE
  _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD')
  AND event = 'MESSAGE_ACCEPTED';

Количество отправленных уведомлений

SELECT COUNT(1)
FROM `project ID.firebase_messaging.data`
WHERE
  _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD')
  AND event = 'MESSAGE_ACCEPTED'
  AND message_type = 'DISPLAY_NOTIFICATION';

Количество отправленных сообщений с данными

SELECT COUNT(1)
FROM `project ID.firebase_messaging.data`
WHERE
  _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD')
  AND event = 'MESSAGE_ACCEPTED'
  AND message_type = 'DATA_MESSAGE';

Как подсчитать количество сообщений, отправленных в рамках темы или кампании

SELECT COUNT(1)
FROM `project ID.firebase_messaging.data`
WHERE
  _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD')
  AND event = 'MESSAGE_ACCEPTED'
  AND bulk_id = your bulk id AND message_id != '';

Чтобы отслеживать события для сообщения, отправленного в определенную тему, измените этот запрос, заменив AND message_id != '' на AND message_id = <your message id>;.

Как рассчитать продолжительность распространения для определенной темы или кампании

Время начала разветвления – это время получения исходного запроса, а время окончания – это время создания последнего отдельного сообщения, предназначенного для одного экземпляра.

SELECT
  TIMESTAMP_DIFF(
    end_timestamp, start_timestamp, MILLISECOND
  ) AS fanout_duration_ms,
  end_timestamp,
  start_timestamp
FROM (
    SELECT MAX(event_timestamp) AS end_timestamp
    FROM `project ID.firebase_messaging.data`
    WHERE
      _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD')
      AND event = 'MESSAGE_ACCEPTED'
      AND bulk_id = your bulk id
  ) sent
  CROSS JOIN (
    SELECT event_timestamp AS start_timestamp
    FROM `project ID.firebase_messaging.data`
    WHERE
      _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD')
      AND event = 'MESSAGE_ACCEPTED'
      AND bulk_id = your bulk id
      AND message_type = 'TOPIC'
  ) initial_message;

Процент доставленных писем

SELECT
  messages_sent,
  messages_delivered,
  messages_delivered / messages_sent * 100 AS percent_delivered
FROM (
    SELECT COUNT(DISTINCT CONCAT(message_id, instance_id)) AS messages_sent
    FROM `project ID.firebase_messaging.data`
    WHERE
      _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD')
      AND event = 'MESSAGE_ACCEPTED'
  ) sent
  CROSS JOIN (
    SELECT COUNT(DISTINCT CONCAT(message_id, instance_id)) AS messages_delivered
    FROM `project ID.firebase_messaging.data`
    WHERE
      _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD')
      AND (event = 'MESSAGE_DELIVERED'
      AND message_id
      IN (
        SELECT message_id FROM `project ID.firebase_messaging.data`
        WHERE
          _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD')
          AND event = 'MESSAGE_ACCEPTED'
        GROUP BY 1
      )
  ) delivered;

Отслеживать все события для заданного идентификатора сообщения и идентификатора экземпляра

SELECT *
FROM `project ID.firebase_messaging.data`
WHERE
    _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD')
    AND message_id = 'your message id'
    AND instance_id = 'your instance id'
ORDER BY event_timestamp;

Как рассчитать задержку для определенного идентификатора сообщения и идентификатора экземпляра

SELECT
  TIMESTAMP_DIFF(
    MAX(delivered_time), MIN(accepted_time), MILLISECOND
  ) AS latency_ms
FROM (
    SELECT event_timestamp AS accepted_time
    FROM `project ID.firebase_messaging.data`
    WHERE
      _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD')
      AND message_id = 'your message id'
      AND instance_id = 'your instance id'
      AND event = 'MESSAGE_ACCEPTED'
  ) sent
  CROSS JOIN (
    SELECT event_timestamp AS delivered_time
    FROM `project ID.firebase_messaging.data`
    WHERE
      _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD') AND
      message_id = 'your message id' AND instance_id = 'your instance id'
      AND (event = 'MESSAGE_DELIVERED'
  ) delivered;