5.0 KiB
5.0 KiB
Логика доставки почты (Signed Messages v2)
Что отправляет клиент
- Клиент формирует пару подписанных блоков с одинаковой базой (
baseKey): incoming— сообщение для получателя (targetLogin = toLogin).outgoing— копия для отправителя (targetLogin = fromLogin).- Пара отправляется методом
ReceiveOutcomingMessage(старое имяSendMessagePairоставлено для совместимости).
Что делает сервер при ReceiveOutcomingMessage
- Валидирует поля запроса (
incomingBlobB64,outgoingBlobB64). - Разбирает оба блока и проверяет, что это корректная пара.
- Проверяет пользователей и криптоподписи по каждому блоку.
- Пытается сохранить обе записи одной транзакцией:
- либо добавляются обе записи,
- либо при дубле/конфликте не добавляется ни одна.
- Если пара реально добавилась в БД, сервер запускает realtime-доставку в активные сессии целевых пользователей.
- Если это дубль, дальнейшая доставка не выполняется (повтор не разгоняется).
Как сообщение доходит до клиента (WS + WebPush)
- После успешной записи сервер пытается доставить сообщение во все активные сессии
targetLogin. - Для каждой сессии сервер сначала создаёт/проверяет запись доставки в
signed_message_session_delivery(pending). - Если сессия онлайн, сервер шлёт
SignedMessageArrivedпо WebSocket. - Если сессия офлайн и тип сообщения входящий текст (
TYPE_INCOMING_TEXT), сервер пробует WebPush (если у сессии сохраненыendpoint/p256dh/auth). - При следующем логине/переподключении сервер дочитывает pending-сообщения и повторно отправляет их в эту сессию как backlog.
Подтверждение доставки (ACK)
- Используется метод
AckSessionDelivery. - Клиент отправляет ACK после обработки
SignedMessageArrivedсmessageKey. - Сервер помечает
(messageKey, sessionId)какdelivered=1, и это сообщение перестаёт быть pending для этой сессии.
Важно про безопасность ACK
AckSessionDeliveryтребует авторизованную WS-сессию (ctx.isAuthenticatedUser()).sessionIdберётся сервером из текущегоConnectionContext, а не из payload запроса.- Поэтому подтвердить доставку «просто зная messageKey/sessionId» без авторизованной сессии нельзя.
Почему допускаются дубли сети
- В модели с несколькими серверами возможны повторные пересылки одного и того же сообщения.
- Дедупликация делается на уровне БД по ключам записи.
- За счёт этого «шторм» затухает: сервер, который уже видел сообщение, больше его не разгоняет.
Будущая мультисерверная схема (цель)
- Клиент может отправить
ReceiveOutcomingMessageна любой из своих серверов. - Сервер-источник:
- сохраняет пару,
- рассылает исходящее по серверам пользователя A,
- рассылает входящее по серверам пользователя B.
- Остальные серверы повторяют тот же принцип, но благодаря дедупликации повторная пересылка быстро прекращается.
Важные текущие ограничения
- A) Реальной мультисерверности пока нет. Сейчас фактически предполагается один сервер на пользователя.
- B) Нет полноценного graceful shutdown для очереди пересылки. Возможен сценарий: запись уже сохранена в БД, но сервер перезагрузился до пересылки дальше. Это нужно доработать отдельно.