Sun Mar 29 2026
...
FastComments готов к космосу!
! Эта статья содержит технический жаргон
Что нового
Каждый point-of-presence FastComments теперь выполняет записи локально и асинхронно реплицирует их на все остальные узлы. Это обеспечит большую надежность по сравнению с предыдущей системой, а также ускорит инструменты модерации для пользователей в некоторых регионах, с некоторыми компромиссами.
"Готов к космосу" - это немного оптимистично, но идея заключается в том, что мы могли бы развернуть FastComments на различных планетах, и в конечном итоге система оказалась бы синхронизированной. Однако пользователям на Плутоне придется ждать около дня, чтобы изменения отразились на их странице предстоящих счетов, так как в данный момент только один мастер на регион может собирать информацию по биллингу.
Немного истории, почему изменение
Когда FastComments изначально запустился, у нас была очень типичная архитектура. У нас был прокси-слой, слой приложения, база данных, несколько реплик, а затем позже - реплики по регионам и облачным провайдерам для дополнительной избыточности.
В конечном итоге мы переместили реплики базы данных во все зоны, где находятся большинство наших пользователей, и также развернули приложение там, создав нашу собственную систему DNS и прокси (описанная в следующей статье) для маршрутизации запросов к ближайшему узлу приложения. Это делает чтение быстрым, но запись медленной, так как теперь вместо ожидания одного HTTP-обращения к бэкенду, вы ожидаете HTTP-обращения к близлежащему узлу, и этот узел может делать несколько записей в базу данных обратно к основному. Не хорошо!
Чтобы бороться с этим, мы реорганизовали многие области приложения, чтобы принимать readPreference в аргументах функций, чтобы вызывающие функции могли решать, насколько устаревшими они готовы принимать свои чтения, и сверху этого сделали больше записей (например, запись статистики модератора по действиям модератора) в режиме fire-and-forget. Не идеально, но это значительно ускорило процесс.
Одна из проблем, с которой мы столкнулись при работе с Mongo на глобальном уровне, это сетевые разрывы. Если достаточно узлов будут отключены, чтения остановятся, так как каждый узел становится неуверенным, принимается ли обслуживать чтения. Есть некоторые обходные пути, но крайние случаи оказываются запутанными. Это не теоретическая проблема - это происходило достаточно раз, приводя к страницам в 3 часа ночи, и нам это надоело, даже пытаясь настроить Mongo, чтобы он нормально работал с неопределенностью выборов репликационного набора до минуты. К сожалению, сети от Сан-Паулу до Фалькенштейна, например, просто не были очень хорошими в некоторых из наших хостинг-провайдеров. Настройка контроля перегрузки помогла, но не решила проблему.
Святой грааль решения, предполагая, что вас устраивают определенные компромиссы, это возможность принимать записи локально на этом узле (который имеет хорошее оборудование, RAID и т. д., что маловероятно, что он выйдет из строя) и сообщить пользователю, что его данные сохранены. Также в этом пункте присутствия вы все еще можете иметь второй узел в качестве горячей реплики для надежности.
Таким образом, мы пришли к тому, что имеем. Орегон, Вирджиния, Фалькенштейн, Сан-Паулу, Сингапур - это все свои собственные репликационные наборы и принимают записи. Развертывание в ЕС (хотя всего три PoP) имеет такое же поведение.
Как это работает
Некоторые из этих моментов были упомянуты в предыдущем разделе, но кратко: это CRDT-lite. Мы создали прокси (на Rust, потому что, конечно) который находится между приложением и Mongo и делает его мульти-мастером. Прокси осведомлен о других узлах, управляет контрольными точками, репликацией, мониторингом и первоначальной синхронизацией. Это мульти-мастерская замена системе репликации Mongo, включая некоторые команды DDL.
Разница с другими инструментами заключается в том, что это не отслеживает oplog. Отслеживание oplog или использование потоков изменений не сработает, потому что они показывают только конечное состояние объекта после записи, что делает невозможным обработку конфликтов. Вам нужно захватывать каждую операцию $set, $inc и реплицировать эту саму операцию.
Это специфическое для домена решение. Оно не сработает для всех продуктов. Можно сказать, что это проектирование, ориентированное на домен :). Это работает для нас, потому что с самого начала мы очень осторожно только $set изменяемые поля в документах - мы никогда не используем replaceOne Mongo, например. То же самое касается счетчиков. Вы никогда не делаете SET VOTES = 5. Вместо этого, вы записали бы INCREMENT VOTES BY 5, так как это позволяет достичь окончательной согласованности. Распределенные блокировки обрабатываются путем не делать. Только один узел на кластер имеет флаг, установленный для запуска крона. Хотя это может показаться ограниченным, мы можем купить серверы с терабайтами ОЗУ, поэтому мы можем пойти на этот компромисс, чтобы снизить риски и сложность.
Чтение ваших собственных записей
Для разработчиков, использующих API, вы должны иметь возможность читать свои собственные записи, как и раньше (сделать API-вызов для создания комментария, затем перечислить комментарии и увидеть новую запись в этом списке). Предупреждение заключается в том, что вы не можете делать это через регионы. Если ваш бэкенд работает только в одном регионе, как us-west, тогда вы должны быть в состоянии читать свои собственные записи, за исключением случая, если между вашей записью и вашим чтением, этот узел выходит из строя и ваш кэш DNS обновляется, чтобы указывать на следующий ближайший узел. При условии, что этого не происходит, чтение ваших собственных записей надежно.
Тестирование и миграция
Около половины кода в системе - это тестовый каркас, фреймворк и тесты. Тем не менее, релиз был немного неровным, заняв больше времени простоя (1 час для ЕС и 20 минут для us-global), чем ожидалось, но мы рады, что преодолели этот рубеж и благодарим вас за терпение!
В заключение и что это значит для вас
FastComments теперь должно быть быстрее и надежнее, чем когда-либо, и теперь мы можем вернуться к работе над функциями :)
Cheers!
