Ильинский Владислав

Этот репозиторий является систем дизайном для мессенджера WhatsApp.

Телеграм для связи

Методические указания

Содержание

1. Тема, аудитория, функционал

2. Расчет нагрузки

3. Глобальная балансировка

4. Локальная балансировка

5. и 6. Логическая и физическая схема базы данных

7. Алгоритмы

8. Технологии

9. Схема проекта

10. Обеспечение надежности

11. Расчет аппаратных ресурсов

Список источников

1. Тема, аудитория, функционал

Тема: мессенджер Whatsapp

MVP функционал

  1. Отправка сообщений в чате.
  2. Просмотр чата.
  3. Вложения фото, видео, файлы, голосовые.
  4. Поиск сообщений по чатам.
  5. Группировка чатов по папкам.
  6. Групповой чат с несколькими пользователями.
  7. Статус сообщения(отправлено, доставлено, прочитано)

Ключевые особенности

  • Эмоции на сообщения в виде смайликов.

  • Голосовые звонки внутри мессенджера

  • Проектировать будем с учетом отсутствия постоянного хранения сообщений на серверах. Т.е. сообщения могут храниться только пока адресат не доступен. После доставки сообщения удаляются с серверов. Можно хранить метаданные о статусе доставке, времени отправки, получения и т.д.

Исходные данные для расчетов

Метрика Значение
Аудитория Весь мир
MAU 482 млн
DAU 193 млн
Сообщений в день 23100 млн
Голосовых сообщений в день 1400 млн
Голосовые звонки минут в день 200 млн
Реакций на сообщения в день 1930 млн*
Сообщения с вложениями фото в день 320 млн*
Сообщения с вложениями видео в день 150 млн*
Сообщения с вложениями остальных файлов в день 30 млн*
Запросов на получение новых сообщений в день 4825 млн
Скачиваний за 2023 год 80 млн
Заявки в службу поддержки в день 1 млн*
Средний размер текстового сообщения 1/1024 Мбайт
Средний размер 1 мин голосового сообщения 1 Мбайт
Средний размер фото 4 Мбайт*
Средний размер видео 200 Мбайт*
Средний размер остальных файлов 10 Мбайт*
Средний размер 1 минуты звонка 0.72 Мбайт
Средний размер профиля 0.6 Мбайт
Средняя продолжительность голосового сообщения 1 мин
Средний размер голосового звонка 10 мин
Средний размер сессии 48 байт
Коэффициент пиковых нагрузок 1.5
Коэффициент запаса 2*
Коэффициент запаса для хранилища 1.2*

"*" - обозначены предполагаемые величины

Т.к. распределение по количеству пользователей довольно равномерное по странам [4] и регионам [6] не будем концентрироваться на определенный регион.

2. Расчет нагрузки

Результаты расчетов

Метрика RPS Пиковое RPS Пиковый RPS * коэф. запаса Трафик, Гб/с Трафик * коэф. запаса, Гб/с
Отправка текстовых сообщений 267 361 401 041 802 082 2 4
Отправка голосовых сообщений 16 204 24 306 48 612 1335 2 670
Запросы поставить реакцию 22338 33 507 67 014 0.001 0.002
Отправка сообщений с фото 3 704 5 556 11 112 116 232
Отправка сообщений с видео 1 736 2 604 5 208 2712 5 424
Отправка сообщений с остальными файлами 347 520 1 040 27 54
Запросы на создание голосового звонка 231 346 692 13 26
Запросов на получение новых сообщений 55 844 83 767 167 534 не учитываем не учитываем
Запросы на регистрацию 3 4.5 9 0.040 0.080
Запросы на проверку сессии 78 081 117 122 234 243 0.028 0.056
Запросы в службу поддержки 12 18 36 не учитываем не учитываем
Метрика хранилища Значение Значение * коэф запаса для хранилища
Трафик переходящий в файловое хранилище в день 13 258 Тбайт 15 910 Тбайт
Трафик переходящий в основное хранилище в день 6.5 Тбайт 7.8 Тбайт
Файловое хранилище за год 389.7 Пбайт 467.6 Пбайт
Основное хранилище за год 2.3 Пбайт 2.8 Пбайт
Рост файлового хранилища из-за новых пользователей в год 12.3 Пбайт в год 14.8 Пбайт в год
Рост основного хранилища из-за новых пользователей в год 2.7 Тбайт в год 3.3 Тбайт в год

Рассчитаем RPS

Я взял monthly active users(MAU) и количество сообщений в день Whatsapp за 2022 с сайта [1] и поделил их на 5.

Daily active users(DAU) я взял с учетом сайтов [2] и [3] приблизительно как 40% от MAU и поделил также на 5.

RPS запросов на отправку текстовых сообщений: 23100 млн в день (24 ч * 60 мин * 60 с) = 267361 запросов/сек

Количество голосовых сообщений в день взято с официального поста whatsapp и поделено на 5 [5]

RPS запросов на отправку голосовых сообщений: 1400 млн в день / (24 ч * 60 мин * 60 с) = 16204 запросов/сек

RPS запросов на поставку реакций: 1930 млн в день / (24 ч * 60 мин * 60 с) = 22338 запросов/сек

Только 2% сообщений используют вложения [8].

RPS запросов на отправку сообщений с фото: 320 млн в день / (24 д * 60 мин * 60 с) = 3704 запросов/сек

RPS запросов на отправку сообщений с видео: 150 млн в день / (24 д * 60 мин * 60 с) = 1736 запросов/сек

RPS запросов на отправку сообщений с остальными: 30 млн в день / (24 д * 60 мин * 60 с) = 347 запросов/сек

Количество голосовых и видео звонков есть здесь [6]. Но т.к. там упоминается и видео, и голосовых звонков поделим на 2, и поделил также на 5. Примем среднее время звонка 10 мин согласно [11]. Тогда кол-во запросов на создание звонка: 200 млн мин в день \ 10 мин = 20 млн в день

RPS запросов на создание звонка = 20 млн в день / (24 ч * 60 мин * 60 с) = 231 запрос в секунду

25 заходов во все мессенджеры в день в среднем на одного человека [14]. Тогда DAU * 25 = 193 млн * 25 = 4825 млн в день

RPS запросов на получение новых сообщений: 4825 млн в день / (24ч * 60м * 60с) = 55845 запросов/сек

Количество скачиваний взял за 2023 год и поделил на 5 [7]. Количество регистраций примем равным количеству скачиваний. Авторизации рассчитывать не будем, т.к. в WhatsApp авторизация постоянна.

RPS запросов на регистрацию: 80 млн в год / (365 д * 24ч * 60 мин * 60 с) = 3 запроса/сек

RPS запросов в службу поддержки: 1 млн в день / (24ч * 60м * 60с) = 12 запросов/сек

25% всех сообщений отправляется в промежуток с 12ч до 16ч

23100 млн * 0.25 / (4ч * 60м *60с) = 401042 запросов/сек

Примерно равномерно 75% всех сообщений отправлены с 12 по 24ч Около 18% всех отправлено сообщений с 8ч до 12ч см. рисунок 1. [8], [9]

RPS запросов проверки сессий: Все запросы исключая текстовые сообщения и реакции (т.к. по установленному websoket) (16 204 + 3 704 + 1 736 + 347 + 231 + 55 844 + 3 + 12) = 78 081 запросов/сек

Коэффициент пиковых нагрузок = пиковое RPS / среднее RPS = 401042 / 267361 = 1.5

Расчет трафика

Примерно 86% всех сообщений до 10 слов длинной [8]. 1 слово в английской в среднем состоит из 5 символов [12]. 4 байта максимально занимает символ в UTF8

Пусть средний размер текстового сообщения равным: 10 слов * 5 букв * 4 байта = 200 Байт

С учетом необходимости метаданных: времени отправки, статуса, id-ков и т.д. примем размер одного сообщения в 1 Кбайт

Трафик текстовых сообщений в день: 267 361 запросов/сек * 1 Кбайт * 8 / (1024^2) = 2 Гбит/с

Пусть средний размер голосового звонка 1 минута Примем средний размер голосового в 1 мин с битрейтом 128 Кбит\с в 1 Мбайт.

Трафик сообщений с голосовыми в день: 1.4 млрд в день * 1 Мбайт = 1335 Тбайт в день

Пусть одна реакция это константа 4 байт. Тогда трафик реакций в день: 22 338 RPS * 4 байт * 8 / (1024^3) = 0.001 Гбит/с

Особо данных по средним размерам вложений нет. Примем средний размер вложения фото 4 Мбайт, видео 200 Мбайт(10 мин 1080p), остальных файлов 10 мб.

Трафик сообщений с фото в день: 3704 RPS * 4 Мбайт * 8 / 1024 = 116 Гбит/с

Трафик сообщений с видео в день: 1736 RPS * 200 Мбайт * 8 / 1024 = 2712 Гбит/с

Трафик сообщений с остальными файлами в день: 347 RPS * 10 Мбайт * 8 / 1024 = 27 Гбит/с

Средний размер минуты голосового звонка 0.72 Мбайт [13]. Трафик голосовых звонков в день: 200 млн в день / (24 ч * 60м * 60с) * 0.72 Мбайт

  • 8 / 1024 = 13 Гбит/с

Запросы на получение не учитываем в трафике, т.к. основной там трафик, это сами сообщения, а их учли выше.

Пусть аватарка весит 0.5 Мбайта.

Пусть при регистрации загружается аватарка, номер телефона и имя. Номер телефона состоит из 12 символов и весит в UTF8 12 байт.

Пусть имя состоит из 30 символов в среднем и весит в UTF8 30*4 байт = 120 байт.

С учетом необходимости метаданных примем размер профиля 0.6 Мбайт.

Трафик регистраций: 9 RPS * 0.6 Мбайт * 8 / 1024 = 0.04 Гбит/с

Пусть сессия состоит из timeuuid пользователя 128 бит = 16 байт. Пусть токен сессии в два раза длиннее 32 байта. Тогда суммарно 48 байт.

Трафик проверки сессий 78 081 Запросов/сек * 48 байт * 8 / (1024^3) = 0.028 Гбит/с

Расчет хранилища

С учетом того, что примерно 32% всех сообщений прочитывается за минуту. 50% сообщений прочитывается за час [8]. Будем считать, что хранить нам необходимо только 30% общего объема в день. Оседать будет весь трафик голосовых сообщений, сообщений, вложений. Хранить будем на протяжении 1 месяца и только непрочитанные сообщения согласно MVP.

Трафик файлов переходящий в хранилище: (1335 + 116 + 2712 + 27)Гбит\с * 0.3 * (24ч * 60м * 60с) / (8 * 1024) = 13 258 Тбайт ежедневно

От хранения 70% файлов в течение дня:
(1335 + 116 + 2712 + 27)Гбит/с * 0.7 * (60мин * 60с) / (8 * 1024) = 1 289 Тбайт.

Трафик переходящий в хранилище в основной бд: (2 + 0.001 + 0.04) Гбит/с * 0.3 * (24ч * 60м * 60с) / (8 * 1024) = 6.5 Тбайт в день

В общем хранилище файлов в год: Т.к. храним файлы только 1 месяц.

13 258 Тбайт в день * 30 д / 1024 + 1 289 Тбайт = 389.7 Пбайт

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

6.5 Тбайт в день * 365 д / 1024 = 2.3 Пбайт в год

Рост хранилища файлов из-за новых пользователей: Дневной сумарный трафик файлов / DAU * Кол-во регистраций в год = 13 258 Тбайт в день / 193 млн в день * 80 млн в год / 1024 = 5.4 Пбайт в год

Рост основного хранилища из-за новых пользователей: Дневной сумарный трафик файлов / DAU * Кол-во регистраций в год = 6.5 Тбайт в день / 193 млн в день * 80 млн в год = 2.7 Тбайт в год

3. Глобальная балансировка

Распределение дата центров

Регион Расположение датацентров
Азия Нью-Делли(Индия), Джакарта(Индонезия)
Европа Лулео(Швеция), Стамбул(Турция)
Северная Америка Темпл(США, Техас), Калифорния)
Южная Америка Сан-Паулу(Бразилия)
Африка Лагос(Нигерия)
Регионы и страны Процент от общего числа MAU MAU
Asia 47.9 % 212 360 000
Europe 19.4 % 86 120 000
Africa 12.6 % 55 920 000
South America 11.2 % 49 520 000
North America 8.9 % 39 280 000
India 24.2 % 107 160 000
Brazil 6.3 % 27 860 000
United States 4.1 % 18 260 000
Indonesia 3.9 % 17 380 000
Mexico 3.1 % 13 940 000
Russia 3 % 13 340 000

World_MAU.jpg Рисунок 1. Расположение дата центров на глобальной карте MAU

В таблице ниже можно увидеть распределение аудитории по условным регионам и 6 стран топа по MAU, основанные на данных [4], посчитал в экселе [10]. Выбирал ориентируясь на расположение основного количества пользователей, карту морских кабелей [15] и карту расположения дата центров [16] и дата центров Meta*** в частности [17].

Взял 2 дата центра в Азии, ввиду географической протяженности, большого количества аудитории и из соображений отказоустойчивости. В Европе и Северной Америке возьмем тоже по 2 дата центра из-за географической протяженности, отказоустойчивости и более высоким требованиям к скорости работы приложения у аудитории в развитых странах. В Южной Америке возьмем 1 дата центр в Бразилии, т.к. в ней находится 6.3% MAU 27 млн пользователей. В Африке также поставим 1 дата центр, т.к. в общем регион вносит существенный вклад в MAU 12.6% 55.9 млн.

Азия:

  • Один в Нью-Делии(Индия) как в первой по численности MAU стране.
  • Один в Джакарте(Индонезия) тоже страна с существенным вкладом в MAU и для лучшего покрытия Тихоокеанского региона(Япония, Южная корея, Филипины ...)

Европа:

  • Один дата центр в Швеции город Лулео(Швеция), т.к. там расположен дата центр Facebook. Расстояние до Стокгольма порядка 1т км, что будет приводить к примерно 10мс дополнительной задержке, но общее время до остальных стран, входящий в покрытие этого дата центра приемлемо см. рисунок 2

RTT_Stockholm.png

Рисунок 2. Пинг из Стокгольма в другие города.

  • Второй дата центр решил расположить в Стамбуле(Турция) для дополнительного покрытия северных регионов Африки и азиатских стран на Аравийском полуострове(Саудовская Аравия, Иран, Израиль...) Пинг для стран покрывающих этим дата центров достаточный см. рисунок 3.

RTT_Istanbul.png

Рисунок 3. Пинг из Стамбула в другие города.

Северная Америка:

  • Один дата центр в Темпл(США, Техас), т.к. там расположен в реальной жизни дата центр Meta** и будет покрывать Мексику с существенной долей MAU 3.1% 14млн.
  • Второй расположим в Сан-Хосе(США, Калифорния) т.к. там есть центр расположения большого кол-ва дата центров и для лучшего покрытия западной части США.

Южная Америка:

  • Расположим дата центр в Сан-Паулу(Бразилия), т.к. там расположено скопление дата центров.

Африка:

  • Расположим дата центр в Лагосе (Нигерия), т.к. там расположены дата центры и для примерно центрального расположения на континенте.

Технологии

  1. Использовать будем Latency based DNS на уровне крупных регионов по типу континентов. Т.к. в общем случае метрика задержки более объективна чем географическая близость в Geo based DNS.
  2. Будем использовать BGP Anycast внутри регионов для более точной настройки распределения между дц нагрузки и возможности переадресовать пользователей в случае инцидентов из одного дц в другой.

4. Локальная балансировка

Для локальной балансировки будем использовать L7 уровень из-за более гибкой возможности настройки(выделения функциональных групп бекендов, более равномерного распределения нагрузки, обработки медленных клиентов).

Решено было использовать кластер прокси серверов реализующих логику балансировки на application сервера, ssl termination, кеширование, архивирование контента. Это решение было выбрано в пользу sidecar серверов для сокращения оверхеда на создание коннектов до application серверов, и поддержании их с постоянном разогнанном состоянии. Также в таком случае из-за уменьшения кол-ва ip доступных из вне, лучше будет кешироваться ssl session, т.к. с большей вероятностью клиент попадет на нужный ip, и реже будет оверхед на ssl handshake. Использование кластера балансинга также позволит уменьшить downtime приложения в случаях выкатки новых версий, падениях, зависаний бекендов благодаря более гибкой настройки механизмов балансировки.

На уровне балансировки между машинами в рамках кластера прокси серверов будем исползовать BGP Anycast.

Для балансировки межсервисного трафика будем использовать кластер API Gateway для изоляции логики роутинга.

5. и 6. Логическая и физическая схема базы данных

Посмотреть диаграмму полностью

designations_db

schema_backend_db

schema_client_db

Таблица База данных Primary Key Index
session Redis token -
permission Redis token -
message Cassandra K(chat_id, month_year). C↓ (id) -
reaction Cassandra K(chat_id, month_year). C↓ (id) -
view Cassandra K(message_id, month_year) -
call_history Cassandra K(chat_id, year) -
user Cassandra K(id) SAI (phone)
chat Cassandra K(id) -
folder Cassandra K(id) -
report_abuse Cassandra K(verdict_support_staff + month_year). C↓ (id) SAI (accused_user_id)
video_binary Amazon S3 url -
photo_binary Amazon S3 url -
files_binary Amazon S3 url -
voice_binary Amazon S3 url -
message SQLite id BI(chat_id, id), PI(value)
call_history SQLite id BI (id)
user SQLite id -
chat SQLite id -
folder SQLite id -
video_binary SQLite url BI(url)
photo_binary SQLite url BI(url)
files_binary SQLite url BI(url)
voice_binary SQLite url BI(url)

Бекенд

В Cassandra есть ключи шардирования или партиционирования (partition key обознач. K) и ключи кластеризации (clustering key обознач. C). Вместе они образуют primary key (обознач. pk). Partition key отвечает за разбиение по шардам в кластере (записи с одним parition key будут хранится на одном наборе машин). Clustering key (обознач. С ↑, ↓ по возрастанию и по убыванию соответственно) отвечает за порядок строк, в котором лежат на одной машине записи из одной таблицы. Storage-attached index (обознач. S) - это индекс в Cassandra он хранится совместно с данными, использует смещения для определения положения нужных данных. В целом в рамках Cassandra используется в большей степени подход с малым количеством индексов и проектированием схемы исходя из запросов. Приведу основные запросы к таблицам в Cassandra для объяснения причин выбора тех или иных ключей и индексов. В Cassandra нет внешних связей как в реалиционных базах, но все же будет указывать их на схеме для наглядности.

message:

  1. Получение новых собщений в чате SELECT * FROM message WHERE chat_id=? AND month_year=? AND id > ?
  2. Получение\измененине конкретного message SELECT * FROM message WHERE chat_id=? AND month_year=? AND id = ?
  3. Удалить сообщение в чате по id UPDATE message SET value='' AND status=deleted AND attachments={} WHERE chat_id = ? AND month_year = ? AND id = ?
  4. Добавить сообщение INSERT * TO message(id, -//-)

C↓ - id. Используется тип timeuuid, содержащий внутри дату создания сообщения.

K - chat_id + month_year. month_year - это месяц и год создания сообщения. Вместе с id чата они позволяют ограничивать рост шарда и обеспечивают получение сообщений из чата.

views:

  1. Получить просмотры по id сообщений? SELECT * FROM view WHERE message_id IN () AND month_year = ?
  2. Добавить просмотр в список UPDATE views SET users_viewed = users_viewed + [?] WHERE message_id = ? AND month_year = ?

K - message_id + month_year. month_year вместе с id чата они позволяют ограничивать рост шарда и обеспечивают получение просмотров.

reaction:

  1. Получить все новые реакции в чате SELECT * FROM reaction WHERE chat_id = ? AND id > ?
  2. Убрать реакцию с сообщения по id реакции UPDATE message SET deleted=true WHERE id = ?
  3. Добавить реакцию INSERT * TO reaction(id, -//-)

C↓ - id. Используется тип timeuuid, содержащий внутри дату создания реакции.

K - chat_id + month_year. month_year вместе с id чата они позволяют ограничивать рост шарда и обеспечивают получение реакций в чате.

user:

  1. Получить user по телефону SELECT * FROM user WHERE phone = ?
  2. Получить пользователя по id SELECT * FROM user WHERE id = ?
  3. Добавить пользователя INSERT * TO user(id, -//-)
  4. Добавить settings, avatar_url, UPDATE user SET settings = ? WHERE id = ?;

K - id. Использовал такой, т.к. в запросах почти всегда мы используем фильтрацию по пользователю.

S - phone. Т.к. запрос пользователя по номеру телефона в связи с редкой регистрацией тоже будет выполняться редко решено использовать индекс, а не отдельную таблицу, чтобы не дублировать данные. Использовать составной ключ нельзя было, т.к. в некоторых запросах мы не знаем номер телефона, это бы усложнило бы реализацию.

chat:

  1. Добавить пользователя в чат UPDATE chat SET users_id = users_id + {?} WHERE id = ?
  2. Получить чат по id SELECT * FROM chat WHERE id = ?
  3. Добавить чат INSERT TO chat(id, ...)

K - id тип timeuuid. Кол-во чатов относительно не большое число, поэтому разбиение на шарды по id чата вполне себе работает.

call_history:

  1. Получить историю звонков с определенного момента для одного пользователя SELECT * FROM call_history WHERE chat_id IN (?,?,...) AND year = ? AND id > ?
  2. Получить новые звонки в чате SELECT * FROM call_history WHERE chat_id = ? AND year = ? AND id > ?
  3. Добавить звонок INSERT * TO call_history(id, ...)

C↓ - id. Используется тип timeuuid, содержащий внутри дату создания реакции.

K - chat_id + year. year - в каком году было создано сообщение. Используем такое поле без месяца т.к. кол-во звонков на 3 порядка меньше количества сообщений, т.е. данных на шарде будет меньше. Вместе они позволяют ограничивать рост шарда и обеспечивают получение истории звонков.

folder:

  1. Добавить чат в папку UPDATE folder SET chats_id = chats_id + {?} WHERE id = ?
  2. Добавить папку с чатами INSERT TO folder(id, ...)

K - id. Количество папок также не очень велико поэтому используем его как ключ шардинга.

report_abuse:

  1. Получить все новые, не просмотренные отчеты SELECT * FROM report_abuse WHERE verdict_support_staff = 0 ? AND month_year = ? AND id > ?
  2. Поставить/изменить вердикт на отчет по id отчета UPDATE report_abuse SET verdict_support_staff = ? WHERE id = ? AND month_year = ? AND verdict_support_staff IN (...)
  3. Поставить вердикт мл модели на отчет по id отчета UPDATE report_abuse SET verdict_ml_model = ? WHERE verdict_support_staff = 0 AND month_year = ? AND id = ?
  4. Посмотреть все отчеты по пользователю SELECT * FROM report_abuse WHERE accused_user_id = ? AND verdict_support_staff IN (...)

K - verdict_support_staff + month_year - для ограничения роста шарда.

C↓ - id - используется тип timeuuid, содержащий внутри дату создания отчета.

S - accused_user_id - индекс для быстрого поиска отчетов по пользователю. Использовать составной ключ нельзя было, т.к. в запросе 1 не известны пользователи. Не выносил в отдельную таблицу, чтобы не дублировать данные, ввиду более редкого использования.

Клиент

В sqllite нет массивов, для представления массивов можно хранить либо смежные таблицы, либо массив в виде json-а. Я использовал массивы json, т.к. эта информация мне нужна только внутри сущностей, снаружи ни при каких сценариях.

Обозначения: pk - primary key; BI - btree index; PI - полнотекстовый индекс;

Индексы по таблицам:

message:

PI - value полнотекстовый индекс для поиска по сообщениям.

BI - chat_id + id(timeuuid) для получения отсортированного по времени списка сообщений в чате.

call_history:

BI - id(timeuuid) индекс для получения в отсортированном виде истории звонков.

photo_binary, video_binary, file_binary, voice_binary:

BI - url индексы для возможности быстрого получения файла по ключу.

7. Алгоритмы

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

Алгоритмы будут применяться для контента, на который пожаловались другие пользователи. Задачи будут с помощью cron job доставаться из основного хранилища, далее батчами поступать в Kafka, мл сервис будет их пулить и обрабатывать (детектировать и оценивать): пропустить, передать в службу поддержки или сразу наложить ограничения. Т.к. подразумевается, что ml support будет достаточно долго обрабатывать задачи, результат их будем сначала записывать в Kafka, а уже reports_verdicts_worker будет класть их в основное хранилище и в Kafka события наложения ограничений. Для подробностей см. схему Для обучения моделей будет использоваться Apache Spark ввиду высокой производительности распределенных алгоритмов.

  • Для классификации текста и содержимого файлов может быть использованы модели на основе BERT - предобученная модель архитектуры трансформер. BERT имеет механизм двухстороннего влияния, что позволяет анализировать контекст с двух сторон и лучше передавать смысловую нагрузку слова, преимуществом перед GPT моделями является меньшее количество параметров, имеет в целом более простой процесс обучения.
  • Для классификации изображений и видео могут быть использованы модели на основе CNN для предварительной обработки и рекуррентные нейронные сети для классификации. В качестве примера в исследовании [18] лучшие результаты c точность 95.66% показала модель EfficientNet-B7 + BiLSTM с обучающей выборкой 80% и 20% тестовой выборкой на датасете 111156 видео из YouTube.
  • Также будут использованы классические методы модерации с использованием жалоб от пользователей и последующей проверкой службами поддержки.

За основу схемы обучения и доставки до продакшена была взята схема MLOps билайна [19].

Посмотреть диаграмму пайплайна обучения и выкатки в продакшен.

Data Lake - это хранилище, где хранятся разнородные данные необходимые для обучения.

Data Marts - это уже хранилище для фич, признаков моделей, параметров, результатов экспериментов, чтобы можно было воспроизвести их и переиспользовать при необходимости.

Шаблоны проектов - это шаблоны для создания новых проектов, включающие в себя базовую структуру проекта, CI/CD, линтеры и т.д. Стандартизация технической стороны позволяет мл инженерам акцентировать внимание в большей степени на исследовательской части, и помогает разворачивать новые проекты.

Код обучения моделей нужен, чтобы иметь возможность просто дообучать модели в случае, если нам понадобится в процессе использования модифицировать модели.

pipeline_ml_training_and_delivery

Алгоритм улучшения качества голосовых звонков Для шумоподавления будем использовать мл модель на основе архитектуры Dual-Signal Transformation LSTM Network подобные модели подходят для использования в real time, т.к. не большие по размеру, имеют высокую скорость обработки и достаточно хорошее качество. Опирался на статью [20].

  • Алгоритмы удаления сообщений после прочтения
  1. В момент получения статуса сообщения доставлено, status worker, кладет в Kafka chat_id + message_id.
  2. removing_attachments микросервис делает запрос в Cassandra и читает в оперативную память вложения для сообщений.
  3. removing_attachments микросервис делает запрос на удаление вложений из Amazon S3.
  4. removing_attachments микросервис делает запрос на зануление соответствующих полей в Cassandra.
  • Алгоритмы удаления сообщений после того, как из не прочитали за год
  1. Настроим автоматическое удаление файлов из Amazon S3 через 1 год.
  2. Удалять из Cassandra не будем, т.к. это бы генерировало много нагрузки, просто не будем их показывать, если прошел срок.
  • Алгоритм голосовых звонков

Для реализации звонков будем использовать WebRTC технологию, которая доступна в браузере, iOS, Android. Для обхода Nat окружения будем использовать Stun, которые работают в случае если у пользователей не симметричный Nat и Turn сервера, который требует проксировать через себя запросы, но позволяет обойти двухсторонний симметричный Nat.

  1. Инициализация звонка одним клиентом через Websocket.
  2. Отсылается уведомление второму клиенту.
  3. Проверка возможности установления соединения между клиентами при помощи Stun сервера.
  4. При принятии поиск лучшего Turn сервера по latency
  5. По определенным публичным ip клиентов устанавливается соединение через Turn сервер.
  6. По завершению/Отклонению звонка клиенты присылают информацию о звонке на сервер используя Websocket.
  • Алгоритм загрузки файлов
  1. Сохранение в локальной файловой системе.
  2. Определение разрешен ли формат.
  3. Сжатие (только для видео). Видео будет использоваться с форматом mp4 с кодеком H.264 ввиду его широкой поддержки, несмотря на более низкий уровень сжатия, например по сравнению с H.265. Этот кодек основан на субъективности человеческих органов чувств. Используется цветовая субдискретизация, т.е. уменьшения разрешения цветовой части изображения. Дискретное косинусное преобразование с отбрасыванием части гармоник (четкости изображения) ради сжатия. Также применяются алгоритмы, основанные на временной избыточности (разбиение на части кадров с последующим вычислением векторов перемещений). На последнем этапе применяются алгоритмы обратимого сжатия (статистические или словарные).
  4. Отправка в Amazon S3.
  5. Сохранения сведений о файле в бд.
  • Алгоритмы шифрования

В WhatsApp используется Signal Protocol, который основан на асинхронном шифровании, X3DH ("Extended Triple Diffie-Hellman"), Double Ratchet Algorithm. X3DH используется для подтверждения личностей отправителя и получателя между собой, и обмена временными ключами. На следующей стадии с помощью Double Ratchet Algorithm сообщение прогоняется через KDF цепочку (расшифрованное одно сообщение не позволяет расшифровать сообщение из прошлого) а с помощью параметра, который является входным для KDF алгоритма и уникальным для каждого сообщения реализуется защита от расшифровки будущих сообщений.

  • Алгоритм поиска по сообщениям на клиенте

Создадим индекс FTS5 в SQLite для полнотекстового поиска, внутри он токенизирует по символам пробелов и пунктуации согласно unicode6.1. Сама структура индекса основана на b-tree деревьях. В конечном итоге поиск будет осуществляться по совпадению и по префиксу.

8. Технологии

Технология Сфера применения Мотивация
Golang Backend Основной язык для бекенда. Используем ввиду его хорошего соотношения производительности, поддерживаемости и кол-ва существующих инструментов. Также есть современные механизмы асинхронности из коробки.
Redis Backend in-memory хранилище со встроенным шардированием.
Cassandra Backend Основное хранилище на бекенде, используем ввиду встроенной возможности шардирования и высокой производительности.
Amazon S3 Backend Объектное хранилище для пользовательских файлов. Используем ввиду удобства, масштабируемости, доступности.
Nginx Backend Высокопроизводительное, многофункциональное, распространенное решение для прокси серверов.
Kafka Backend Высокопроизводительное, масштабируемое, отказоустойчивое решения для межсервисного взаимодействия. Обработка просмотров сообщений, очередь для удаления вложений.
Cron Backend Демон на linux для выполнения отложенных задач. Будет использоваться для запуска обработки мл микросервисом жалоб в службе поддержки.
Prometheus + Victoria Metrics + Grafana Backend Система для сбора, отображения метрик для мониторинга и алертинга.
Git-lab Backend Git-hub и git-lab системы контроля версий схожи по функционалу, но git-lab позволяет развернуть хранилища на собственной инфраструктуре, что дает больше контроля.
etcd Backend Распределенное хранение конфигов.
Jaeger Backend Система трассировки для отлаживания проблем в микросервисной архитектуре.
Docker Backend Средство виртуализации, позволяющее унифицировать окружение для разработки, упростить доставку, развертывание и тестирование.
Kubernetes Backend Автоматизация развертывания, масштабирования и координации контейнеров.
Apache Spark Backend Используется для задач реализации распределенных алгоритмов машинного обучения.
Signal Protocol Backend & Client Протокол для шифрования был реализован Singal, Facebook мессенджерами и WhatApp. Плюсом является защита от компрометирования предыдущих и следующих сообщений при утечки текущего ключа
WebRTC Backend & Client Технология, позволяющая реализовать голосовые звонки, поддерживается в браузерах, android, ios.
Websocket Backend & Client Позволяет держать постоянное соединение, отсылать сообщения и уведомления о событиях только при их появлении, без дополнительных запросов.
React Client Фреймворк от Meta** является широкоиспользуемым.
sql.js Web Client Библиотека для использования SQLite в браузере, нужна для унификации способов хранения на различный клиентах.
SQLite iOS и Android Client Используем локальное персистентное хранилище на клиенте, т.к. реализуем отсутствие хранения сообщений на сервере.
React Native iOS и Android Фреймворк на базе React для возможности писать кросплатформенные приложения. Позволит ускорить разработку в рамках общей экосистемы.

9. Схема проекта

Посмотреть схему полностью

designations_full_schema

full_schema_top

full_schema_buttom

Будем использовать API Gateway - Backend for frontend для изолированности логики различных клиентов и для централизованной точки роутинга. Также будем микросервисы по бизнес слоям для изоляции логики и стандартизации взаимодействия микросервисов.

Сервис chat - основной сервис, реализует логику сообщений в чатах, обработку событий.

Attachment - микросервисы будут использоваться для реализиции пайплайна загрузки, вложений, используя Amazon S3.

Chat and events - микросервисы для обработки событий в чатах, отправки сообщений, просмотров, реакций. Эти сервисы шлют в Kafka сообщения, которые обрабатываются соответствующими воркерами. Воркеры в свою очередь пишут в кафку события, которые потом должны обработать сервисы chat and events (отправка получателю сообщения, и т.д.). Chat and events отправляют сообщения и события пользователям через websoket.

Для удаления вложений сервис status worker будет слать в кафку сообщения, какие chat_id + message_id можно уже удалять, а сервис removing attachment будет удалять их, подробнее описано в секции алгоритмов.

Auxiliary - микросервисы для работы с редко меняющимися таблицами: chat, folder.

Сервис user отвечает за работу с пользователями.

Сервис auth отвечать за авторизацию пользователей, хранение их прав.

Сервис call будет осуществлять соединение голосовых звонков Websoсket, а также содержать Stun и Turn сервера для соединения p2p клиентов между собой. Также на Turn серверах будет оптимизироваться обработка звонков с большим количеством пользователей, например объединятся дорожки.

Ml voice сервис используется для обработки методами машинного обучения в реалтайме звука с целью его улучшения. Также используется Kafka для пересылки событий в сервис чата, для уведомления пользователей через чат микросервис, держащий соединение websoket. По истечению таймаута, если второй пользователь не открыл websoket соединение с микросервисом call, то мы делаем вывод, что не дозвонились и уже отсылаем событие пропушенного звонка.

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

Cервис support нужен для работы с жалобами пользователей, их обработкой мл моделями и службой поддержки.

Через support микросервис проходят все записи в бд Cassandra, чтобы разграничить логику и контролировать трафик в одном месте.

Доставляться сообщения до ml сервиса будут через Kafka, в которую будут записываться подготовленные сообщения из микросервиса auxiliary запушенные по cron job-e. Результат работы ml сервиса также будет записываться в Kafka, с последующей обработкой reports verdicts worker. Он через support микросервис записывает вердикты в Cassandra и в Kafka события наложения ограничений.

10. Обеспечение надежности

Cassandra

  • Т.к. это основное хранилище будем использовать репликацию с replication factor (RF) 4. Три реплики будем располагать в одном дц, а 4-ую в другом.
  • Уровень согласованности QUORUM, т.е. дожидаемся ответа от (N/2) + 1 = 3 реплик, как правило тех, которые находятся с одном дц.

Redis

  • Для таблицы session Redis Data Base (RDB) уровень персистентности с созданием бекапов каждый час, две реплики в том же дц, и 1 в другом.
  • Для таблицы permission_and_limitation будем использовать Append Only File (AOF), fsync always (с записью всех изменений в лог), т.к. она более требовательная к потери данных. Две реплики в том же дц, и одна в другом.

Kafka

  • Будем использовать для всех топиков replication.factor = 3.

Для пути записи сообщения в Cassandra (chat and events -> Kafka -> Cassanra) будем реализовывать гарантию доставки Exactly Once:

  1. ack=all - дожидаемся для производителя (chat and events микросервис) ответа от всех реплик, что сообщение записано.
  2. enable.idempotence=true - гарантирует, что сообщения, полученные от одного производителя (с одинаковым producer.id) в результате retry-ов не будут дублироваться.
  3. использование api transaction producer - гарантия того, что разные производители не смогут записать сообщение дважды, т.к. в случае тех или иных сбоев транзакция будет откатываться.
  4. isolation_level=read_commited - для гарантии чтения только закомиченных сообщений на стороне потребителя (message worker микросервис).
  5. генерация id timeuuid для сообщений на клиенте, чтобы в системе на уровне потребителя уже не получилось записать в бд Cassandra одинакового сообщения.
  6. retry-и на производителе в случае ошибок, которые возможно решить с помощью них (retriable_errors) пример такой ошибки - LEADER_NOT_AVAILABLE. \

Для пути записи заявок в службу поддержки будем также реализовывать гарантию доставки Exactly Once по схожим принцип, так что подробно расписывать не будем.

Для остальных топиков не имеем таких жестких требований, можем потерять некоторые события или некоторые события сохранить несколько раз, поэтому будем реализовывать at least once гарантию доставки:

  1. ack=1 - дожидаемся ответа от одной реплики, что сообщение записано, что достаточно производительно, но оставляет вероятность потерять события, например в случае, когда ведомые ноды не успели реплицировать данные, а лидер нода упала, не записав на диск данные.
  2. retry-и на производителе в случае ошибок, которые возможно решить с помощью них (retriable_errors) пример такой ошибки - LEADER_NOT_AVAILABLE.
  3. на уровне клиентских приложений (web client, mobile client) удалять дубликаты данных, чтобы не показывать их пользователю.

Дата центры

  • Резервирование электропитания, линий связи, охлаждения.
  • Использование аварийных дизель генераторов для возможности реализации Graceful Shutdown.

Сервисы

  • Резервирование ресурсов (CPU,RAM), физических компонентов (сервера, диски...).
  • Для дисков для баз данных будем использовать RAID 6, для повышения отказоустойчивости, плюс чтобы при отказе одного диска система продолжала работать.

Подходы к разработке и инфраструктуре

  • Различные виды тестирования (модульные, интеграционные, нагрузочные, ручные ...)
  • Сode review
  • CI/CD с прогонами тестов и линтеров.
  • Проведение учений, с отключением одного дц, для проверки работоспособности.
  • Уменьшение области отказа за счет расположения сервисов в различных зонах(стойка, ряд, зал, дц).
  • Graceful degradation:

По ходу работы в сервисе звонков мы используем мл модель для обработки голоса с целью подавления шумов. Мы измеряем время, за которое отрабатывают мл сервисы запросы, если оно превосходит пороговое значение, то turn сервер делает запрос в микросервис call, чтобы он по websoket отправил событие включения стандартного механизма шумоподавления WebRTC. Ухудшаем работу, но не отказываем совсем в обслуживании.

  • Graceful shutdown:

На уровне приложений будем обрабатывать сигнал SIGTERM и реализовывать Graceful Shutdown (останавливать обработку новых клиентов, обрабатывать старых, освобождать ресурсы).

При развертывании в Kubernetes по-умолчанию период после отправки SIGTERM, который будет ждать Kuber до отправки SIGKILL будет 30 секунд. Так как нам нужно дождаться удаления данных из Endpoint и компонентов по типу kube-proxy, ingress controller, чтобы не направлять трафик на не рабочие поды, мы можем ожидать, скажем 15 сек, перед тем как реализовывать Graceful Shutdown в наших приложениях не использующих долгоживущие соединения и не занимающихся долгой обработкой.

Для компонентов, на код которых мы не можем повлиять, можно использовать preStop хуку с таймером.

Для компонентов, использующих долгоживущие соединения (Websoket в chat, call микросервисах) или занимающиеся долгой обработкой (attachment, ml support микросервисы) Graceful Shutdown будем реализовывать за счет горизонтального автоматического масштабирования основываясь на данных метрик из Prometheus.

  • Использование различных методов обработки проблем на сервисах:

Retry: ретраить будем на API Gateway. Используем подход с 3-мя ретраями exponential backoff (увеличение по экспоненте времени между попытками) jitter (внесение рандомного смещения) для уменьшения вероятности синхронного повторения ошибок, retry budget (общее ограничение кол-ва попыток как процент от успешно выполненных запросов) для уменьшения кол-ва ретраев в случае аварий, чтобы не генерировать лишнюю нагрузку на сервисы. Не забываем ретраить только подходящие ошибки (например не ретраим 4xx), мониторить кол-во ретраев, следить за идемпотентностью запросов.

Timeout: будем использовать таймауты по 99 процентилю времени запросов, как отправная точка. Придерживаться стратегии deadline propagation, где это возможно, чтобы не нагружать лишний раз сервисы, которые уже не успели обработать запрос.

Rate limiting: на уровне API Gateway будем использовать burst rage limit с фиксированным окном, как первоначальное довольно простое решение.

Circuit breaker: будем использовать на уровне языка go (можем например использовать [21]) в turn сервере в сервисе call, чтобы реализовать graceful degradation при большом проценте ошибок или долгих ответах, с переключением на стандартные алгоритмы обработки голоса.

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

Асинхронные паттерны

  • Паттерн CQRS (command and query responsibility segregation) - используется для обработки частых запросов (message, reaction, status, view, call_history) см. схему проекта. Используется для возможности дозировать нагрузку на запись.
  • Паттерн Event-driven - используется для обработки событий звонков (начало, неудачный звонок), для обработки событий, требующих отправки пользователю событий(поступление сообщения, реакция, просмотр), событий наложения ограничений на пользователей, также используется в service support для повышения надежности работы с обработки ml support заявок в службу поддержки. Нужен для поддержки консистентности данных в различных бд и как временное персистентное хранилище для последующей обработки событий.

Наблюдаемость

  • Будем использовать логирование, трассировку, мониторинг, алертинг, профилирование. Для понимания текущего состояния системы и анализа инцидентов.

11. Расчет аппаратных ресурсов

Сервис Целевой RPS CPU RAM Гб Net Гбит/с
web && mobile nginx (суммарно) RPS 869 096 и 234 243 CPS 548 5 8410.138
web && mobile api gateway (суммарно) 1 103 339 482 - 8410.138
auth.auth 234 243 59 0.86 0.056
auth.limitation_worker 36 1 15 0.00005
user.user 9 1 15 0.000009
chat.chat 935 068 10 864 1 592 4.002
chat.attachments 84 374 8 247 1 238 8 380
chat.removing_message 561 457 7 019 1 029 4
chat.message, chat.view, chat.status workers (на каждый) 802 082 201 3 4
chat.reaction worker 67 014 17 0.3 не учитываем
call.call, stun&&turn 692 9 1.4 26 (только на stun&&turn)
support.support 36 1 0.2 0.3

Расчет сервисов

через web&&mobile nginx и web&&mobile api gateway будут проходить все запросы. Общий RPC для запросов, требующих нового tcp handshake (CPS) = 48 612 + 11 112 + 5 208 + 1 040 + 692 + 167 534 + 9 + 36 = 234 243 запросов в секунду.

Общий RPC для запросов, переиспользующих tcp соединение через websoket = 802 082 + 67 014 = 869 096

Примем средний размер запроса 100Кб с учетом файлов. Из бенчмарков nginx [22] для https 1 ядро = 428 CPS (10 Мб RAM) 1 ядро = 4830 RPS (100 Мб RAM) без установления tcp соединения каждый раз.

Посчитаем кол-во ядер требующихся для обработки запросов: CPS = 234 243 / 428 = 548 ядер RPS = 869 096 / 4830 = 180 ядер Выбираем большее - 548 ядер.

Суммарный трафик = 4 + 2 670 + 232 + 0.002 + 5 424 + 54 + 26 + 0.080 + 0.056 = 8410.138 Гбит/с Исходя из трафика используя 100 Гбит/с сеть = 85 сетевых карт.

Т.к. на api gateway уже будет поступать http трафик, плюс tcp коннекты будут переиспользоваться, то судя по бенчмаркам nginx [22] 8 ядер обрабатывают 91 640 RPS или 1 ядро = 11 455 RPS

Предположим envoy api gateway в 5 раз менее производительный чем nginx, т.к. имеет более сложную логику Тогда нам нужно (869 096 + 234 243) * 5 / 11 455 = 482 ядра.

Т.к. производительности веб фреймворков go и C++ схожи согласно [23]. А реальная производительности и потребление RAM сильно зависит от реализации приложения, предположим, что Go в 1.25 раз требовательнее к CPU чем С++ и в 1.5 раз требовательнее к RAM. Тогда таблица на основе методических указаний потребления ресурсов на ядро [24].

Характер сервиса RPS RAM
Тяжелая бизнес логика 8 150 МБ
Средняя бизнес логика 80 150 МБ
легкое JSON API 4000 15 МБ

auth.auth: считаем легким JSON API. CPU = (234 243) / 4000 = 59 ядер; RAM = 59 * 15 Мб = 0.86 Гб; Net = 0.056 Гбит/с.

auth.limitation_worker: считаем легким JSON API. Пусть максимально запрос занимает 1 Кбайт. CPU = 36 / 4000 = 1; RAM = 1 * 15 МБ = 0.01 Гб; Net = 1 Кбайт * 36 = 0.00005 Гбит/с.

user.user: считаем легким JSON API. Пусть максимально запрос занимает 1 Кбайт. CPU = 9 / 4000 = 1; RAM = 1 * 15 МБ = 0.01 Гб; Net = 1 Кбайт * 9 = 0.000009 Мбит/с.

chat.chat: считаем средней бизнес логикой CPU = (802 082 + 67 014) / 80 = 10 864 ядер; RAM = 10 864 * 150 МБ = 1 592 Гб; Net = 4 + 0.002 = 4.002 Гбит/с.

chat.attachments: считаем тяжелой бизнес логикой CPU = (48 612 + 11 112 + 5 208 + 1 040) / 8 = 8 247 ядер; RAM = 8 247 * 150 МБ = 1 238 Гб; Net = (2 670 + 232 + 5 424 + 54) = 8 380 Гбит/с.

chat.removing_message: считаем средней бизнес логикой CPU = (802 082 * 70 %) / 80 = 7019 ядер; RAM = 7018 * 150 МБ = 1 029 Гб; Net = 4 Гбит/с.

chat.message, chat.view, chat.status workers: считаем легким JSON API CPU = 802 082 / 4000 = 201 ядер; RAM = 201 * 15 МБ = 3 Гб; Net = 4 Гбит/с.

chat.reaction worker: считаем легким JSON API CPU = 67 014 / 4000 = 17 ядер; RAM = 17 * 15 МБ = 0.3 Гб; Net - не учитываем.

call.call, stun&&turn: считаем средней бизнес логикой CPU = 692 / 80 = 9 ядер; RAM = 9 * 150 МБ = 1.4 Гб; Net = 26 Гбит/с. (только на stun&&turn)

support.support: считаем средней бизнес логикой CPU = 36 / 80 = 1 ядро; RAM = 1 * 150 МБ = 0.2 Гб; Net = 1Кб * 36 = 0.3 Мбит/с.

Расчет хранилищ данных

Основные р-ты объемов хранения были посчитаны в разделе 2

Amazon S3: Используем тариф Amazon S3 Glacier Instant Retrieval - хранилище для редко используемых архивных данных с быстрым доступом (в миллисекундах).

В месяц будем иметь объем: (467.6 + 14.8)Пбайт / 12 месяцев * 1024 = 41 164 Тбайт в месяц

Согласно калькулятору от Amazon [25] для азиатского региона сумма составит 210 762 $ в месяц.

Cassandra: C учетом физической схемы рассчитаем более точно хранилище.

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

Рассчитаем размер message: 5 uuid + 8 UTF8 (month_year) + 1 Кбайт (value) + 4 байта (status) + 8 байт (updated_at) + 1 Байт (limitation) = 5 * 16 + 8 * 4 + 1024 + 4 + 8 + 1 = 1 149 Байт

Net: 1 149 Байт * 802 082 RPS * 8 / (1024^3) = 6.9 Гбит/c

За год: (1 149 Байт - 1 Кб * 70%) * 802 084 RPS * (365д * 24ч * 60м * 60с) / (1024^4) = 9 962 Тбайт

Прирост из-за новых пользователей в год: (1 149 Байт - 1 Кб * 70%) * 9 RPS * (365д * 24ч * 60м * 60с) / (1024^4) = 0.112 Тбайт - не учитываем.

Рассчитаем размер view: 10 uuid(users_viewed) + 8 UTF8 (month_year) + uuid (message_id) = 10 * 16 + 8 * 4 + 16 = 208 Байт

Net: 208 Байт * 802 082 RPS * 8 / (1024^3) = 1.3 Гбит/c

За год: 208 Байт * 802 082 RPS * (365д * 24ч * 60м * 60с) / (1024^4) = 4 786 Тбайт

Прирост из-за новых пользователей в год: 208 Байт * 9 RPS * (365д * 24ч * 60м * 60с) / (1024^4) = 0.054 Тбайт - не учитываем.

Рассчитаем размер reaction: 4 uuid + 4 UTF8(value) + 8 UTF8 (month_year) + bool = 4 * 16 + 4 * 4 + 8 * 4 + 1 = 577 Байт

Net: 577 Байт * 67 014 RPS * 8 / (1024^3) = 0.3 Гбит/c

За год: 577 Байт * 67 014 RPS * (365д * 24ч * 60м * 60с) / (1024^4) = 1 109 Тбайт

Прирост из-за новых пользователей в год: 577 Байт * 9 RPS * (365д * 24ч * 60м * 60с) / (1024^4) = 0.149 Тбайт - не учитываем.

Примем среднее кол-во человек в чате 10.

Рассчитаем размер call_history: 3 uuid + 2 UTF8 (year) + 8 байт (time) + 4 байта (status) = 3 * 16 + 2 * 4 + 8 + 4 = 68 Байт

Net: 68 * 692 RPS * 8 / (1024^3) = 0.0004 Гбит/c

За год: 68 * 692 * (365д * 24ч * 60м * 60с) / (1024^4) = 2 Тбайт

Прирост из-за новых пользователей в год: 68 Байт * 9 RPS * (365д * 24ч * 60м * 60с) / (1024^4) = 0.018 Тбайт - не учитываем.

Остальные таблицы не считаем, из-за малого объема данных.

Предполагая, что кол-во view и изменения статуса сообщений соизмеримо с кол-вом сообщений.

Таблица RPS Хранилище за 5 лет Тбайт Net суммарный Гбит/с
message 802 082 * 2 + 167 534 = 1 771 698 9 962 * 5 = 49 810 6.9
view 802 082 4 786 * 5 = 23 930 1.3
reaction 67 014 1 109 * 5 = 5 545 0.3
call_history 692 2 * 5 = 10 0.0004
report_abuse 36 не учитываем не учитываем
user 9 не учитываем не учитываем

Из официальных рекомендаций Cassandra минимальные production [26]: 32 Гб RAM, 16 физических ядер, 1 Гбит/с сеть.

Согласно бенчмаркам Cassandra [27]:

Процессор Максимальный RPS
AMD EPYC 7343 - 16 Cores 3.2GHz/3.9GHz, 128MB Cache 172 339
AMD EPYC 7313P - 16 Cores 3GHz/3.7GHz, 128MB Cache Согласно [28] производительности близки, тестов на Cassandra не нашел, выберем его для слабо нагруженных таблиц в Cassandra из-за цены

Redis: Рассчитаем размер session: 1 uuid + 128 * 4 Байта token = 16 + 128 * 4 = 528 Байт

Net с учетом доп данных пример 1 Кбайт на запрос: 1 Кбайт * 234 243 RPS * 8 / 1024^2 = 1.8 Гбит/с

За год по MAU с поправочным коэффициентом 1.5: 528 Байт * 482 000 000 * 1.5 / 1024^3 = 356 Гбайт

Прирост из-за новых пользователей в год: 528 Байт * 9 регистраций в секунду * (365д * 24ч * 60мин * 60сек) / 1024^3 = 140 Гбайт

Рассчитаем размер permission_and_limitation: 128 * 4 Байта token + 128 байт json = 128 * 4 + 128 = 640 Байт

Net с учетом доп данных пример 1 Кбайт на запрос: 1 Кбайт * 36 RPS * 8 / 1024^2 = 0.0003 Гбит/с

За год: 640 Байт * 36 RPS * (365д * 24ч * 60м * 60с) / (1024^3) = 677 Гбайт

Прирост из-за новых пользователей в год: 640 Байт * 36 RPS / 482 млн MAU * 80 млн (регистраций) * (365д * 24ч * 60мин * 60сек) / 1024^3 = 113 Гбайт

Рассчитаем необходимые ресурсы на 5 лет с поправочным коэффицентом 1.5, т.к. 2 коэф. запасы мы закладывали изначально, а Redis рекомендует иметь 3 реплики [29]:

Таблица RPS RAM за 5 лет Гбайт Net суммарный Гбит/с
session 234 243 * 1.5 = 351 364 (356 + 140 * 5) * 1.5 = 1 584 1.8 * 1.5 = 2.7
permission_and_limitation 36 * 1.5 = 54 (677 + 113 * 5) * 1.5 = 1 863 0.0003 * 1.5 = 0.00045

Их рекомендаций Redis для 1 ноды [29]: более 8 core, 30 Gb RAM, 10 Gb/s net, RAM * 6 disk space.

Согласно бенчмаркам Redis [30]:

Процессор Максимальный RPS
AMD Ryzen 7 7700X, 8 Core, 16 Thread, 5.4GHz 4 309 987

Конфигурация серверов

Т.к. для всех параметров на этапе проектирования мы взяли запас в 2 раза относительно пиковых нагрузок. Считаем здесь по характеристикам немного превышающих требуемые. Цена с учетом гарантии 5 лет и RAID 6 для дисков Cassandra, резервирования диска ос RAID 1, защита по питанию. Минимальное кол-во серверов на один узел 16шт = 8 ДЦ * 2 резервирование. Цены на own bare metal взяты из [31], microsoft azure [32], DigitalOcean [33]. Для облачных решений считал в калькуляторе сопоставимые по характеристикам сервера.

Название Хостинг Конфигурация Cores Cnt Покупка на 5 лет 1 шт $ Аренда на 5 лет 1 шт $
web && mobile nginx own bare metal 1 AMD EPYC 7443P/132Gb/NVme 2250Gb/1*400Gb 24 24 7 048 не рассматривался из-за высоких требований к сети
web && mobile api gateway own bare metal 1 AMD EPYC 7443P/132Gb/NVme 2250Gb/1*400Gb 24 21 7 048 не рассматривался из-за высоких требований к сети
chat.attachments own Kuber 1 AMD EPYC 7713P/264Gb/NVme 2250Gb/2*100Gb 64 129 12 266 не рассматривался из-за высоких требований к сети
call.(stun&&turn) own Kuber OR DigitalOcean 1 AMD EPYC 7313P/216Gb/NVme 2250Gb/2*10Gb 16 16 4 425 10 080
all other microservices own Kuber OR DigitalOcean 1 AMD EPYC 7713P/264Gb/NVme 2250Gb/2*10Gb 64 166 10 860 20 160
Cassandra message own bare metal OR azure 1 AMD EPYC 7343/232Gb/Sata3 24x7.6TB/21Gb 16 298 36 099 984 720
Cassandra view own bare metal OR azure 1 AMD EPYC 7343/232Gb/Sata3 24x7.6TB/21Gb 16 144 36 099 984 720
Cassandra reaction own bare metal OR azure 1 AMD EPYC 7313P/232Gb/Sata3 247.6TB/2*1Gb 16 34 36 099 984 720
Cassandra call_history own bare metal OR DigitalOcean 1 AMD EPYC 7313P/232Gb/NVMe 2250Gb/2*1Gb 16 16 4 601 32 640
Cassandra other own bare metal OR DigitalOcean 1 AMD EPYC 7313P/232Gb/NVMe 2250Gb/2*1Gb 16 16 4 601 32 640
Redis session own bare metal OR DigitalOcean 1 AMD Ryzen 7 7700X/432Gb/Nvme 1Tb/210Gb 8 25 5 500 20 160
Redis permission_and_limitation own bare metal OR DigitalOcean 1 AMD Ryzen 7 7700X/432Gb/Nvme 1Tb/210Gb 8 24 5 500 20 160
Amazon S3 amazon 41 164 TB - - - 210 762 * 12 * 5 = 12 645 720

Для сервисов в оркестрации:

Сервис CPU/r CPU/l RAM/r RAM/l
chat.attachments 16 32 16 64
chat.chat, chat.removing_message, call.call, call.(stun&&turn), support.support 8 16 8 32
auth.auth, auth.limitation_worker, user.user, chat.message, chat.view, chat.status workers, chat.reaction worker and etc 4 8 4 16

Суммарная стоимость системы: 12 645 720 + 7 048 * 55 + 12 266 * 129 + 4 425 * 16 + 10 860 * 166 + 36 099 * 476 + 4 601 * 32 + 5 500 * 49 = 34 089 090 $

Список источников

  1. Общая статистика Whatsapp https://www.bankmycell.com/blog/number-of-whatsapp-users/#1613581803631-96bfa-c1678a7f-e18553c4-2e8a2161-0689e002-e6d075e8-7b5a
  2. Частота использования ежедневно в США https://www.statista.com/statistics/814813/frequency-with-which-us-internet-users-visit-whatsapp/
  3. Количество ежедневных пользователей https://www.statista.com/statistics/730306/whatsapp-status-dau/
  4. Распределение MAU по странам https://worldpopulationreview.com/country-rankings/whatsapp-users-by-country
  5. Официальный пост Whatsapp о количестве голосовых сообщений https://blog.whatsapp.com/making-voice-messages-better
  6. Количество голосовых звонков https://dataprot.net/statistics/whatsapp-statistics/#:~:text=3.-,Over%202%20billion%20minutes%20of%20voice%20and,are%20sent%20daily%20via%20WhatsApp.&text=There's%20more%20to%20WhatsApp%20than,the%20latest%20WhatsApp%20personal%20statistics.
  7. Статистика по скачиваниям https://www.businessofapps.com/data/whatsapp-statistics/
  8. Исследование 4 млн сообщений от 100 людей 2016г https://www.researchgate.net/publication/299487660_WhatsApp_Usage_Patterns_and_Prediction_Models
  9. Исследование с 6 млн сообщений и 100 участников 2018г https://www.researchgate.net/publication/327918943_WhatsApp_usage_patterns_and_prediction_of_demographic_characteristics_without_access_to_message_content
  10. Расчеты MAU по условным регионам https://docs.google.com/spreadsheets/d/1bwwJy5Y3Objel3J5x4IXkPFhqaDwpeiLdYi_NVZZPw0/edit?usp=sharing
  11. Среднее кол-во времени на один голосовой звонок https://www.wharftt.com/how-long-can-a-whatsapp-call-last/
  12. Исследование английских слов https://norvig.com/mayzner.html
  13. Калькулятор голосовых звонков WhatsApp https://3roam.com/whatsapp-data-and-bandwidth-usage-calculator/
  14. Среднее кол-во заходов в мессенджеры в день https://www.tadviser.ru/index.php/%D0%A1%D1%82%D0%B0%D1%82%D1%8C%D1%8F:%D0%9C%D0%B5%D1%81%D1%81%D0%B5%D0%BD%D0%B4%D0%B6%D0%B5%D1%80%D1%8B_(Instant_Messenger,_IM)#:~:text=%D0%9A%D0%B0%D0%BA%20%D0%B2%D1%8B%D1%8F%D1%81%D0%BD%D0%B8%D0%BB%D0%BE%D1%81%D1%8C%2C%20%D0%B2%20%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B5%D0%BC%20%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D1%8C,%D0%B8%D0%B4%D1%83%D1%82%20Viber%2C%20Telegram%20%D0%B8%20Skype.)
  15. Карта морских кабелей https://www.submarinecablemap.com/
  16. Карта дата центров https://www.datacentermap.com/
  17. Карта дата центров Meta** https://datacenters.atmeta.com/
  18. Исследование про анализ запрещенного контента в видео https://www.researchgate.net/publication/358201557_A_Deep_Learning-Based_Approach_for_Inappropriate_Content_Detection_and_Classification_of_YouTube_Videos
  19. MLOps в билайн https://habr.com/ru/companies/beeline_tech/articles/760308/
  20. Архитектура мл для шумоподавления от VK https://habr.com/ru/companies/vk/articles/572950/
  21. Circuit breaker от sony на Go https://github.com/sony/gobreaker
  22. Бенчмарки nginx https://www.nginx.com/blog/testing-the-performance-of-nginx-and-nginx-plus-web-servers/
  23. Бенчмарки веб фреймворков от TechEmpower https://www.techempower.com/benchmarks/#section=data-r22&test=fortune&hw=ph&l=zii9rz-cn3
  24. Методические указания к дз 11 https://github.com/init/highload/blob/main/highload_l11_hosting.md
  25. Калькулятор Amazon S3 https://calculator.aws/#/createCalculator/S3
  26. Рекомендации Cassandra по production серверам https://docs.datastax.com/en/planning/oss/oss-capacity-planning.html
  27. Бенчмарки Cassandra на разных процессорах https://openbenchmarking.org/test/pts/cassandra&eval=1f26111c58c3f69e4239be4a14c7d346537f4ac7
  28. Сравнение AMD 7313 и 7343 https://openbenchmarking.org/vs/Processor/AMD+EPYC+7343+16-Core,AMD+EPYC+7313P+16-Core
  29. Рекомендации Redis по production серверам https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/hardware-requirements/
  30. Бенчмарки Redis на разных процессорах https://openbenchmarking.org/test/pts/redis&eval=09fe23c16324414efce5603cb618cb1a8dbd571d#metrics
  31. Цены на сервера Broadberry https://www.broadberry.eu/rackmount-servers
  32. Цена хостинг Microsoft Azure https://azure.microsoft.com/en-gb/pricing/calculator/

** Meta - организация, деятельность которой запрещена на территории Российской Федерации