Ильинский Владислав
Этот репозиторий является систем дизайном для мессенджера WhatsApp.
1. Тема, аудитория, функционал
5. и 6. Логическая и физическая схема базы данных
11. Расчет аппаратных ресурсов
- Отправка сообщений в чате.
- Просмотр чата.
- Вложения фото, видео, файлы, голосовые.
- Поиск сообщений по чатам.
- Группировка чатов по папкам.
- Групповой чат с несколькими пользователями.
- Статус сообщения(отправлено, доставлено, прочитано)
Ключевые особенности
-
Эмоции на сообщения в виде смайликов.
-
Голосовые звонки внутри мессенджера
-
Проектировать будем с учетом отсутствия постоянного хранения сообщений на серверах. Т.е. сообщения могут храниться только пока адресат не доступен. После доставки сообщения удаляются с серверов. Можно хранить метаданные о статусе доставке, времени отправки, получения и т.д.
Метрика | Значение |
---|---|
Аудитория | Весь мир |
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] не будем концентрироваться на определенный регион.
Метрика | 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 Тбайт в год |
Я взял 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 Тбайт в год
Регион | Расположение датацентров |
---|---|
Азия | Нью-Делли(Индия), Джакарта(Индонезия) |
Европа | Лулео(Швеция), Стамбул(Турция) |
Северная Америка | Темпл(США, Техас), Калифорния) |
Южная Америка | Сан-Паулу(Бразилия) |
Африка | Лагос(Нигерия) |
Регионы и страны | Процент от общего числа 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 |
Рисунок 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
Рисунок 2. Пинг из Стокгольма в другие города.
- Второй дата центр решил расположить в Стамбуле(Турция) для дополнительного покрытия северных регионов Африки и азиатских стран на Аравийском полуострове(Саудовская Аравия, Иран, Израиль...) Пинг для стран покрывающих этим дата центров достаточный см. рисунок 3.
Рисунок 3. Пинг из Стамбула в другие города.
Северная Америка:
- Один дата центр в Темпл(США, Техас), т.к. там расположен в реальной жизни дата центр Meta** и будет покрывать Мексику с существенной долей MAU 3.1% 14млн.
- Второй расположим в Сан-Хосе(США, Калифорния) т.к. там есть центр расположения большого кол-ва дата центров и для лучшего покрытия западной части США.
Южная Америка:
- Расположим дата центр в Сан-Паулу(Бразилия), т.к. там расположено скопление дата центров.
Африка:
- Расположим дата центр в Лагосе (Нигерия), т.к. там расположены дата центры и для примерно центрального расположения на континенте.
- Использовать будем Latency based DNS на уровне крупных регионов по типу континентов. Т.к. в общем случае метрика задержки более объективна чем географическая близость в Geo based DNS.
- Будем использовать BGP Anycast внутри регионов для более точной настройки распределения между дц нагрузки и возможности переадресовать пользователей в случае инцидентов из одного дц в другой.
Для локальной балансировки будем использовать L7 уровень из-за более гибкой возможности настройки(выделения функциональных групп бекендов, более равномерного распределения нагрузки, обработки медленных клиентов).
Решено было использовать кластер прокси серверов реализующих логику балансировки на application сервера, ssl termination, кеширование, архивирование контента. Это решение было выбрано в пользу sidecar серверов для сокращения оверхеда на создание коннектов до application серверов, и поддержании их с постоянном разогнанном состоянии. Также в таком случае из-за уменьшения кол-ва ip доступных из вне, лучше будет кешироваться ssl session, т.к. с большей вероятностью клиент попадет на нужный ip, и реже будет оверхед на ssl handshake. Использование кластера балансинга также позволит уменьшить downtime приложения в случаях выкатки новых версий, падениях, зависаний бекендов благодаря более гибкой настройки механизмов балансировки.
На уровне балансировки между машинами в рамках кластера прокси серверов будем исползовать BGP Anycast.
Для балансировки межсервисного трафика будем использовать кластер API Gateway для изоляции логики роутинга.
Посмотреть диаграмму полностью
Таблица | База данных | 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:
- Получение новых собщений в чате SELECT * FROM message WHERE chat_id=? AND month_year=? AND id > ?
- Получение\измененине конкретного message SELECT * FROM message WHERE chat_id=? AND month_year=? AND id = ?
- Удалить сообщение в чате по id UPDATE message SET value='' AND status=deleted AND attachments={} WHERE chat_id = ? AND month_year = ? AND id = ?
- Добавить сообщение INSERT * TO message(id, -//-)
C↓ - id. Используется тип timeuuid, содержащий внутри дату создания сообщения.
K - chat_id + month_year. month_year - это месяц и год создания сообщения. Вместе с id чата они позволяют ограничивать рост шарда и обеспечивают получение сообщений из чата.
views:
- Получить просмотры по id сообщений? SELECT * FROM view WHERE message_id IN () AND month_year = ?
- Добавить просмотр в список UPDATE views SET users_viewed = users_viewed + [?] WHERE message_id = ? AND month_year = ?
K - message_id + month_year. month_year вместе с id чата они позволяют ограничивать рост шарда и обеспечивают получение просмотров.
reaction:
- Получить все новые реакции в чате SELECT * FROM reaction WHERE chat_id = ? AND id > ?
- Убрать реакцию с сообщения по id реакции UPDATE message SET deleted=true WHERE id = ?
- Добавить реакцию INSERT * TO reaction(id, -//-)
C↓ - id. Используется тип timeuuid, содержащий внутри дату создания реакции.
K - chat_id + month_year. month_year вместе с id чата они позволяют ограничивать рост шарда и обеспечивают получение реакций в чате.
user:
- Получить user по телефону SELECT * FROM user WHERE phone = ?
- Получить пользователя по id SELECT * FROM user WHERE id = ?
- Добавить пользователя INSERT * TO user(id, -//-)
- Добавить settings, avatar_url, UPDATE user SET settings = ? WHERE id = ?;
K - id. Использовал такой, т.к. в запросах почти всегда мы используем фильтрацию по пользователю.
S - phone. Т.к. запрос пользователя по номеру телефона в связи с редкой регистрацией тоже будет выполняться редко решено использовать индекс, а не отдельную таблицу, чтобы не дублировать данные. Использовать составной ключ нельзя было, т.к. в некоторых запросах мы не знаем номер телефона, это бы усложнило бы реализацию.
chat:
- Добавить пользователя в чат UPDATE chat SET users_id = users_id + {?} WHERE id = ?
- Получить чат по id SELECT * FROM chat WHERE id = ?
- Добавить чат INSERT TO chat(id, ...)
K - id тип timeuuid. Кол-во чатов относительно не большое число, поэтому разбиение на шарды по id чата вполне себе работает.
call_history:
- Получить историю звонков с определенного момента для одного пользователя SELECT * FROM call_history WHERE chat_id IN (?,?,...) AND year = ? AND id > ?
- Получить новые звонки в чате SELECT * FROM call_history WHERE chat_id = ? AND year = ? AND id > ?
- Добавить звонок INSERT * TO call_history(id, ...)
C↓ - id. Используется тип timeuuid, содержащий внутри дату создания реакции.
K - chat_id + year. year - в каком году было создано сообщение. Используем такое поле без месяца т.к. кол-во звонков на 3 порядка меньше количества сообщений, т.е. данных на шарде будет меньше. Вместе они позволяют ограничивать рост шарда и обеспечивают получение истории звонков.
folder:
- Добавить чат в папку UPDATE folder SET chats_id = chats_id + {?} WHERE id = ?
- Добавить папку с чатами INSERT TO folder(id, ...)
K - id. Количество папок также не очень велико поэтому используем его как ключ шардинга.
report_abuse:
- Получить все новые, не просмотренные отчеты SELECT * FROM report_abuse WHERE verdict_support_staff = 0 ? AND month_year = ? AND id > ?
- Поставить/изменить вердикт на отчет по id отчета UPDATE report_abuse SET verdict_support_staff = ? WHERE id = ? AND month_year = ? AND verdict_support_staff IN (...)
- Поставить вердикт мл модели на отчет по id отчета UPDATE report_abuse SET verdict_ml_model = ? WHERE verdict_support_staff = 0 AND month_year = ? AND id = ?
- Посмотреть все отчеты по пользователю 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 индексы для возможности быстрого получения файла по ключу.
- Алгоритм детектирования запрещенных видов контента(призывы к терроризму и т.д.) с помощью машинного обучения
Алгоритмы будут применяться для контента, на который пожаловались другие пользователи. Задачи будут с помощью 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, линтеры и т.д. Стандартизация технической стороны позволяет мл инженерам акцентировать внимание в большей степени на исследовательской части, и помогает разворачивать новые проекты.
Код обучения моделей нужен, чтобы иметь возможность просто дообучать модели в случае, если нам понадобится в процессе использования модифицировать модели.
Алгоритм улучшения качества голосовых звонков Для шумоподавления будем использовать мл модель на основе архитектуры Dual-Signal Transformation LSTM Network подобные модели подходят для использования в real time, т.к. не большие по размеру, имеют высокую скорость обработки и достаточно хорошее качество. Опирался на статью [20].
- Алгоритмы удаления сообщений после прочтения
- В момент получения статуса сообщения доставлено, status worker, кладет в Kafka chat_id + message_id.
- removing_attachments микросервис делает запрос в Cassandra и читает в оперативную память вложения для сообщений.
- removing_attachments микросервис делает запрос на удаление вложений из Amazon S3.
- removing_attachments микросервис делает запрос на зануление соответствующих полей в Cassandra.
- Алгоритмы удаления сообщений после того, как из не прочитали за год
- Настроим автоматическое удаление файлов из Amazon S3 через 1 год.
- Удалять из Cassandra не будем, т.к. это бы генерировало много нагрузки, просто не будем их показывать, если прошел срок.
- Алгоритм голосовых звонков
Для реализации звонков будем использовать WebRTC технологию, которая доступна в браузере, iOS, Android. Для обхода Nat окружения будем использовать Stun, которые работают в случае если у пользователей не симметричный Nat и Turn сервера, который требует проксировать через себя запросы, но позволяет обойти двухсторонний симметричный Nat.
- Инициализация звонка одним клиентом через Websocket.
- Отсылается уведомление второму клиенту.
- Проверка возможности установления соединения между клиентами при помощи Stun сервера.
- При принятии поиск лучшего Turn сервера по latency
- По определенным публичным ip клиентов устанавливается соединение через Turn сервер.
- По завершению/Отклонению звонка клиенты присылают информацию о звонке на сервер используя Websocket.
- Алгоритм загрузки файлов
- Сохранение в локальной файловой системе.
- Определение разрешен ли формат.
- Сжатие (только для видео). Видео будет использоваться с форматом mp4 с кодеком H.264 ввиду его широкой поддержки, несмотря на более низкий уровень сжатия, например по сравнению с H.265. Этот кодек основан на субъективности человеческих органов чувств. Используется цветовая субдискретизация, т.е. уменьшения разрешения цветовой части изображения. Дискретное косинусное преобразование с отбрасыванием части гармоник (четкости изображения) ради сжатия. Также применяются алгоритмы, основанные на временной избыточности (разбиение на части кадров с последующим вычислением векторов перемещений). На последнем этапе применяются алгоритмы обратимого сжатия (статистические или словарные).
- Отправка в Amazon S3.
- Сохранения сведений о файле в бд.
- Алгоритмы шифрования
В WhatsApp используется Signal Protocol, который основан на асинхронном шифровании, X3DH ("Extended Triple Diffie-Hellman"), Double Ratchet Algorithm. X3DH используется для подтверждения личностей отправителя и получателя между собой, и обмена временными ключами. На следующей стадии с помощью Double Ratchet Algorithm сообщение прогоняется через KDF цепочку (расшифрованное одно сообщение не позволяет расшифровать сообщение из прошлого) а с помощью параметра, который является входным для KDF алгоритма и уникальным для каждого сообщения реализуется защита от расшифровки будущих сообщений.
- Алгоритм поиска по сообщениям на клиенте
Создадим индекс FTS5 в SQLite для полнотекстового поиска, внутри он токенизирует по символам пробелов и пунктуации согласно unicode6.1. Сама структура индекса основана на b-tree деревьях. В конечном итоге поиск будет осуществляться по совпадению и по префиксу.
Технология | Сфера применения | Мотивация |
---|---|---|
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 для возможности писать кросплатформенные приложения. Позволит ускорить разработку в рамках общей экосистемы. |
Будем использовать 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 события наложения ограничений.
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:
- ack=all - дожидаемся для производителя (chat and events микросервис) ответа от всех реплик, что сообщение записано.
- enable.idempotence=true - гарантирует, что сообщения, полученные от одного производителя (с одинаковым producer.id) в результате retry-ов не будут дублироваться.
- использование api transaction producer - гарантия того, что разные производители не смогут записать сообщение дважды, т.к. в случае тех или иных сбоев транзакция будет откатываться.
- isolation_level=read_commited - для гарантии чтения только закомиченных сообщений на стороне потребителя (message worker микросервис).
- генерация id timeuuid для сообщений на клиенте, чтобы в системе на уровне потребителя уже не получилось записать в бд Cassandra одинакового сообщения.
- retry-и на производителе в случае ошибок, которые возможно решить с помощью них (retriable_errors) пример такой ошибки - LEADER_NOT_AVAILABLE. \
Для пути записи заявок в службу поддержки будем также реализовывать гарантию доставки Exactly Once по схожим принцип, так что подробно расписывать не будем.
Для остальных топиков не имеем таких жестких требований, можем потерять некоторые события или некоторые события сохранить несколько раз, поэтому будем реализовывать at least once гарантию доставки:
- ack=1 - дожидаемся ответа от одной реплики, что сообщение записано, что достаточно производительно, но оставляет вероятность потерять события, например в случае, когда ведомые ноды не успели реплицировать данные, а лидер нода упала, не записав на диск данные.
- retry-и на производителе в случае ошибок, которые возможно решить с помощью них (retriable_errors) пример такой ошибки - LEADER_NOT_AVAILABLE.
- на уровне клиентских приложений (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 заявок в службу поддержки. Нужен для поддержки консистентности данных в различных бд и как временное персистентное хранилище для последующей обработки событий.
Наблюдаемость
- Будем использовать логирование, трассировку, мониторинг, алертинг, профилирование. Для понимания текущего состояния системы и анализа инцидентов.
Сервис | Целевой 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 $
- Общая статистика Whatsapp https://www.bankmycell.com/blog/number-of-whatsapp-users/#1613581803631-96bfa-c1678a7f-e18553c4-2e8a2161-0689e002-e6d075e8-7b5a
- Частота использования ежедневно в США https://www.statista.com/statistics/814813/frequency-with-which-us-internet-users-visit-whatsapp/
- Количество ежедневных пользователей https://www.statista.com/statistics/730306/whatsapp-status-dau/
- Распределение MAU по странам https://worldpopulationreview.com/country-rankings/whatsapp-users-by-country
- Официальный пост Whatsapp о количестве голосовых сообщений https://blog.whatsapp.com/making-voice-messages-better
- Количество голосовых звонков 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.
- Статистика по скачиваниям https://www.businessofapps.com/data/whatsapp-statistics/
- Исследование 4 млн сообщений от 100 людей 2016г https://www.researchgate.net/publication/299487660_WhatsApp_Usage_Patterns_and_Prediction_Models
- Исследование с 6 млн сообщений и 100 участников 2018г https://www.researchgate.net/publication/327918943_WhatsApp_usage_patterns_and_prediction_of_demographic_characteristics_without_access_to_message_content
- Расчеты MAU по условным регионам https://docs.google.com/spreadsheets/d/1bwwJy5Y3Objel3J5x4IXkPFhqaDwpeiLdYi_NVZZPw0/edit?usp=sharing
- Среднее кол-во времени на один голосовой звонок https://www.wharftt.com/how-long-can-a-whatsapp-call-last/
- Исследование английских слов https://norvig.com/mayzner.html
- Калькулятор голосовых звонков WhatsApp https://3roam.com/whatsapp-data-and-bandwidth-usage-calculator/
- Среднее кол-во заходов в мессенджеры в день 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.)
- Карта морских кабелей https://www.submarinecablemap.com/
- Карта дата центров https://www.datacentermap.com/
- Карта дата центров Meta** https://datacenters.atmeta.com/
- Исследование про анализ запрещенного контента в видео https://www.researchgate.net/publication/358201557_A_Deep_Learning-Based_Approach_for_Inappropriate_Content_Detection_and_Classification_of_YouTube_Videos
- MLOps в билайн https://habr.com/ru/companies/beeline_tech/articles/760308/
- Архитектура мл для шумоподавления от VK https://habr.com/ru/companies/vk/articles/572950/
- Circuit breaker от sony на Go https://github.com/sony/gobreaker
- Бенчмарки nginx https://www.nginx.com/blog/testing-the-performance-of-nginx-and-nginx-plus-web-servers/
- Бенчмарки веб фреймворков от TechEmpower https://www.techempower.com/benchmarks/#section=data-r22&test=fortune&hw=ph&l=zii9rz-cn3
- Методические указания к дз 11 https://github.com/init/highload/blob/main/highload_l11_hosting.md
- Калькулятор Amazon S3 https://calculator.aws/#/createCalculator/S3
- Рекомендации Cassandra по production серверам https://docs.datastax.com/en/planning/oss/oss-capacity-planning.html
- Бенчмарки Cassandra на разных процессорах https://openbenchmarking.org/test/pts/cassandra&eval=1f26111c58c3f69e4239be4a14c7d346537f4ac7
- Сравнение AMD 7313 и 7343 https://openbenchmarking.org/vs/Processor/AMD+EPYC+7343+16-Core,AMD+EPYC+7313P+16-Core
- Рекомендации Redis по production серверам https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/hardware-requirements/
- Бенчмарки Redis на разных процессорах https://openbenchmarking.org/test/pts/redis&eval=09fe23c16324414efce5603cb618cb1a8dbd571d#metrics
- Цены на сервера Broadberry https://www.broadberry.eu/rackmount-servers
- Цена хостинг Microsoft Azure https://azure.microsoft.com/en-gb/pricing/calculator/
** Meta - организация, деятельность которой запрещена на территории Российской Федерации