Кстати, а есть ли у вас мысли, можно ли как-то ещё "смягчить" эту проблему, кроме классической буферизации? (например, более агрессивная упаковка батчей, или какие-то спец-режимы загрузки?)
S3 Express One Zone сократит затраты на PUT-запросы в 5 раз, но это не 500 как хотелось бы. А так больше идей нет... если пойти дальше по этому спектру, мы придем к tiered storage: по сути это создание больших буферов, за которые мы расплачиваемся межзонным трафиком (т.е., репликацией)
Начем с одного единственного брокера. Тут все просто: мы можем писать на S3 что угодно в любом удобном формате. Тут одна проблема, которая проходит красной нитью через всю эту историю: писать множество маленьких файлов дорого (PUT-запросы не бесплаты). Поэтому тут мы тоже придем в каком-то виде к буферизации на брокере, как в Diskless.
Если у нас появляются несколько брокеров, то ситуация усложняется: нам неизбежно в том или ином виде нужен sequencer, т.е. компонент, который будет так или иначе задавать порядок для батчей в партиции.
В S3 не так давно появились conditional writes, т.е. CAS. На этом можно сделать счетчик оффсетов: брокер бронирует оффсет для батча и загружает батч на S3 под именем, включающим этот оффсет. Помимо того, что это неприлично дорого (буферизацию мы сделать не можем), появляется возможность "дырок" в оффсетах (брокер "взял" оффсет и упал). В принципе, в Кафке нигде явно не сказано, что оффсеты идут строго последовательно без дырок (и compacted топики и транзакции это нарушают), так что это терпимо.
Становистя сложнее, если нам нужно сделать compacted топики. Для этого нужно переписывать уже загруженные файлы, причем атомарно. Нужны транзакции на файлах в каком-то виде. В принципе, "we can solve any problem by introducing an extra level of indirection": добавляем некую структуру метаданных сверху над файлами и на ней сможем сделать транзакции. Например, описывать множества файлов в мета-файлах и подменять метафайлы, если нужно сделать атомарную операцию на нескольких файлах. Так примерно работает Apache Iceberg.
Развесистая структура метаданных может позволить сохранить буферизацию. Например, брокер загружает файл с кучей данных для разных партиций, а потом пытается с помощью optimistic locking (т.е. того же CAS) вставить батчи в соответствующие партиции. Это уже в целом похоже на Diskless, где вместо базы или топика под batch coordinator'ом -- сам S3.
Можно посмотреть с другого конца: оставить лидеров партиций и использовать CAS не для счетчика оффсетов и не для атомарной замены метаданных партиции, а для выбора лидера. Будет работать примерно как лок: брокер берет лок на партицию (т.е. становится ее лидером) и дальше steady режим похож на обычную Кафку. Но немного непонятно как делать fencing: что, если брокер думает, что он все еще лидер, а он уже нет? Тогда случится split brain. Наверное, можно придумать, как сделать fencing также используя дополнительную структуру с мета-файлами (например, увеличивать версию корня, когда меняется лидер, т.е. добавить leader epoch). С похожей проблемой мы столкнулись в tiered storage (что, если два брокера загрузят один и тот же сегмент), но там вышло проще, потому что все же есть нормальные удобные метаданные в самой Кафке.
В общем, в S3 есть strong read-after-write consistency и CAS, а это универсальный примитив для разных алгоритмов синхронизации и транзакций. Быстро и дешево не будет, но можно сделать что угодно :)
Какой объём событий рассчитан на Batch Coordinator?
Batch Coordinator будет сделан в виде плагинов, поэтому будет зависеть от имплементации. Мы ожидаем около 100 байт метаданных на один батч данных + оптимизации, до которых еще не дошла очередь + всякие трюки по снижению количества батчей, которые хранятся долгосрочно. 1 триллион батчей должен потребовать около 100 ГБ метаданных. По современным меркам это можно хранить и запрашивать много в чем.
Можно ли его будет горизонтально масштабировать?
Та имплементация, что мы предлагаем в KIP-1164, сделана на топиках самой Кафки. Можно будет масштабировать, увеличивая количество партиций в топике с метаданными и разнося их лидеров по разным узлам.
Какие сейчас наблюдаются средние задержки доставки сообщений на прототипах?
Не буду пока говорить, чтобы цифры из этих ранних прототипов не остались потом в истории Гугла на годы :)
Обработка Produce-запроса состоит из трех частей:
Буферизация на брокере.
Загрузка буфера в хранилище.
Коммит.
1 конфигурируется пользователем. По дефолту будет 250 мс. При большом входящем трафике должно заполняться быстрее (там еще лимит по размеру буфера, 8 МБ по дефолту).
2 зависит от хранилища. Обычный S3 даст миллисекунд 200-300. S3 Express One Zone должен быть быстрее (мы еще не замеряли).
3 -- самое интересное и зависит от имплементации batch coordinator'а. Там маленькие транзакции и будет реалистично побороться за low two digits.
Какие юзкейсы лучше всего подходят под такую архитектуру? Где бы вы не советовали её использовать?
Все, где допустимы producer-consumer latency в, скажем, 1 секунду (p99). Т.е. почти все, кроме риалтайм аналитики/рекомендаций/рекламы и того, где на Кафку завязан интерактив с пользователем.
Через сколько версий, по вашему мнению, diskless-топики будут готовы к стабильному продакшену?
Сложно сказать. У tiered storage заняло 5,5 лет от публикации KIP до general availability. Большая часть времени прошла в обсуждениях дизайна. Надеюсь, у diskless года 3 займет :)
Альтернативная реализация от Aiven Open не может успешно вычитать записанные ей в удаленное хранилище данные. Для попытки починки необходимо более детально исследовать механизм chunk-ов.
Это вариант, но не то, что я сделал: в этом случае все равно придется установить gdbserver в контейнере.
Я сделал с помощью namespaces. GDB запускается в PID namespace целевого процесса и в новом mount namespace, аналогично nsenter -t <pid> -p unshare --mount-proc gdb ... -p 1. Нужно еще добавить -iex set sysroot /proc/1/root -iex set auto-load safe-path /proc/1/root/<solib paths> -iex set solib-search-path /proc/1/root/<solib paths>. Еще важно: если в mount namespace GDB есть такой же путь к бинарнику, как в mount namespace целевого процесса (условно /usr/bin/python3), то его нужно скрыть (с помощью bind mount, например), чтобы GDB читал символы из /proc/1/exe.
В каждом контейнере нашего кластера запущен демон, который просыпается раз в несколько минут в случайные моменты времени и присоединяется к профилируемому процессу с помощью GDB.
Кстати, я тут экспериментировал для одного своего проекта и мне удалось сделать так, что GDB подключается к процессу в контейнере, считывает символы там же, но при этом сам установлен и запущен на хосте. Т.е. технически в вашем случае можно было бы запускать один экземпляр демона.
Если так, то не позиционируйте себя как джуна. Делайте упор на свой общий инженерный опыт и т.п. Если человек умеет писать осмысленный код, покрывать его тестами, связно коммуницировать со всеми включенными в процесс, то это уже не джун.
S3 Express One Zone сократит затраты на PUT-запросы в 5 раз, но это не 500 как хотелось бы. А так больше идей нет... если пойти дальше по этому спектру, мы придем к tiered storage: по сути это создание больших буферов, за которые мы расплачиваемся межзонным трафиком (т.е., репликацией)
Давайте пофантазируем.
Начем с одного единственного брокера. Тут все просто: мы можем писать на S3 что угодно в любом удобном формате. Тут одна проблема, которая проходит красной нитью через всю эту историю: писать множество маленьких файлов дорого (PUT-запросы не бесплаты). Поэтому тут мы тоже придем в каком-то виде к буферизации на брокере, как в Diskless.
Если у нас появляются несколько брокеров, то ситуация усложняется: нам неизбежно в том или ином виде нужен sequencer, т.е. компонент, который будет так или иначе задавать порядок для батчей в партиции.
В S3 не так давно появились conditional writes, т.е. CAS. На этом можно сделать счетчик оффсетов: брокер бронирует оффсет для батча и загружает батч на S3 под именем, включающим этот оффсет. Помимо того, что это неприлично дорого (буферизацию мы сделать не можем), появляется возможность "дырок" в оффсетах (брокер "взял" оффсет и упал). В принципе, в Кафке нигде явно не сказано, что оффсеты идут строго последовательно без дырок (и compacted топики и транзакции это нарушают), так что это терпимо.
Становистя сложнее, если нам нужно сделать compacted топики. Для этого нужно переписывать уже загруженные файлы, причем атомарно. Нужны транзакции на файлах в каком-то виде. В принципе, "we can solve any problem by introducing an extra level of indirection": добавляем некую структуру метаданных сверху над файлами и на ней сможем сделать транзакции. Например, описывать множества файлов в мета-файлах и подменять метафайлы, если нужно сделать атомарную операцию на нескольких файлах. Так примерно работает Apache Iceberg.
Развесистая структура метаданных может позволить сохранить буферизацию. Например, брокер загружает файл с кучей данных для разных партиций, а потом пытается с помощью optimistic locking (т.е. того же CAS) вставить батчи в соответствующие партиции. Это уже в целом похоже на Diskless, где вместо базы или топика под batch coordinator'ом -- сам S3.
Можно посмотреть с другого конца: оставить лидеров партиций и использовать CAS не для счетчика оффсетов и не для атомарной замены метаданных партиции, а для выбора лидера. Будет работать примерно как лок: брокер берет лок на партицию (т.е. становится ее лидером) и дальше steady режим похож на обычную Кафку. Но немного непонятно как делать fencing: что, если брокер думает, что он все еще лидер, а он уже нет? Тогда случится split brain. Наверное, можно придумать, как сделать fencing также используя дополнительную структуру с мета-файлами (например, увеличивать версию корня, когда меняется лидер, т.е. добавить leader epoch). С похожей проблемой мы столкнулись в tiered storage (что, если два брокера загрузят один и тот же сегмент), но там вышло проще, потому что все же есть нормальные удобные метаданные в самой Кафке.
В общем, в S3 есть strong read-after-write consistency и CAS, а это универсальный примитив для разных алгоритмов синхронизации и транзакций. Быстро и дешево не будет, но можно сделать что угодно :)
Batch Coordinator будет сделан в виде плагинов, поэтому будет зависеть от имплементации. Мы ожидаем около 100 байт метаданных на один батч данных + оптимизации, до которых еще не дошла очередь + всякие трюки по снижению количества батчей, которые хранятся долгосрочно. 1 триллион батчей должен потребовать около 100 ГБ метаданных. По современным меркам это можно хранить и запрашивать много в чем.
Та имплементация, что мы предлагаем в KIP-1164, сделана на топиках самой Кафки. Можно будет масштабировать, увеличивая количество партиций в топике с метаданными и разнося их лидеров по разным узлам.
Не буду пока говорить, чтобы цифры из этих ранних прототипов не остались потом в истории Гугла на годы :)
Обработка Produce-запроса состоит из трех частей:
Буферизация на брокере.
Загрузка буфера в хранилище.
Коммит.
1 конфигурируется пользователем. По дефолту будет 250 мс. При большом входящем трафике должно заполняться быстрее (там еще лимит по размеру буфера, 8 МБ по дефолту).
2 зависит от хранилища. Обычный S3 даст миллисекунд 200-300. S3 Express One Zone должен быть быстрее (мы еще не замеряли).
3 -- самое интересное и зависит от имплементации batch coordinator'а. Там маленькие транзакции и будет реалистично побороться за low two digits.
Все, где допустимы producer-consumer latency в, скажем, 1 секунду (p99). Т.е. почти все, кроме риалтайм аналитики/рекомендаций/рекламы и того, где на Кафку завязан интерактив с пользователем.
Сложно сказать. У tiered storage заняло 5,5 лет от публикации KIP до general availability. Большая часть времени прошла в обсуждениях дизайна. Надеюсь, у diskless года 3 займет :)
Всем привет. Я один из разработчиков KIPа, готов ответить на вопросы.
Обязательно посмотрим, в чем проблема!
Привет
Я разработчик remote storage manager плагина https://github.com/Aiven-Open/tiered-storage-for-apache-kafka, использованного в статье. Как говорится, ask me anything
Возможно, режим тренировок не оптимальный, организм не успевает восстановиться и возникает перетренированность.
Это вариант, но не то, что я сделал: в этом случае все равно придется установить gdbserver в контейнере.
Я сделал с помощью namespaces. GDB запускается в PID namespace целевого процесса и в новом mount namespace, аналогично
nsenter -t <pid> -p unshare --mount-proc gdb ... -p 1
. Нужно еще добавить-iex set sysroot /proc/1/root -iex set auto-load safe-path /proc/1/root/<solib paths> -iex set solib-search-path /proc/1/root/<solib paths>
. Еще важно: если в mount namespace GDB есть такой же путь к бинарнику, как в mount namespace целевого процесса (условно/usr/bin/python3
), то его нужно скрыть (с помощью bind mount, например), чтобы GDB читал символы из/proc/1/exe
.Метод 6: использовать gVisor в качестве рантайма, чтобы существенно сократить возможность побега из контейнера через уязвимости ядра.
Кстати, я тут экспериментировал для одного своего проекта и мне удалось сделать так, что GDB подключается к процессу в контейнере, считывает символы там же, но при этом сам установлен и запущен на хосте. Т.е. технически в вашем случае можно было бы запускать один экземпляр демона.
Если интересно, могу рассказать подробнее.
Наполеоновские войны?
За рубежом ИТ -- это тоже денежно (сильно выше среднего по стране даже в социалистической Европе) и престижно.
А как "технологии" помогут решить эти проблемы?
В общем, других причин и не надо.
Go - основной язык в компании.
Citation needed. Я не знаю про .NET, но Java и JVM не "менее эффективны" (чтобы это не значило).
Люди программируют не только веб с микросервисами
Если так, то не позиционируйте себя как джуна. Делайте упор на свой общий инженерный опыт и т.п. Если человек умеет писать осмысленный код, покрывать его тестами, связно коммуницировать со всеми включенными в процесс, то это уже не джун.
Ну а еще есть просто работа на C++.