01
READY как граница состояния.
Новый READY ждёт завершения прежних задач до обновления состояния серверов. Следующие события запускаются позже, поэтому старая работа не перезапишет новое состояние.
За кулисами Fjord
Бот на Rust. Собственный графический движок. А также фреймворки, библиотеки и базы данных, на которых построен Fjord.
Здесь мы рассказываем о наших разработках и технологиях, из которых состоит Fjord.
Fjord
Fjord обрабатывает события Discord по порядку и выполняет независимые задачи параллельно. Twilight связывает бота с Discord и хранит данные серверов локально. Dragonfly хранит ожидающие события и помогает координировать соединения со шлюзом.
01
Новый READY ждёт завершения прежних задач до обновления состояния серверов. Следующие события запускаются позже, поэтому старая работа не перезапишет новое состояние.
02
События одного сервера запускаются в порядке поступления. Каждое ждёт завершения предыдущего, чтобы изменения этого сервера не обгоняли друг друга.
03
Диспетчер различает задачи по ключам. Разные серверы могут работать одновременно; ждать друг друга должны только задачи с одним ключом.
04
Одно и то же сообщение одновременно поступает в AutoMod и журнал активности. Каждый обрабатывает его отдельно для своей задачи.
05
Ожидающие события Discord и их данные хранятся в Dragonfly. AutoMod и журналы активности также используют это общее хранилище для временного состояния. Выполнением задач по-прежнему управляет бот.
06
Когда срабатывает активный фильтр AutoMod, проверка этого сообщения прекращается. Остальные фильтры не вызывают повторную меру для того же события.
07
Twilight поддерживает в памяти актуальные данные серверов, участников, ролей, каналов, эмодзи, присутствия и голосовых состояний. Сообщения в этот кеш не входят.
08
Взаимодействия планируются независимо от событий серверов. READY обозначает границу состояния внутри каждого соединения со шлюзом.
09
Dragonfly координирует владение отдельными шардами — соединениями со шлюзом. После передачи прежний владелец не может брать в работу или завершать дальнейшие события.
01 / Собственная разработка
Fjell — наш собственный графический движок на Rust. Он объединяет фоны, аватары и тексты в персональные приветственные карточки.
Мы хотели контролировать создание этих изображений. Поэтому разработали решение под задачи Fjord, которое даёт нам творческую свободу.
Не менее важна экономическая эффективность. Fjell обеспечивает максимальную производительность без потери качества и максимально эффективно использует процессор и оперативную память при генерации. Это значительно снижает наши расходы на серверы.
Наша реализация охватывает даже обработку пикселей. Собственные циклы SIMD используют AVX2, FMA и BMI1/BMI2 для параллельного вычисления нескольких пикселей.
Измерение производительности
Четыре уровня нагрузки, контролируемые варианты карточек и единая методика тестирования.
| Параллельно | Карточек/с | Карточек/мин | Рендер ср. | Рендер P50 | Рендер P95 | Рендер P99 | Всего ср. | Всего P50 | Всего P95 | Всего P99 | CPU ср. | RAM ср. |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 797,48 | 47.849 | 1,062 ms | 1,061 ms | 1,226 ms | 1,242 ms | 1,253 ms | 1,251 ms | 1,428 ms | 1,473 ms | 88,3 % | 381,2 MiB |
| 2 | 1.577,95 | 94.677 | 1,038 ms | 1,004 ms | 1,225 ms | 1,421 ms | 1,266 ms | 1,236 ms | 1,470 ms | 1,715 ms | 173,5 % | 360,1 MiB |
| 4 | 3.173,00 | 190.380 | 1,023 ms | 1,011 ms | 1,188 ms | 1,230 ms | 1,259 ms | 1,243 ms | 1,448 ms | 1,557 ms | 344,9 % | 375,3 MiB |
| 8 | 5.850,25 | 351.015 | 1,050 ms | 1,026 ms | 1,194 ms | 1,290 ms | 1,366 ms | 1,349 ms | 1,578 ms | 1,803 ms | 647,0 % | 385,8 MiB |
Измеренная пропускная способность в сравнении с идеальным линейным масштабированием.
| Воркеры | Измерено | Идеальная линейная | Масштабирование | Эффективность |
|---|---|---|---|---|
| 1 | 797,48 | 797,48 | 1,00× | 100,0 % |
| 2 | 1.577,95 | 1.594,96 | 1,98× | 98,9 % |
| 4 | 3.173,00 | 3.189,92 | 3,98× | 99,5 % |
| 8 | 5.850,25 | 6.379,84 | 7,34× | 91,7 % |
Все четыре локальных теста длились по 60 секунд на AMD Ryzen 9 9950X. Использовались 100 лёгких фонов с обычными цветами или иллюстрациями размером примерно от 100 до 300 КБ, обычно около 200 КБ. Жидкий слой был отключён, а шрифт оставался неизменным; между карточками менялись цвета и положения шейдеров текста и круга.
Время рендеринга включает создание изображения, кодирование и сжатие PNG до получения готового PNG. Передача по сети и время загрузки не включены.
Генератор нагрузки использовал 1, 2, 4 или 8 параллельных запросов. Новый запрос начинался только после полного получения и проверки предыдущего PNG. Указанная загрузка CPU возникла именно при такой нагрузке. Графический движок мог распределять работу между несколькими ядрами.
* Внутренние, независимо не проверенные измерения в описанных условиях. Они не являются гарантированной характеристикой, гарантией или обязательством по производительности. Фактическая производительность зависит от системы, нагрузки и среды.
Агрессивный случайный тест
Значительно более вариативная схема с 1 000 фонов, 237 шрифтами и случайными эффектами в каждом запросе.
| Параллельно | Карточек/с | Карточек/мин | Рендер ср. | Рендер P50 | Рендер P95 | Рендер P99 | Всего ср. | Всего P50 | Всего P95 | Всего P99 | CPU ср. | RAM ср. |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 92,65 | 5.559 | 9,775 ms | 10,478 ms | 12,781 ms | 13,401 ms | 10,788 ms | 11,553 ms | 14,006 ms | 14,784 ms | 93,6 % | 1.205,6 MiB |
| 2 | 184,53 | 11.072 | 9,944 ms | 10,621 ms | 12,858 ms | 13,550 ms | 10,832 ms | 11,576 ms | 13,938 ms | 14,698 ms | 189,1 % | 1.278,8 MiB |
| 4 | 358,82 | 21.529 | 10,209 ms | 10,855 ms | 13,240 ms | 13,997 ms | 11,138 ms | 11,857 ms | 14,363 ms | 15,212 ms | 377,7 % | 1.300,9 MiB |
| 8 | 660,52 | 39.631 | 10,897 ms | 11,610 ms | 14,343 ms | 15,336 ms | 12,101 ms | 12,889 ms | 15,838 ms | 16,880 ms | 744,8 % | 1.308,6 MiB |
Измеренная пропускная способность в сравнении с идеальным линейным масштабированием.
| Воркеры | Измерено | Идеальная линейная | Масштабирование | Эффективность |
|---|---|---|---|---|
| 1 | 92,65 | 92,65 | 1,00× | 100,0 % |
| 2 | 184,53 | 185,30 | 1,99× | 99,6 % |
| 4 | 358,82 | 370,60 | 3,87× | 96,8 % |
| 8 | 660,52 | 741,20 | 7,13× | 89,1 % |
Эти четыре локальных теста длились по 60 секунд на том же AMD Ryzen 9 9950X. Каждый запрос выбирал один из 1 000 более сложных фонов размером от 800 КБ до 2,4 МБ и один из 237 шрифтов. Изменялись тексты, прозрачность, размеры шрифта и аватара, обводка, цвета текста и круга, а также угол, сила и интенсивность шейдеров. Примерно на половине карточек дополнительно включался жидкий слой со случайными цветом, непрозрачностью и размытием.
Генератор нагрузки использовал 1, 2, 4 или 8 параллельных воркеров в замкнутом цикле. Каждый воркер начинал следующий запрос только после полного получения и проверки предыдущего PNG. Все 60-секундные тесты завершились без ошибок, тайм-аутов и повреждённых PNG.
Время рендеринга включает создание изображения, кодирование и сжатие PNG до получения готового PNG. Передача по сети и время загрузки не включены.
* Внутренние, независимо не проверенные измерения в описанных условиях. Они не являются гарантированной характеристикой, гарантией или обязательством по производительности. Фактическая производительность зависит от системы, нагрузки и среды.
02 / Наша техническая основа
Помимо собственного движка мы используем Rust, Leptos, TypeScript, Vue, Twilight, Dragonfly, PostgreSQL, ScyllaDB и RustFS. У каждой технологии своя понятная роль в системе.
Язык программирования
На Rust работают наш бот для Discord, серверная часть, редактор карточек и графический движок Fjell.
Логотип © Rust Foundation. CC BY 4.0 · без изменений
Веб-фреймворк
Leptos используется в отдельных частях нашего веб-интерфейса. Этот фреймворк позволяет использовать Rust в браузере и хорошо вписывается в остальной стек.
Логотип от сопровождающих Leptos. MIT · без изменений
Язык программирования
Интерактивные публичные страницы мы пишем на TypeScript. Проверка типов помогает сохранять согласованность компонентов и состояния.
Официальный логотип TypeScript. Используется согласно правилам бренда TypeScript.
Веб-фреймворк
Vue объединяет компоненты публичного сайта. К ним относятся навигация, документация и другие интерактивные области. Так интерфейс проще поддерживать и развивать.
Логотип © Evan You. CC BY-NC-SA 4.0 · без изменений
Библиотека для Discord
Для подключения к Discord мы используем Twilight. Эта библиотека связывает нашего бота на Rust с Discord.
Логотип от Fezhoof. CC BY-SA 4.0 · без изменений
База данных
Dragonfly — наше общее временное хранилище для событий Discord, координации шардов и нескольких соединений со шлюзом, AutoMod и журналов активности.
Программное обеспечение под BSL 1.1 с дополнительным правом использования.
Реляционная база данных
PostgreSQL дополняет наш слой данных там, где нужны реляционные данные и структуры. Он работает рядом со ScyllaDB как отдельная часть инфраструктуры.
База данных
ScyllaDB входит в наш слой данных вместе с PostgreSQL. Эти две системы используются для разных задач внутри Fjord.
Объектное хранилище
RustFS используется у нас как S3-совместимое объектное хранилище. Оно добавляет отдельный слой хранения рядом с PostgreSQL и ScyllaDB.
ПО по лицензии Apache 2.0. RustFS является товарным знаком RustFS, Inc.