Требования к ресурсам¶
Данный документ содержит:
требования процессов репликаторов;
расчет размера событий;
принцип работы сборщика мусора в Go;
информация о переменных
GOGC
иGOMEMLIMIT
;расчет компонентов Gateway и Destination;
пример практического тестирования компонентов TCF в условиях штатной и холодной нагрузки.
Требования процессов репликаторов¶
Таблица ниже описывает требования процессов репликаторов Gateway и Destination к ресурсам сервера.
Gateway – компонент TCF, который подключается к исходному кластеру как анонимная реплика и подписывается на его обновления.
Оперирует структурами типа Event
– событиями репликации, которые передаются по протоколу gRPC компоненту Destination.
Destination – компонент TCF, который делает запрос на Gateway (подписывается по gRPC), забирает репликационные события и с помощью них пишет изменения в целевой кластер.
Подробнее см. в разделе Репликаторы данных.
Компонент |
CPU |
RAM |
Disk (записей журнала в сутки) |
Комментарии |
---|---|---|---|---|
Gateway |
1 vCPU на 2 шарда |
~11,5 МБ |
~823 МБ |
Stateless, RAM зависит от размера события и параметров буфера. В конфигурации – |
Destination |
1 vCPU на 2 шарда |
~52,5 МБ |
~55 МБ |
Stateless, RAM выше из-за хранения всех входящих событий и сериализации. |
Оба |
Пропорционально шардам |
< 100 МБ |
< 1 ГБ |
Все записи журнала идут в stdout/stderr (по умолчанию), это можно ограничить через системные средства. В конфигурации – |
Расчет размера события (Event)¶
Каждое событие (Event) передаётся по сети между Gateway и Destination. Оценка его размера помогает точнее понять нагрузку на сеть, сериализацию и память.
Событие содержит набор полей различного типа: строки (string
), числа (int64
, int32
), булевы значения (bool
) и байтовые массивы ([]byte
).
Для оценки размера одного события в байтах можно использовать формулу:
EventSize = 188+4*X
Здесь:
188
– суммарный размер основных полей (UUID
,bool
,TSN
,InstanceID
,ConfirmedVClock
,EventType
и др.) в байтах;X
– оценочный размер одного кортежа (Updates
,Before
,After
,Arrow
,PK
) в байтах;поля
Updates
,Before
,After
,Arrow
,PK
опциональны. Если задействованы все четыре поля, то добавляется4×X
.
Пример: если средний размер одного кортежа составляет 200 байт, то итоговый размер события ≈ 188+4×200 = 988 байт.
Расчет компонента Gateway¶
CPU¶
Требуется 1 vCPU на 2 шарда при наличии гиперпоточности.
Тестирование проводилось на 4 vCPU / 14 потоков.
RAM¶
Нагрузка была посчитана с учетом прохождения через оба репликатора, надо предположить нагрузку отдельно для Gateway, опираясь на данные из таблицы.
Потребление памяти посчитано по следующей формуле:
RAM=EventSize(EPS⋅GC+BufferSize)+C
Здесь:
GC
– время между концом работы сборщика мусора и началом следующей итерации;ShardsNum
– количество наборов реплик в кластере;BufferSize
– размер буфера для Gateway;EventSize
– размер события;C
– объем памяти, который приходится на второстепенное выделение объектов программы.
Среднее время срабатывания сборщика мусора при активной нагрузке составило 10 секунд.
Так как на каждый набор реплик выделяется буферизированный канал, куда складываются события, необходимо учесть их в формуле.
Сейчас размер буфера константный и равен BufferSize
= 1024.
Размер кортежа в тестах взят за 20 байт. EventSize
будет равен 180+4*20 = 260 байт.
Остальная память уходит на:
создание горутин: на пике 83, каждая по 2 Кб, итого 166 Кб;
создание http-сервера: около 3 Кб;
создание gRPC-сервера: около 1 Кб;
139446 выделений памяти;
прочее. Прочие расходы округлены до
C
= 0,5 МБ.
Подставив все найденные значения в исходную формулу, получается:
RAM=(260⋅3000⋅10)+(1024⋅260)+(5⋅105)=8,5 МБ
Практически количество используемой памяти при активной нагрузке Gateway представлено на диаграмме ниже. На пике процесс расходует 11,5 Мбайт RAM.
Disk¶
Gateway является stateless-сервисом. Это означает, что Gateway не пишет на диск ничего кроме записей журнала.
При проведении нагрузки были получены следующие результаты при уровне журналирования INFO
:
2,6 КБ на Gateway, подключенный к активному кластеру;
569 КБ на Gateway, подключенный к пассивному кластеру.
Например, если такая нагрузка будет идти сутки, то на диск запишется (2600 × 60 × 24) + (56000 × 60 × 24) = 3744000 + 819360000 = 823104000 байт ≈ 823 МБ/сут.
Расчет компонента Destination¶
RAM¶
При расчете будет использована та же формула, по которой считался RAM для Gateway. Отличаться будет лишь то, что у Destination будет ровно ShardsNum
буферизированных канала размера BufferSize
.
Общий размер буфера канала, куда клиент по gRPC получает события, равен 1. Размер BufferSize
настраивается в конфигурации, в отличие от Gateway, и при тесте был равен 10000.
Destination на вход получает 6000 EPS от Gateway, а на выходе имеет 5000 EPS, возьмем оценку сверху – 6000 EPS для переменной EPS
.
GC
в среднем, аналогично Gateway, срабатывает каждые 10 секунд. Event
будет браться без дополнительного коэффициента, так как объект десериализуется из protobuf
и в дальнейшем из него формируется запрос на целевой кластер, который составляет 180+4*20 = 240 байт.
Остальная память уходит на:
создание горутин: на пике 48, каждая по 2 Кб, итого 96 Кб;
268917 выделений памяти;
прочее. С учетом выделений памяти примерно в два раза больше, чем у Gateway, прочие расходы округляются до 0,5 МБ.
Итого:
RAM=(10×10000×2×260)+0,5МБ=52,5МБ
Количество используемой памяти Destination по результатам нагрузки представлено на диаграмме ниже:
Disk¶
Destination является stateless-сервисом. Это означает, что Destination не пишет на диск ничего кроме записей журнала.
При проведении нагрузки были получены следующие результаты:
14 Кб на Destination, подключенный к пассивному кластеру. Destination простаивает, так как он подключен на текущий момент к активному кластеру. Следовательно, компонент не пишет ничего кроме общей информации о статусах системы;
24 КБ на Destination, подключенный к активному кластеру.
Например, если такая нагрузка будет идти сутки, то на диск запишется (14000 x 60 x 24) + (24000 x 60 x 24) = 54720000 = 54,7 МБ/сут.
Сборщик мусора в Go¶
Информация взята из официальной статьи разработчиков про сборщик мусора в Go.
Механизм срабатывания сборщика мусора работает по следующей формуле:
TargetHeapMemory=LiveHeap+(LiveHeap∗GOGC/100)
Механизм определяет целевой размер кучи (TargetHeapMemory
) после каждого цикла сборки мусора, что является целевым значением общего размера кучи в следующем цикле.
Цель сборщика мусора – завершить цикл сборки мусора до того, как общий размер кучи превысит целевой размер (TargetHeapMemory
).
Общий размер кучи определяется как текущий размер кучи (LiveHeap
) в конце предыдущего цикла плюс любая новая память кучи, выделенная приложением с момента предыдущего цикла.
На формулу целевого размера кучи можно влиять извне, установив процент GOGC
в качестве переменной окружения.
GOGC¶
GOGC
задает порог роста кучи в процентах.
Значение по умолчанию: 100%. Таким образом, чем больше процент GOGC
, тем чаще будет запускаться сборщик мусора.
В качестве примера приведена программа на Go с размером активной кучи 8 МБ, 1 МБ стеков горутин и 1 МБ указателей в глобальных переменных.
Тогда при значении GOGC
, равном 100, объём новой памяти, которая будет выделена перед следующими запусками сборщика мусора, составит 10 МБ, или 100% от 10 МБ работы, что соответствует общему объёму кучи 18 МБ.
При значении GOGC
, равном:
50
– объём составит 50%, или 5 МБ;200
– объём составит 200%, или 20 МБ.
MGOMEMLIMIT¶
Еще одна переменная, на которую может повлиять пользователь – лимит памяти GOMEMLIMIT
. Начиная с версии Go 1.19 она позволяет указать жесткий лимит памяти, которое сможет выделять приложение.
Если приложение приближается к лимиту, то сборщик мусора запускается с повышенной частотой, даже если TargetHeapMemory
еще не достигнут. Таким образом время выполнения (runtime) старается удержать использование памяти ниже лимита.
Пример расчета нагрузки TCF¶
В этом разделе показано, как оценивать нагрузку на компоненты TCF (Gateway и Destination) по CPU, RAM и дисковому пространству на основе штатной и холодной нагрузки.
Штатная нагрузка¶
Штатная нагрузка – такая нагрузка, при которой все компоненты TCF запущены и постоянно реплицируют данные из одного кластера в другой. Опираясь на настоящие данные, для расчета компонентов Gateway и Destination было проведено нагрузочное тестирование стенда. Дальнейший расчет будет основываться на результатах этого теста.
Сценарий: в момент нагрузки активного кластера включается счетчик времени. Счетчик остановится, когда нагрузка закончится и все кортежи будут успешно реплицированы на пассивный кластер. Фактически ожидается, что количество кортежей во всех спейсах исходного кластера (Gateway) будет равно количеству кортежей во всех спейсах целевого кластера (Destination).
Информация о нагрузке:
100 потоков;
время нагрузки 5 минут.
Собранные метрики показывают количество событий, обрабатываемых каждым компонентом, что позволяет оценить нагрузку на систему. Сводная информация, представленная в таблице ниже – расчет количества событий в секунду для каждого компонента.
Компонент |
EPS |
---|---|
Gateway |
6000 |
Destination |
5000 |
Gateway + Destination |
5000 |
Холодная загрузка¶
Холодная нагрузка – это первый запуск репликаторов TCF для репликации всех данных кластера. Потребление памяти при этом не отличается от штатной нагрузки, так как ограничено размером буфера на стороне Gateway.
Go-replica имеет небуферизированный канал, в который изначально попадают все прочитанные события.
Далее через обработчик applier
они попадают в буферизированный канал на каждом наборе реплик на стороне Gateway.
После события попадают в централизованный буферизированный канал, который читается для отправки событий на Destination.
Небуферизированный канал в Go – это канал с нулевой емкостью, который блокируется на запись до тех пор, пока consumer
не прочитает из канала.
Таким образом, размер памяти, потребляемый Gateway в случае, когда нужно за раз вычитать большой объем данных, не будет приводить к непредвиденным утечкам памяти.
Сейчас размер буфера – это константа (1024). В дальнейшем с помощью этого параметра можно увеличить пропускную способность Gateway,
так как при изолированном замере производительности go-replica
может выдавать около 400000 EPS
при объеме буферизированного канала в 3000000.
Сравнение по количеству потребляемой памяти двух типов тестов, представленных выше:
Тип нагрузки |
Зависимость потребляемой памяти от времени |
---|---|
Штатная |
|
Холодная |
Из графиков видно, что вне зависимости от типа нагрузки, репликаторы будут расходовать одно и то же количество памяти.