Git Product home page Git Product logo

highload_google_calendar's Introduction

Google Calendar

Оглавление

1. Тема и целевая аудитория

Google Calendar — сервис для планирования встреч, событий и дел.

1.1 Целевая аудитория

MAU = 500 млн. [1], [2], [4]

Информация о DAU отсутствует, поэтому примем его, равным 500 млн. пользователей, т.е. MAU = DAU, т.к. данный тип сервиса используется для планирования встреч, событий и дел, а также для синхронизации данных на устройствах. [3]

1.2 Географическое распространение (данные за январь 2024) [4]

Страна Процент пользователей от общего числа, % Количество, млн.
США 42,93 214,65
Япония 8,17 4,085
Великобритания 3,68 1,84
Канада 3,56 1,78
Франция 3,43 1,715
Другие 38,24 191,2

1.3 Распространение среди различных возрастных групп (данные за январь 2024) [4]

Возрастная группа Процент пользователей от общего числа, %
18 - 24 14,74
25 - 34 29,81
35 - 44 20,58
45 - 54 16,89
55 - 64 11,33
65+ 6,65

1.4. MVP

  • Регистрация, авторизация;
  • Создание, редактирование календарей;
  • Каждый календарь можно сделать общедоступным либо предоставить отдельным людям доступ к нему (просмотр или редактирование) через приглашение по электронной почте;
  • Уведомления о предстоящих событиях по почте;
  • Создание и редактирование событий.

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

2.1 Продуктовые метрики

2.1.1 Хранилище пользователя

Базовый функционал этого приложения включает в себя следующие типы данных:

Пользователь (профиль) Календарь Событие в календаре Уведомление Ответ на уведомление Напонимание Подписка на календарь
email Название Дата создания Время до события email пользователя Время до события Календарь
Хэшированный пароль Описание Дата обновления Событие (ссылка) Ответ пользователя Событие (ссылка) Пользователь
Имя и фамилия Дата создания Дата начала Дополнительная нагрузка Событие (ссылка) Дополнительная нагрузка
Часовой пояс (ссылка) Дата обновления Дата окончания
Телефонный номер Часовой пояс (ссылка) Название
Страна Владелец (ссылка) Описание
Город Календарь (ссылка)
Пользователь (ссылка)
Рекуррентность события
Период создания
  • Примем число личных календарей, равным 5 и число календарей, в которые пользователь может быть приглашён, равным 10.
  • Максимальное число пользователей, которых можно пригласить на событие примем равным 200 [14].
  • Число событий, создаваемым пользователем в день примем равным 3 (при общем числе событий в 1,5 млрд [3] и DAU = 500 млн. число событий 1500/500 = 3) и хранятся 5 лет, поэтому за 5 лет у пользователя накопится 5 * 365 * 3 = 5475.
  • Все символы будут храниться в кодировке UTF-8, где каждый символ занимает около 3-х байт [15].

Далее проведём подробный расчёт с целью выяснение необходимого места для одного пользователя на каждый из этих типов.

Пользователь (профиль)

Тип данных Размер
email 255 B [6]
Хэшированный пароль 72 B [7]
Имя и фамилия 300 B [8]
Часовой пояс 8 B (ссылка)
Телефонный номер 15 B [8]
Страна 165 B [8]
Город 150 B [8]
Общий размер 255 + 72 + 300 + 8 + 15 + 165 + 150 = 965 B
Календарь
Тип данных Размер
Название 128 символов UTF-8 (384 B)
Описание 1024 символа UTF-8 (3072 B)
Дата создания 8 B [9]
Дата обновления 8 B
Часовой пояс 8 B (ссылка)
Владелец (ссылка) 8 B
Общий размер 5 * (372 + 3072 + 8 + 8 + 8 ) = 5 * 3480 = 17400 B < 17.4 KiB (принимаем 17.4 KiB)
Событие
Тип данных Размер
Дата создания 8 B
Дата обновления 8 B
Дата начала 8 B
Дата окончания 8 B
Название 128 символов UTF-8 (384 B)
Описание 1024 символа UTF-8 (3072 B)
Календарь 8 B (ссылка)
Пользователь 8 B (ссылка)
Рекуррентность события 1 B (bool)
Период создания 8 B
Общий размер 5475 * (8 + 8 + 8 + 8 + 384 + 3072 + 8 + 8 + 1 + 8) = 5475 * 3513 = 19 233 675 B < 19.25 MiB (принимаем 19.25 MiB)
Уведомление

В виду сложности оценки пересылаемой информации в уведомлении примем размер дополнительной полезной нагрузки, равным 1 KiB.

Тип данных Размер
Событие 8 B (ссылка)
Время события 8 B
Дополнительная нагрузка 1024 B
Общий размер (8 + 8 + 1024) = 1040 B
Ответ на уведомление
Тип данных Размер
email пользователя 255 B
Ответ пользователя 1 B (uint_8t)
Событие 8 B (ссылка)
Общий размер (255 + 1 + 8) = 264 B
Напоминание

В виду сложности оценки пересылаемой информации в напоминании примем размер дополнительной полезной нагрузки, равным 1 KiB.

Тип данных Размер
Событие 8 B (ссылка)
Время до события 8 B
Дополнительная нагрузка 1024 B
Общий размер (8 + 8 + 1024) = 1040 B
Подписка на календарь
Тип данных Размер
Календарь 8 B (ссылка)
Пользователь 8 B (ссылка)
Общий размер 10 * (8 + 8) = 10 * 16 = 160 B
Размер хранилища
Тип Размер хранилища
Пользователь (профиль) 965 B
Календари 6 KiB
Подписки на календари 160 B
События 19.25 MiB
Уведомление 1040 B
Ответ на уведомление 264 B
Напоминание 1040 B
Общий размер 965 B + 6 KiB + 160 B + 19.25 MiB + 1040 B + 264 B + 1040 B < 19.3 MiB
  • Общий размер здесь проведён приближённо, для оценки размера хранилища.

2.2 Технические метрики

2.2.1 Размер хранения по типам данных

Т.к. количество зарегистрированных аккаунтов в Google Workspace равно 3 млрд [1]-[4] и, учитывая, что Calendar создаётся для каждого пользователя, примем количество зарегистрированных пользователей равным 3 млрд.

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

Тип Размер хранилища
Пользователь (профиль) 965 B * 3 млрд. = 2.63 TiB
Календари 6 KiB * 3 млрд. = 16.763 TiB
Подписки на календари 160 B * 3 млрд. = 447.035 GiB
События 19.25 MiB * 500 млн. = 8.96 PiB

Здесь также не учтены приглашённые пользователи, т.к. это динамически меняющийся массив и можно установить лишь ограничение на присоединение к календарю в течение короткого промежутка времени [16].

Остальные элементы: уведомление, ответ на уведомление и напиминание относятся лишь к сети (за исключением того, что по ответу на уведомление меняется динамически список пользователей, участвующих в событии); поэтому не учтены в размере хранилища.

2.2.1 Сетевой трафик

Формулы расчёта

  1. Средний RPS

    DAU * количество действий * количество запросов для действия / 86400 сек

  2. Средний суточный трафик:

    DAU * количество действий * количество байт пересылаемых запросами для совершения действия / 86400 сек

Количество событий в сутки

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

Допущения

  • Каждый пользователь может отменить подписку на события, но для расчёта примем, что у всех пользователей подписка есть.
  • Примем, что из календаря приходит по 1 уведомлению в день и столько же ответов на уведомления и напоминаний.
Тип события Количество действий в день
Просмотр календарей 5 + 10 = 15
Взаимодействия с календарями 3
Получение уведомлений 15
Получение ответов на уведомление 15
Получение напоминаний 15

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

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

  • Просмотр календарей, кроме запроса на получение данных самого календаря, включает запрос на получение данных пользователя, поэтому суммарное количество запросов равно (1 + 1) * 15 = 30, а количество данных равно соответственно (17400 + 965) * 15 = 275475 B.
  • Взаимодействие с календарями включает лишь создание/обновление события, поэтому суммарное количество запросов равно 3, а количество данных равно соответственно 3 * 17400 = 52200 B.
  • Получение уведомлений включает лишь один запрос, поэтому суммарное количество запросов на получение уведомлений равно 15, а количество данных уведомления соответственно данным о событии, т.е. получим 15 * 1040 = 15600 B.
  • Получение ответа на уведомление включает лишь запрос на отправку ответа на уведомление, поэтому получим 15 * 264 = 3960 B.
  • Получение напоминания составляет столько пересылаемой информации, как и получение уведомления, поэтому 15 * 1040 = 15600 B.

Расчёт метрик

Тип метрики RPS Средний суточный трафик
Просмотр календарей 500 млн. * 15 / 86400 сек = 86806 500 млн. * 15 * 275475 / 86400 = 191.3 Гбит/с
Взаимодействия с календарями 500 млн. * 3 / 86400 сек = 17362 500 млн. * 3 * 52200 / 86400 = 7.25 Гбит/с
Получение уведомлений 500 млн. * 15 / 86400 сек = 86806 500 млн. * 15 * 15600 / 86400 = 10.83 Гбит/с
Получение ответов на уведомления 500 млн. * 15 / 86400 сек = 86806 500 млн. * 15 * 3960 / 86400 = 2.75 Гбит/с
Получение напоминаний 500 млн. * 15 / 86400 сек = 86806 500 млн. * 15 * 15600 / 86400 = 10.83 Гбит/с

Полученные значения метрик

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

Действие RPS Пиковый RPS Средний трафик Пиковый трафик
Просмотр календарей 86806 173612 191.3 Гбит/с 382.6 Гбит/сек
Взаимодействия с календарями 17362 34724 7.25 Гбит/с 14.5 Гбит/сек
Получение уведомлений 86806 173612 10.83 Гбит/с 21.66 Гбит/сек
Получение ответов на уведомления 86806 173612 2.75 Гбит/с 5.5 Гбит/сек
Получение напоминаний 86806 173612 10.83 Гбит/с 21.66 Гбит/сек

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

3.1 Физическое расположение дата-цетров

Для анализа основных регионов потоков трафика были выбраны следующие статистики:

Страна hypestat.com [11] webstatsdomain.org [12] similarweb.com [5]
USA 18.8% 32.7% 42.93%
India 10.7% 7.7%
China 4.4%
Japan 5.2% 4.3% 8.17%
United Kingdom 3.68%
Canada 3.56%
Russia 2.9%
France 3.43%
Brazil 2.8%
Iran 3%

Similarweb.com был выбран, несмотря на свою неточность для обобщения с остальными статистиками, т.к. это позволит увеличить выборку.

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

  1. USA — 42.93%
  2. India — 10.7%
  3. Japan — 8.17%
  4. United Kingdom — 3.68%
  5. Canada — 3.56%
  6. France — 3.43%
  7. Iran — 3%
  8. Russia — 2.9%
  9. Brazil — 2.8%
  10. Other — 25.26%

Исходя их этой статистики можно сделать вывод, что основная часть дата-центров должна располаться в США, остальные — региональные.

Возможные города распложения дата-центров возьмём из [13].

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

Из вышесказанного следует, что дата-центры будут расположены следующим образом:

  • Основные дата-центры в США:
    • Вирджиния (Северо-восток)
    • Лос-Анджелес (Юго-запад)
  • Дополнительные дата-центры:
    • Индия (Мумбаи)
    • Германия (Франкфурт)

Расположение в США двух дата-центров обосновано тем, что США — основной потребитель трафика подобного сервиса, в Индии — потому что следующим регионом по аудитории данного сервиса после США является Индрия, а в Лондоне, который является очень крупным хабом для датацентров Европы, — для обеспечения покрытия Европы, которая суммарно составляет также крупный регион потребления трафика.

Карта физического расположения дата-центров

Карта физического расположения дата-центров

Ссылка на карту (https://yandex.ru/maps/?um=constructor%3Aaea556dba6be4bcec4779f984ed8f4823e591b580d93639704395d6a6a3eb012&source=constructorLink)

3.2 Алгоритм балансировки

Отсюда можно заключить, что наиболее целесообразно будет использовать следующие технологии:

  • BGP Anycast
  • Geo-based DNS

Выбор BGP Anycast обоснован тем, что пользователя будет направлять в оптимальный по расположению дата-центр в пределах региона (с точки зрения BGP), т.к. позволяет распределить нагрузку с помощью настройки anycast-сетей, которая позволит гранулярно "отправлять" пользователей в различные дата-центры. Использование Geo-based DNS целессобразно для разграничения пользователей по регионам с помощью настройки GeoIP-базы, что может использоваться для персонализации контента, например, региональной рекламы.

Также откажемся технологий:

  • Latency-based DNS
  • Использование CDN в качестве хаба для обеспечения более стабильного соединения

Как уже было сказано, задержка (Latency) для данного сервиса не важна, поэтому Latency-based DNS здесь не имеет смысл. Использование CDN в качестве хаба для обеспечения более стабильного соединения не нужен, т.к. нет необходимости в стабильном и качественном подключении для создания событий календаря.

3.3 Отказоустойчивость

Для обеспечение надёжности необходимо решить несколько вопросов:

  • резервирование ДЦ в случае отказа всех в данном регионе (это возможно ввиду небольшого их числа).
  • пропускная способность ДЦ в случае прилива аудитории (например, в случае отказа одного из ДЦ).

Резервирование в рамках одного региона будет осуществляться BGP Anycast, который будет перенаправлять трафик из одного ДЦ в другой в рамках одного региона (это актуально для США). В случае остальных регионов балансировка будет осуществляться с помощью Geo-based DNS, который будет перенаправлять трафик из одного региона в другой.

Пропускная способность ДЦ в данном случае гарантирована тем, что часть трафика можно распределить между отдельными ДЦ (например, между Европейским и Американскими), а также тем, что для своих регионов были выбраны крупнейшие ДЦ.

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

Для балансирования межсервисных запросов наиболее рационально выбрать L7 балансировщик, т.к. у него много достоиств, несмотря на возросший overhead, следовательно, Latency, что не является критическим для сервиса календаря.

4.1. Входящий трафик

Входящий трафик будем балансировать с помощью протокола HSRP.

Один из способов добиться доступности сети, близкой к 100%, — использование протокола HSRP, который обеспечивает избыточность IP-сетей, предоставляя возможность немедленного прозрачного восстановления пользовательского трафика в случае отказа первого перехода в оконечных сетевых устройствах или каналах доступа.

Благодаря совместному использованию IP-адреса и MAC-адреса (уровень 2), два и более маршрутизаторов могут действовать как единый "виртуальный" маршрутизатор. Члены группы виртуальных маршрутизаторов непрерывно обмениваются сообщениями о состоянии. Это позволяет маршрутизаторам подменять другие маршрутизаторы при их запланированном или незапланированном отключении. Узлы продолжают переадресовать IP-пакеты на согласованный IP- и MAC-адрес, и аварийное переключение устройств, производящих маршрутизацию, происходит незаметно для пользователей и приложений.

4.2 Межсервисные запросы

Межсервисные запросы будут балансироваться с помощью Sticky Session. В качестве стратегии балансировки выберем алгоритм Round Robin в nginx из его Ingress-контроллера, т.к. предполагается, что будет использоваться неблокирующий ЯП для backend-сервисов.

Для улучшения производительности будет использоваться Session Cache, который будет кэшировать SSL-сессии.

Для уменьшения пересылаемого трафика будет осуществляться сжатие gzip.

4.2 Оркестрация сервисов

Также используем оркестрацию сервисов через K8, что позволит:

  • выполнять auto-scaling для полного использования ресурсов доступных машин.
  • service discovery для оценки работоспособности сервиса.
  • распределять экземпляры сервиса по узлам.

4.3 Отказоустойчивость

Для обеспечения отказоустойчивости используем дополнительный keepalived, который является:

  • Программным монитором доступности нод.
  • Сигнализирует балансеру при падении/подъёме ноды.
  • Умеет VRRP/CARP резервирование нод между собой для обеспечения отказоустойчивости самих балансировщиков.

В VRRP/CARP хосты разделяют один ip-адресс, но активен он только на одном их хостов, а на другом находится в виртуальном интерфейсе, но не анонсируется в сеть. Поэтому при прекращении работы одной из машин тот же самый ip-адресс поднимается на другой машине и туда переносится весь трафик.

Балансеров устанавливаем в 2 раза больше, чем нужно для пикового трафика, ставим их попарно.

Проверку работоспособности сервиса будем проводить с помощью livenessProbe в K8.

4.3 Терминация SSL

Т.к. уже используется L7 балансировщик nginx, то терминацию SSL также осуществляем с помощью nginx.

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

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

5.1 Размеры данных в таблице БД

При расчёте размера таблиц принимаются те же размеры данных, что и при расчёте нагрузки

User Calendar Event Session User_to_calendar User_to_event Timezone Error
id (8 B) id (8 B) id (8 B) id (8 B) id (8 B) id (8 B) id (8 B) id (8 B)
time_zone_id (8 B) time_zone_id (8 B) calendar_id (8 B) user_id (8 B) user_id (8 B) user_id (8 B) time_zone (8 B) event_id (8 B)
email (255 B) owner_id (8 B) user_id (8 B) created_at (8 B) calendar_id (8 B) event_id (8 B) name (64 B) payload (1 KiB)
username (300 B) name (384 B) creation_date (8 B) expired_at (8 B) access (1 B) error_code (2 B)
hash_password (72 B) description (3072 B) update_date (8 B)
telephone (15 B) creation_date (8 B) start_date (8 B)
country (165 B) update_date (8 B) end_date (8 B)
city (150 B) name (384 B)
description (3072 B)
reccurent (1 B)
creation_period (8 B)
Всего 978 B 3496 B 3521 B 32 B 25 B 24 B 80 B 1042 B

5.2 Примерные размеры данных и нагрузка на чтение и запись

Наибольшую сложность для оценки представляют данные об участниках календарей и событий, поэтому проведём оценку по нижней границе, предположив, что пользователей в календаре не превышает 10, а количество событий в день составляет около 5 при числе участников, не превышающих 10, откуда за 5 лет получим 5 * 10 * 5 * 365 = 91250 шт. Для оценки размера таблицы Error необходимо знать количество уведомлений и напоминаний, которые будут возвращать ошибку. Для приблизительной оценки примем, что это 5% от общего числа, т.е. 2 * 173612 (пиковый RPS) * 0.05 = 17361.2 (примем 17632).

В следующей таблице приведены примерные данные о суммарном объёме данных в БД.

Для оценки нагрузки на чтения для таблиц User, Calendar и Event возьмём данные пикового RPS.

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

Нагрузка на запись и чтение для таблицы Error сопоставима с нагрузкой на чтение.

Таблица Размер данных Нагрузка на чтение Нагрузка на запись
User 873 B * 3 млрд. = 2.38 TiB 500 млн. / 86400 = 5787 579
Calendar 3496 B * 500 млн. * 5 шт. = 7.95 TiB 173612 17362
Event 3521 B * 500 млн. * 5475 шт. = 8.56 PiB 34724 3473
Session 32 B * 500 млн. = 14.9 GiB 500 млн. / 86400 = 5787 5787
User_to_calendar 25 B * 500 млн. * 10 шт. = 116.4 GiB - -
User_to_event 25 B * 500 млн. * 91250 шт. = 1.01 PiB - -
Timezone 80 B * 38 шт. = 3040 B 5787 + 173612 = 179 399 0
Error 1042 B * 17632 шт. = 17.52 MiB 173612 * 2 * 0.05 = 17362 17362

6. Физическая схема БД

6.1 Денормализация БД

Для денормализации БД проведём следующие преобразования:

  1. Уберём таблицу Timezone, чтобы не делать дорогостоящий JOIN, и будем хранить информацию о временной зоне в таблицах User и Calendar напрямую в виде строки name и timespampz time_zone.
  2. Пользователей, приглашённых на событие, добавим в качестве массива members в таблицу Event.
  3. Пользователей, приглашённых в календарь, и их уровни доступа добавим в качестве словаря members_with_access в таблицу Calendar.

Денормализованная БД

6.2 Выбор баз данных

В случае сервиса календаря нагрузка на чтение намного превышает нагрузку на изменение/создание, поэтому для хранения информации о пользователях будем использовать релиционную СУБД PostgreSQL, т.к. она очень хорошо справляется с таким типом нагрузки и обладает высокой надёжностью.

Для хранения сессий пользователей будем использовать неряляционную СУБД Redis, т.к. она осуществяет хранение данных in-memory, имеет поддержку неблокирующей репликации master-slave и возможность организации кластера Redis cluster. Сессии наиболее рационально хранить в in-memory базе, т.к. данный тип данных довольно часто меняется и важно получать быстро данные о сессии пользоваталей, при этом в случае падения базы не произойдёт потери критически важной информации, как, например, данных пользователей. Использование SQL-решения здесь неэффективно, т.к. при одинаковом оборудовании разница в производительности будет примерно в 10 раз быстрее в сторону Redis.

Также в Redis будем хранить информацию об ошибках при отправке уведомлений и напоминаний, т.к. частота записи в данном случае сопоставима с частотой чтения.

Таблица База данных
User PostreSQL
Calendar PostreSQL
Event PostreSQL
Session Redis
Error Redis

6.3 Шардирование

Используем стандартное горизонтальное шардирование, в котором:

  • Таблицу User шардируем по ключу id.
  • Таблицу Calendar шардируем по ключу id.
  • Таблицу Event шардируем по ключу id.

Ключами шардирования для каждой из таблиц выступают id, чтобы обеспечить псевдоравномерное распределение нагрузки между шардами и избежать случая, когда несколько шардов перегружены в сравнении с остальными (например, случая, когда один шард нагружен на 70%, а другой — на 30%).

6.4 Реплицирование

В качестве схемы репликации будем использовать стандартную схему с физической репликацией. Одна база данных на запись master и много на чтение slave, так как запросов на чтение в приложении будет на несколько порядков больше, чем на запись.

6.5 Индексирование

Индексирование проведём, исходя из критического функционала приложения.

Таблица User

  • Индекс на email для быстрого поиска и проверки уникальности.

Таблица Calendar

  • Индекс на owner_id для поиска календарей, созданных пользователем.

Таблица Event

  • Индекс на start_date и end_date (комбинированный индекс) для быстрого поиска событий в определённый период времени.
  • Индекс на calendar_id для поиска событий календаря.
  • Индекс на owner_id для поиска событий, созданных пользователем.

6.6 Резервное копирование

Для резеврного копирования используем следующие подходы [17]:

  • Еженедельное полное резервное копирование.
  • Ежедневное непрерывное инкрементное резервное копирование.

6.7 Клиентские библиотеки

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

  • Для PostgreSQL: pgx.
  • Для Redis: go-redis.

7. Алгоритмы

7.1 Особенности шардирования

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

Для решения этой проблемы было принято решение испльзовать в качестве ключа шардирования id для каждой из таблиц User, Calendar и Event, т.к. ввиду псевдослучайной генерации id между пользователями с разной активностью будет создано псевдоравномерное распределение нагрузки между шардами.

7.2 Особенности использования брокера сообщений Apach Kafka

В календаре для двух типов данных (уведомление и напоминание) необходимо использовать Apach Kafka, т.к. каждый из них требует отправки в определённое время. Под уведомлением здесь следует понимать сообщение, оправленное на почту со временем события и опросом о готовности пользователя прийти на ней, а под напоминанием — такое же уведомление, но за небольшое время до начала встречи, например, 15 минут.

При использовании Kafka возникают сразу несколько проблем:

  • Возможность "торможения" очереди из-за возможных ошибок при отправке.
  • Определение событий, для которых нужно оправить напоминание.

Первую из проблем можно решить с помощью дополнительной таблицы Error, которую необходимо разместить в отдельной БД, например, Redis, чтобы микросервис отправки сообщений при любой возможной ошибке не тормозил очередь, а отправил в эту БД данные для отправки с указанным кодом ошибки, а далее уже в зависимости от кода будет отпределяться предпринимать ли новую попытку отправки (эта попытка снова проходит через Kafka).

Для решения второй проблемы существует несколько вариантов:

  • Хранить все напоминания, которые создаются при создании события, а при редактировании также редактируются.
  • Генерировать их динамически.

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

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

8.1 Основные технологии

Технология Область применения
TypeScript Языке программирования frontend
React Frontend framework
Go Язык программирования backend
PostgreSQL Основаная реляционная СУБД для хранения данных
Redis Нереляционная СУБД для хранения сессий пользователей и ошибок в Apach Kafka при отправке уведомлений и напоминаний
nginx L7 балансировщик нагрузки
Apach Kafka Брокер сообщений для создания очереди отправки уведомлений и напоминаний пользователям
Prometheus Сбор статистики
Grafana Визуализация статистики
Kubernetes Оркестрация контейниризованных сервисов

8.2 Описание технологий

  1. TypeScript — статически типизируемый язык программирования для frontend, который транслируется в JavaScript код. Основное преимущество данного языка в том, что при масштабировании проекта могут возникать ошибки, которые намного проще заметить на этапе компиляции. Также данный язык поддерживает новые функции ECMAScript и модульность, как и JavaScript.
  2. React — один из самых распространённых framework для frontend-разработки, с помощью которого можно существенно ускорить разработку приложения и улучшить его производительность за счёт реактивности из коробки. Также благодаря своей популярности данный framework имеет огромную кодовую базу, довольно невысокий порог вхождения и хорошую масштабируемость дл поддержки крупных проектов, что делает его применение наиболее рационалным.
  3. Go — компилируемый многопоточный язык программирования backend, который благодаря своей неблокирующей модели многопоточности очень хорошо подходит для разработки высоконагруженных проектов, что, учитывая довольно простой порог вхождения в него, делает данный язык программирования хорошим выбором для высоконагруженного сервиса.
  4. PostgreSQL — реляционная СУБД с открытым исходным кодом, которая очень хорошо масштабируется и хорошо работает с нагрузкой в случае, когда запросов на чтение существенно больше, чем запросов на запись. Также обеспечивает надёжноси, т.к. обладает механизмами сохранения целостности данных, транзакционной безопасности и отказоустойчивости.
  5. Redis — неряляционная in-memory СУБД, которая в случае данного приложения находится перед "настоящей" базой данных. Данная СУБД используется для хранения сессий пользователей, т.к. они относительно редко меняются и к ним часто обращается приложение и они не относятся к критически важным. Также, в отличие от Memcached имеет поддержку транзакций, очереди, поддерживает репликацию и кластеризацию.
  6. nginx — используется в качестве L7 балансировщика нагрузки, т.к. очень хорошо изучен сообществом, имеет очень высокую производительности и гибкость настройки.
  7. Apach Kafka — используется для создании очереди отправки уведомлений и напоминаний пользователям. В качестве основных критериев выбора данного решения: масштабируемость (позволяет распределять данные по нескольким серверам), скорость (разеляет потоки данных), надёжность (разделы распространяются и реплицируются на многих серверах).
  8. Prometheus — сбор метрик с их последующим хранением.
  9. Grafana — платформа для визуализации собранных данных.
  10. Kubernetes — открытое программное обеспечение для оркестровки контейнеризированных приложений, автоматизации их развёртывания, масштабирования и координации в условиях кластера.

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

9.1 Надёжность компонентов

Компонент Метод Обоснование
Глобальная балансировка Geo-based DNS, BGP Anycast, фильтрация трафика L4 балансировщиком В случае падения одного из ДЦ резервирование достигается с помощью распределения нагрузки между остальными ДЦ с помощью Geo-based DNSи BGP Anycast, а фильтрация входящего трафика производится, например, компанией qrator, чтобы защититься от входящих атак до уровня ДЦ.
nginx резервирование ресурсов и сервисов, использование VRRP/CARP Ввиду того, что балансировщик является критически важным компонентом системы, необходимо обязательно резервировать под него ресурсы. Это включает в себя дополнительные серверы, которые с помощью механизма VRRP/CARP, будучи установленными в паре, резервируют друг друга.
PostgreSQL Репликация Ввиду критической важности для бизнеса, информация пользователей должна храниться в нескольких копиях, чтобы в случае отказа одной из БД не произошла потеря данных. Также использование репликации позволит снизить нагрузку на основую master БД, т.к. запросов на чтение существенно больше запросов на запись.
Redis Репликация Сессии пользователей не представляют ценности для бизнеса, но их всё равно необходимо дублировать, чтобы потом не произошёл заброс трафика ввиду единомоментной авторизации всех пользователей региона, а также это может повредить бизнесу косвенно, негативно повлияв на общее представление о приложении. Данные об ошибках необходимо резервировать, т.к. информация из уведомлений очень важна для пользователей.
API Gateway Резервирование логики Главный шлюз является узким местом системы, поэтому имеет смысл реплицировать его для повышения пропускной способности и предотвращения отказа всего сервиса в случае отказа основного шлюза.
Kafka Репликация Ввиду того, что брокер сообщений является критически важным элементом архитектуры, обеспечивающим важный аспект бизнес-логики, необходимо реплицировать его для обеспечения отказоустойчивости.

9.2 Механизмы надёжности сервиса

  • Использование Graceful Shutdown для обеспечения безопасного релизов новых версий приложения (не должно запускаться никаких новых процессов и не должно приниматься никаких новых веб-запросов и закрытия всех открытых соединений с внешними сервисами и базами данных).
  • Использование Graceful Degradation для обработки отказов определённых компонентов, чтобы при отказе одних микросервисов не происходил отказ всего приложения.
  • Использование на стороне клиента offline-логики для более бесшовной работы в случае обрыва интернет соединения.
  • Использование на стороне сервера для уменьшения посылаемых запросов на проблемный хост и использование curcuit breaker паттерна для предотвращения каскадного отказа сервисов.
  • Использование кеширования данных на стороне сервиса в случае отказа БД с последующей их записью при восстановлении её работоспособности.
  • Использование логирования, мониторинга и оповещений для своевременного реагирования на возможные ошибки со стороны сервисов.

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

10.1 Описание микросервисов

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

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

Микросервисы:

  • Авторизации (auth).
  • Пользователей и календарей (users and calendars).
  • Событий (event).
  • Уведомлений (notification).
  • Ошибок (error).

Микросервис auth пользователей отвечает, соответственно, за авторизацию пользователей, что выражается также в виде взаимодействия с БД Users Storage, в которой хранятся сессии пользователей.

Микросервис users and calendars объединяет в себе логику получения данных пользователя и логику обработки данных календаря. Решение объединить эти две функции в один микросервис обусловлено тем, чтобы не плодить искуссвенные микросервисы, т.к. никогда данные календаря не запрашиваются отдельно от данных пользователя и наоборот. Этот микросервис взаимодействует с БД, в которой хранятся данные о пользователях и их календарях.

Микросервис events вынесен отдельно от пользователей и календарей по причине того, что именно события в отличие от календарей ежеминутно посылают запрос в БД для нахождения тех событий, для которых нужно отправить напоминание. Также этот микросервис ответственен за оправку таких событий в брокер сообщений Apach Kafka. Следовательно, добавление этой логики, а также логики взаимодействия с событиями, привело бы к не нужным усложнениям и созданию монолита в случае объединения с микросервисом users and calendars. Этот микросервис взаимодействует с БД, в которой хранятся данные о событиях.

Микросервис notifications отвечает за отправку уведомлений (при создании/изменении) и напоминаний (за определённое время до) о предстоящих событиях. Он напрямую взаимодействует с микросервисом error, т.к. в некоторых случаях возможны ошибки отправки.

Как указано выше, при отправке уведомлений возможны некоторые ошибки, которые обрабатывает сервис error. В зависимости от кода ошибки он принимает решение о повторной попытке либо о удалении этого уведомления/напоминания. Все будущие уведомления/напоминания, для которых будет предпринята ещё одна попытка отправки, кладутся в соответствующую БД. Эта БД также, как и в случае БД с событиями, ежеминутно сканируется и передаёт извлечённые данные обратно в микросервис error. Далее микросервисом эти данные передаются снова в брокер сообщений Apach Kafka.

10.2 Схема отказоустойчивости

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

  1. Случай возможного отказа ДЦ в одном из регионов.

Как было описано в п.3 про глобальную балансировку, такой сценарий должен обрабатываться механизмом Geo-based DNS, который перенаправляет пользователей из одного региона в другой.

  1. Резервирование информации при отказе одного из ДЦ.

Описанный выше сценарий обработки отказа ДЦ в одном из регионов невозможен без резервирования информации, т.к. иначе может сложиться такая ситуация, что при хранении данных всех пользователей, например, из азиатского региона на сервере в Мумбаи (Индия) и отказе этого сервера, всех пользователей перенаправит, например, на европейский сервер во Франкфурте (Германия), но их данных там не окажется. Т.о., сложится ситуция, при которой пользователям не будут доступны их данные, а это сильно повлияет на репутацию продукты и, следовательно, доходы бизнеса.

Поэтому необходимо резервировать ДЦ попарно, учитывая фактор катастрофоустойчивости, т.е. что ДЦ должны находиться друг от друга на расстояни не менее 1000 км. Следовательно, в этом случае для ДЦ в Мумбаи (Индия) назначим резервным ДЦ во Франкфурте (Германия) и наоборот. Для ДЦ в Нью-Йорке (восточное побережье США) назначим резервным ДЦ в Лос-Анджелесе (западное побережье США) и наоборот.

Пересылка информации будет осущетвляться по внутренним 100 Гбит/с линиям, как на серверах Amazon, что возможно, учитывая общую тенденцию роста данных, т.к. данные не будут копироваться все сразу, а лишь те, которые изменились.

11. Расчёт ресурсов

11.1 Таблица оценки производительности ЯП

Для оценки производительности Go будем опираться на таблицу из методических указаний [19]

Учтём, что согласно бенчмарку [20] производительности C++ и Go ориентировочно равны (если брать одинаковый уровень написания приложения), однако по памяти Go затратнее, чем C++, поэтому примем ориентировочно, что для Go требуется на 20% больше памяти, чем для C++.

Тогда таблица для оценки производительности будет иметь следующий вид.

ЯП Характер сервиса RPS RAM
Go Тяжёлая бизнес-логика 1 120 MiB
Go Средняя бизнес-логика 100 120 MiB
Go Лёгкая бизнес-логика (JSON API) 5000 12 MiB
Nginx SSL handshake (CPS) 500 10 MiB

11.2 Расчёт нагрузки на сервисы

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

Допущения:

  • Пусть cookies выдаётся пользователю каждый день в течение первого захода на сайт [18].

  • Данные о нагрузке на сервис по RPS возьмём из пункта про расчёт нагрузки.

  • Данные по нагрузке на сервис auth возьмём, как RPS для просмотра календарей, т.к. это всегда требует обращения в этот сервис.

  • Нагрузку на сервисы, совмещающие несколько функций, например, как микросервис users and calendars, представим в виде суммы нагрузок.

  • Данные о нагрузке на API Gateway представим в виде суммы нагрузок на все сервисы, связанные с календарями и событиями, т.к. он является медиатором системы.

  • Данные о нагрузке на БД возьмём из пункта про логическую схему БД.

  • Для компенсации возможной неточности расчёта между средней и лёгкой бизнес-логикой в плане RAM установим минимальное значение RAM в 8 GiB.

Сервис Пиковый RPS CPU cores RAM Пиковый трафик
auth 173612 (просмотр календарей) = 173612 1737 206 GiB 382.6 Гбит/сек
users and calendars 173612 (просмотр календарей) + 34724 (взаимодействие с календарями) + 173612 (ответы на уведомления) = 381948 3820 448 GiB 382.6 Гбит/сек + 14.5 Гбит/с + 5.5 Гбит/с = 402.6 Гбит/с
events 34724 (взаимодействие с календарями) = 34724 348 40.8 GiB 14.5 Гбит/с
notifications 173612 (отправка напоминаний) + 173612 (отправка уведомлений) = 347224 70 8 GiB 21.66 Гбит/с + 21.66 Гбит/с = 43.32 Гбит/с
error (173612 (отправка напоминаний) + 173612 (отправка уведомлений)) * 0.1 = 34723 348 40.8 GiB 4.34 Гбит/с
API Gateway 381948 (календари) + 34724 (события) = 416671 4167 488.3 GiB 402.6 Гбит/с + 14.5 Гбит/с = 417.1 Гбит/с

11.3 Расчёт нагрузки на СУБД

  • Все данные дублируются и для учёта возможных неточностей умножим данные для диска, RAM и CPU минимум на 2.5.
  • Минимальный объём диска установим в 4 GiB.
  • Нагрузку на запись примем 25% от нагрузки на чтение, учитывая дополнительно пересылку данных для резервирования между ДЦ.
Хранилище Объём диска Пиковый RPS на чтение Пиковый RPS на запись CPU cores RAM Нагрузка на чтение Нагрузка на запись
Redis (sessions storage) 37.25 GiB 5787 5787 58 * 2.5 = 174 14.13 GiB 382.6 Гбит/сек 95.65 Гбит/сек
Redis (errors storage) 4 GiB 17362 17362 174 * 2.5 = 435 43 GiB 4.34 Гбит/с 1.09 Гбит/сек
PostgreSQL (users and calendars storage) (2.38 TiB + 7.95 TiB) * 2.5 = 25.83 TiB 5787 + 173612 = 179399 579 + 17362 = 17941 180 * 2.5 = 450 43.8 GiB 402.6 Гбит/с 161.04 Гбит/сек
PostgreSQL (Events storage) 21.4 PiB 34724 3473 35* 2.5 = 88 8.5 GiB 14.5 Гбит/с 3.63 Гбит/сек

11.4 Выбор серверного оборудования

Ввиду того, что важно выбирать серверы с хорошим соотношением частоты ядра на количество ядер, выберем серверы с процессорами AMD EPYC.

Сервис Хостинг Конфигурация CPU cores Количество Покупка, $ (1 шт) Амортизация, $/мес (сумма на 5 лет)
auth own 2x AMD EPYC 9654 (96C Cache L3 384M Cache 2.40GHz) / 768 (24x 32GB) / 3xSSD 800GB (до 24 HDD 2.5") / H755 / 2x БП 2400W 192 10 38222 6371
users and calendars own 2x AMD EPYC 9654 (96C Cache L3 384M Cache 2.40GHz) / 768 (24x 32GB) / 3xSSD 800GB (до 24 HDD 2.5") / H755 / 2x БП 2400W 192 20 38222 12742
events own 2x AMD EPYC 9654 (96C Cache L3 384M Cache 2.40GHz) / 768 (24x 32GB) / 3xSSD 800GB (до 24 HDD 2.5") / H755 / 2x БП 2400W 192 2 38222 1275
notifications own 1x AMD EPYC 9654 (96C 384M Cache 2.40 GHz) / 2x 16GB DDR5 RDIMM 4800MHz (Поддержка до 6TB максимально, 24 DIMM портов) / HPE MR216i-p Gen11 (ZM) / noHDD (до 48 HDD 2.5'' SFF) / 1x HP 800W 192 2 38222 1275
notifications own 1x AMD EPYC 9654 (96C 384M Cache 2.40 GHz) / 2x 16GB DDR5 RDIMM 4800MHz (Поддержка до 6TB максимально, 24 DIMM портов) / HPE MR216i-p Gen11 (ZM) / noHDD (до 48 HDD 2.5'' SFF) / 1x HP 800W 96 1 14126 236
error own 2x AMD EPYC 9654 (96C Cache L3 384M Cache 2.40GHz) / 768 (24x 32GB) / 3xSSD 800GB (до 24 HDD 2.5") / H755 / 2x БП 2400W 192 2 38222 1275
API Gateway own 2x AMD EPYC 9654 (96C Cache L3 384M Cache 2.40GHz) / 768 (24x 32GB) / 3xSSD 800GB (до 24 HDD 2.5") / H755 / 2x БП 2400W 192 22 38222 14017

Список использованных источников

  1. https://explodingtopics.com/blog/google-workspace-stats
  2. https://zipdo.co/statistics/google-calendar/
  3. https://marketsplash.com/google-workspace-statistics/
  4. https://www.patronum.io/key-google-workspace-statistics-for-2023/
  5. https://www.similarweb.com/website/calendar.google.com/#geography
  6. https://emaillistvalidation.com/blog/demystifying-email-validation-understanding-the-maximum-length-of-email-addresses/#:~:text=Defining%20the%20Maximum%20Length&text=Domain%20Part%3A%20The%20domain%20part,email%20address%20is%20320%20characters.
  7. https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
  8. https://www.geekslop.com/technology-articles/2016/here-are-the-recommended-maximum-data-length-limits-for-common-database-and-programming-fields#:~:text=35%20chars%20(US)%2C-,50%20(other),-Last%20name
  9. https://www.postgresql.org/docs/8.2/datatype-datetime.html
  10. https://stackoverflow.com/questions/24138581/what-is-the-maximum-allowed-expiration-time-for-a-google-notification-channel
  11. https://hypestat.com/info/calendar.google.com
  12. https://webstatsdomain.org/d/calendar.google.com
  13. https://www.visualcapitalist.com/cp/top-data-center-markets/
  14. https://support.google.com/calendar/answer/37161?hl=en&co=GENIE...&co=GENIE.Platform%3DDesktop#zippy=%2Cguest-limit-for-invitations
  15. https://stackoverflow.com/questions/10229156/how-many-characters-can-utf-8-encode
  16. https://support.google.com/a/answer/2905486?hl=en
  17. https://backupsolution.ru/backup-types/
  18. https://developer.chrome.com/blog/cookie-max-age-expires?hl=ru
  19. https://github.com/init/highload/blob/main/highload_l11_hosting.md
  20. https://www.techempower.com/benchmarks/#section=data-r22&test=json&hw=ph&l=zii9rz-cn3

highload_google_calendar's People

Contributors

wolpy01 avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.