FastComments.com Blog

Sun Mar 29 2026
...

FastComments готовий до космосу!

! Ця стаття містить технічну термінологію

Що нового

Кожен point-of-presence FastComments тепер здійснює записи локально та асинхронно реплікує їх до всіх інших вузлів. Це забезпечить підвищену надійність у порівнянні з попередньою системою, а також зробить модераційні інструменти швидшими для користувачів у деяких регіонах, хоча з певними компромісами.

"Готовий до космосу" звучить трохи оптимістично, але ідея полягає в тому, що ми могли б розгорнути FastComments на різних планетах, і врешті-решт система синхронізувалася б. Однак користувачам на Плутоні доведеться чекати близько дня, щоб зміни відобразилися на їхній сторінці рахунків, оскільки наразі лише один мастер на регіон може агрегувати інформацію про білінг.

Трохи історії, чому зміна

Коли FastComments спочатку запустилось, у нас була дуже типова архітектура. У нас був проксі-шар, шар програми, база даних, кілька реплік, а пізніше репліки в регіонах і хмарних постачальників для додаткової резервованості.

Врешті-решт ми перемістили репліки бази даних в усі зони, де знаходиться більшість наших користувачів, і також розгорнули додаток там, створивши власну систему DNS і проксі (описану в наступному блозі), щоб маршрутизувати запити до найближчого вузла програми. Це робило читання швидким, але записи - повільнішими, оскільки тепер замість того, щоб чекати одного HTTP-раунду до бекенда, ви чекаєте на HTTP-раунд до найближчого вузла, і цей вузол може зробити кілька записів у базі даних, повертаючись до первинного. Не дуже добре!

Отже, для боротьби з цим ми перепроектували багато областей програми, щоб приймати readPreference в аргументах функції, щоб викликаючі функції могли вирішити, наскільки старими можуть бути їхні читання, а зверху цього зробили більше записів (наприклад, записуючи статистику модераторів про дії модераторів) fire-and-forget. Не ідеально, але це значно прискорило процеси.

Однією з проблем, з якою ми стикалися при глобальному запуску Mongo, є розділення мережі. Якщо достатня кількість вузлів відключиться, читання зупиняються, оскільки кожен вузол стає невпевненим, чи прийнятно виконувати читання. Є деякі способи обійти це, але крайні випадки стають заплутаними. Це не теоретична проблема - це сталося достатньо разів, викликавши сторінки о 3 годині ночі, і ми втомилися від цього, навіть намагалися налаштувати Mongo, щоб він нормально працював з невизначеністю вибору реплікасету до хвилини. На жаль, мережі між Сан-Паулу та Фалькенштайном, наприклад, не були дуже хорошими для деяких наших постачальників хостингу. Налаштування контролю заторів і тому подібне допомогло, але не вирішило проблему.

Святий грааль рішення, за умови, що ви згодні на певні компроміси, - це можливість приймати записи локально на цьому вузлі (який має хороше обладнання, RAID і т. д., що малоймовірно, що зламається) і повідомити користувача, що його дані збережені. Ви також можете мати на цьому point-of-presence ще один вузол як гарячу репліку для надійності.

Отже, з цього ми й прийшли. Орегон, Вірджинія, Фалькенштайн, Сан-Паулу, Сінгапур - всі це свої власні реплікасети та приймають записи. Європейське розгортання (хоча всього три PoP) має таку ж поведінку.

Як це працює

Частина цього вже покрита в попередньому розділі, але коротко: це CRDT-lite. Ми створили проксі (на Rust, тому що, звісно) яка перебуває між додатком і Mongo і робить його багатомаcтерним. Проксі знає про сусідів, управлінні контрольними точками, реплікації, моніторингу та первинній синхронізації. Це багатомаcтерна заміна системи реплікації Mongo, включаючи деякі DDL команди.

Різниця від інших інструментів полягає в тому, що це не стежить за oplog. Стеження за oplog або використання стрімів змін не працювало б, тому що вони показують лише фінальний стан об'єкта після запису, що робить неможливим обробку конфліктів. Вам потрібно захопити кожну операцію $set, $inc та реплікувати саму цю операцію.

Це специфічне для галузі рішення. Воно не буде працювати для всіх продуктів. Це можна назвати дизайном, орієнтованим на предмети :). Воно працює для нас, оскільки ми з самого початку дуже обережно тільки $set поля, які змінюємо в документах - ми ніколи не використовуємо replaceOne Mongo, наприклад. Так само з лічильниками. Ви ніколи не робите SET VOTES = 5. Замість цього ви б написали INCREMENT VOTES BY 5, оскільки це дозволяє досягти остаточної узгодженості. Розподілені блокування обробляються повною їх відсутністю. Лише один вузол на кластер має прапорець, встановлений для виконання crons. Хоча це може здаватися обмеженим, ми можемо купувати сервери з терабайтами оперативної пам'яті, тому можемо прийняти цей компроміс для зменшення ризику та складності.

Читання ваших власних записів

Для розробників, які використовують API, ви повинні мати можливість читати свої записи так само, як і раніше (зробити API-запит для створення коментаря, потім перелічити коментарі та побачити новий запис у цьому списку). Обмеження полягає в тому, що ви не можете робити це між регіонами. Якщо ваш бекенд працює лише в одному регіоні, як us-west, тоді ви повинні мати можливість читати свої записи, окрім випадків, коли між вашим записом і вашим читанням цей вузол виходить з ладу і ваш кеш DNS оновлюється, щоб вказати на наступний найближчий вузол. Якщо цього не станеться, читання ваших власних записів є надійним.

Тестування та міграція

Приблизно половина коду в системі - це тестове середовище, фреймворк та тести. Все ж, реліз був трохи нерівним, зайняв більше простою (1 година для ЄС і 20 хвилин для us-global), ніж бажалося, але ми раді, що пройшли цей етап і дякуємо за ваше терпіння!

На завершення та що це означає для вас

FastComments тепер має бути швидшим і надійнішим, ніж будь-коли, і тепер ми можемо повернутися до роботи над функціями :)

На здоров'я!