Sun Mar 29 2026
...
FastComments готов к космосу!
! Эта статья содержит технический жаргон
Что нового
Каждый point-of-presence FastComments теперь выполняет записи локально и асинхронно реплицирует их на все остальные узлы. Это обеспечит повышенную стойкость по сравнению с предыдущей системой, а также ускорит инструменты модерации для пользователей в некоторых регионах, с некоторыми компромиссами.
«Готовы к космосу» - это несколько оптимистично, но идея заключается в том, что мы могли бы развернуть FastComments на разных планетах, и в конечном итоге система синхронизировалась бы. Однако пользователям на Плутоне придется ждать около дня, чтобы изменения отразились на их предстоящей странице счета, так как в настоящее время только один мастер на регион может агрегировать информацию о биллинге.
Немного истории, почему изменение
Когда FastComments изначально был запущен, у нас была очень типичная архитектура. У нас был прокси-уровень, уровень приложений, база данных, некоторые реплики, а затем позже реплики по регионам и облачным провайдерам для дополнительной избыточности.
В конечном итоге мы переместили реплики БД во все зоны, где находятся большинство наших пользователей, и также развернули приложение там, создав нашу собственную систему DNS и прокси (описанную в последующих постах) для маршрутизации запросов к ближайшему узлу приложения. Это делает чтения быстрыми, но записи медленными, так как теперь вместо ожидания одной HTTP-окончательной поездки к бэкенду, вы ждете поездку HTTP к близкому узлу, и этот узел может выполнять несколько записей в базу данных обратно к первичному. Неудобно!
Чтобы справиться с этим, мы реорганизовали многие области приложения, чтобы принимать readPreference в аргументах функции, чтобы вызывающие функции могли решать, насколько устаревшими они готовы быть в своих чтениях, и помимо этого сделали больше записей (например, записи статистики модераторов о действиях модераторов) с режимом fire-and-forget. Не идеально, но это значительно ускорило процессы.
Одной из проблем, с которыми мы столкнулись, работая с Mongo по всему миру, являются сетевые разделения. Если достаточно узлов отсекаются, чтения останавливаются, так как каждое узел становится неуверенным, допустимо ли обслуживать чтения. Существуют некоторые способы обхода этого, но крайние случаи становятся неаккуратными. Это не теоретическая проблема - это произошло достаточно раз, вызывая аварийные страницы в 3 часа утра, и мы устали от этого, даже пытаясь настроить Mongo так, чтобы он был готов к неопределенности выборов репликации вплоть до минуты. К сожалению, сети от Сан-Паулу до Фалькенштайна, например, были просто не очень хорошими у некоторых наших хостинг-провайдеров. Настройка контроля перегрузки и подобные вещи помогали, но не решали проблему.
Святой Грааль решения, при условии, что вы готовы к определенным компромиссам, - это возможность принимать записи локально на этом узле (у которого хорошее оборудование, RAID и т. д., что маловероятно выйдет из строя) и сообщить пользователю, что его данные сохранены. Также вы можете в этой точке присутствия иметь второй узел в качестве горячей реплики для надежности.
Вот к чему мы пришли. Орегон, Вирджиния, Фалькенштейн, Сан-Паулу, Сингапур - все они являются собственными наборами реплик и принимают записи. Развертывание в ЕС (хотя только три PoP) ведет себя так же.
Как это работает
Некоторые из этих аспектов уже были охвачены в предыдущем разделе, но TL;DR - это 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 теперь должен работать быстрее и быть более надежным, чем когда-либо, а теперь мы можем вернуться к работе над функциями :)
Ура!
