zilencer: Log how long the remote server delayed the push notification.

This commit is contained in:
Alex Vandiver 2023-09-18 15:07:34 +00:00 committed by Tim Abbott
parent fd6091ad17
commit 4cef665e98
2 changed files with 117 additions and 23 deletions

View File

@ -11,6 +11,7 @@ from urllib import parse
import aioapns
import orjson
import responses
import time_machine
from django.conf import settings
from django.db import transaction
from django.db.models import F, Q
@ -428,6 +429,68 @@ class PushBouncerNotificationTest(BouncerTestCase):
remote=server,
)
def test_old_timestamp_format(self) -> None:
hamlet = self.example_user("hamlet")
RemotePushDeviceToken.objects.create(
kind=RemotePushDeviceToken.GCM,
token=hex_to_b64("aaaaaa"),
user_id=hamlet.id,
server=RemoteZulipServer.objects.get(uuid=self.server_uuid),
)
time_sent = now().replace(microsecond=0)
with time_machine.travel(time_sent):
message = Message(
sender=hamlet,
recipient=self.example_user("othello").recipient,
realm_id=hamlet.realm_id,
content="This is test content",
rendered_content="This is test content",
date_sent=now(),
sending_client=get_client("test"),
)
message.set_topic_name("Test topic")
message.save()
gcm_payload, gcm_options = get_message_payload_gcm(hamlet, message)
apns_payload = get_message_payload_apns(
hamlet, message, NotificationTriggers.DIRECT_MESSAGE
)
# Reconfigure like old versions, which had integer-granularity
# timestamps, and only in the GCM payload.
self.assertIsNotNone(gcm_payload.get("time"))
gcm_payload["time"] = int(gcm_payload["time"])
self.assertEqual(gcm_payload["time"], int(time_sent.timestamp()))
self.assertIsNotNone(apns_payload["custom"]["zulip"].get("time"))
del apns_payload["custom"]["zulip"]["time"]
payload = {
"user_id": hamlet.id,
"user_uuid": str(hamlet.uuid),
"gcm_payload": gcm_payload,
"apns_payload": apns_payload,
"gcm_options": gcm_options,
}
time_recieved = time_sent + datetime.timedelta(seconds=1, milliseconds=234)
with time_machine.travel(time_recieved, tick=False), mock.patch(
"zilencer.views.send_android_push_notification"
), mock.patch("zilencer.views.send_apple_push_notification"), self.assertLogs(
"zilencer.views", level="INFO"
) as logger:
result = self.uuid_post(
self.server_uuid,
"/api/v1/remotes/push/notify",
payload,
content_type="application/json",
)
self.assert_json_success(result)
self.assertEqual(
logger.output[0],
"INFO:zilencer.views:"
f"Remote queuing latency for 6cde5f7a-1f7e-4978-9716-49f69ebfc9fe:<id:{hamlet.id}><uuid:{hamlet.uuid}> "
"is 1 seconds",
)
def test_remote_push_unregister_all(self) -> None:
payload = self.get_generic_payload("register")
@ -1087,21 +1150,24 @@ class HandlePushNotificationTest(PushNotificationTest):
self.setup_apns_tokens()
self.setup_gcm_tokens()
message = self.get_message(
Recipient.PERSONAL,
type_id=self.personal_recipient_user.id,
realm_id=self.personal_recipient_user.realm_id,
)
UserMessage.objects.create(
user_profile=self.user_profile,
message=message,
)
time_sent = now()
with time_machine.travel(time_sent, tick=False):
message = self.get_message(
Recipient.PERSONAL,
type_id=self.personal_recipient_user.id,
realm_id=self.personal_recipient_user.realm_id,
)
UserMessage.objects.create(
user_profile=self.user_profile,
message=message,
)
time_recieved = time_sent + datetime.timedelta(seconds=1, milliseconds=234)
missed_message = {
"message_id": message.id,
"trigger": NotificationTriggers.DIRECT_MESSAGE,
}
with mock.patch(
with time_machine.travel(time_recieved, tick=False), mock.patch(
"zerver.lib.push_notifications.gcm_client"
) as mock_gcm, self.mock_apns() as (apns_context, send_notification), self.assertLogs(
"zerver.lib.push_notifications", level="INFO"
@ -1124,9 +1190,12 @@ class HandlePushNotificationTest(PushNotificationTest):
self.assertEqual(
views_logger.output,
[
"INFO:zilencer.views:"
f"Remote queuing latency for 6cde5f7a-1f7e-4978-9716-49f69ebfc9fe:<id:{self.user_profile.id}><uuid:{self.user_profile.uuid}> "
"is 1.234 seconds",
"INFO:zilencer.views:"
f"Sending mobile push notifications for remote user 6cde5f7a-1f7e-4978-9716-49f69ebfc9fe:<id:{self.user_profile.id}><uuid:{self.user_profile.uuid}>: "
f"{len(gcm_devices)} via FCM devices, {len(apns_devices)} via APNs devices"
f"{len(gcm_devices)} via FCM devices, {len(apns_devices)} via APNs devices",
],
)
for _, _, token in apns_devices:
@ -1148,21 +1217,24 @@ class HandlePushNotificationTest(PushNotificationTest):
self.setup_apns_tokens()
self.setup_gcm_tokens()
message = self.get_message(
Recipient.PERSONAL,
type_id=self.personal_recipient_user.id,
realm_id=self.personal_recipient_user.realm_id,
)
UserMessage.objects.create(
user_profile=self.user_profile,
message=message,
)
time_sent = now()
with time_machine.travel(time_sent, tick=False):
message = self.get_message(
Recipient.PERSONAL,
type_id=self.personal_recipient_user.id,
realm_id=self.personal_recipient_user.realm_id,
)
UserMessage.objects.create(
user_profile=self.user_profile,
message=message,
)
time_recieved = time_sent + datetime.timedelta(seconds=1, milliseconds=234)
missed_message = {
"message_id": message.id,
"trigger": NotificationTriggers.DIRECT_MESSAGE,
}
with mock.patch(
with time_machine.travel(time_recieved, tick=False), mock.patch(
"zerver.lib.push_notifications.gcm_client"
) as mock_gcm, self.mock_apns() as (apns_context, send_notification), self.assertLogs(
"zerver.lib.push_notifications", level="INFO"
@ -1184,9 +1256,12 @@ class HandlePushNotificationTest(PushNotificationTest):
self.assertEqual(
views_logger.output,
[
"INFO:zilencer.views:"
f"Remote queuing latency for 6cde5f7a-1f7e-4978-9716-49f69ebfc9fe:<id:{self.user_profile.id}><uuid:{self.user_profile.uuid}> "
"is 1.234 seconds",
"INFO:zilencer.views:"
f"Sending mobile push notifications for remote user 6cde5f7a-1f7e-4978-9716-49f69ebfc9fe:<id:{self.user_profile.id}><uuid:{self.user_profile.uuid}>: "
f"{len(gcm_devices)} via FCM devices, {len(apns_devices)} via APNs devices"
f"{len(gcm_devices)} via FCM devices, {len(apns_devices)} via APNs devices",
],
)
for _, _, token in apns_devices:

View File

@ -1,7 +1,7 @@
import datetime
import logging
from collections import Counter
from typing import Any, Dict, List, Optional, Type, TypeVar
from typing import Any, Dict, List, Optional, Type, TypeVar, Union
from uuid import UUID
import orjson
@ -317,6 +317,25 @@ def remote_server_notify_push(
if apple_devices and user_id is not None and user_uuid is not None:
apple_devices = delete_duplicate_registrations(apple_devices, server.id, user_id, user_uuid)
remote_queue_latency: Optional[str] = None
sent_time: Optional[Union[float, int]] = gcm_payload.get(
"time", apns_payload["custom"]["zulip"].get("time")
)
if sent_time is not None:
if isinstance(sent_time, int):
# The 'time' field only used to have whole-integer
# granularity, so if so we only report with
# whole-second granularity
remote_queue_latency = str(int(timezone_now().timestamp()) - sent_time)
else:
remote_queue_latency = f"{timezone_now().timestamp() - sent_time:.3f}"
logger.info(
"Remote queuing latency for %s:%s is %s seconds",
server.uuid,
user_identity,
remote_queue_latency,
)
logger.info(
"Sending mobile push notifications for remote user %s:%s: %s via FCM devices, %s via APNs devices",
server.uuid,