Кейс: две реплики отдавали разные ответы на один и тот же GET-запрос
Сервис читал профиль пользователя через балансировщик, который раскидывал запросы round robin между тремя read-репликами Postgres. После обновления профиля пользователь иногда видел старые данные, а через секунду — новые, при том что запросы шли подряд с одного клиента.
Причина была не в коде приложения. Репликация в Postgres асинхронная: мастер подтверждает commit, не дожидаясь применения WAL на репликах. Каждая реплика догоняет мастер с собственной задержкой, и эта задержка не одинаковая — зависит от нагрузки на конкретную реплику в конкретный момент.
Балансировщик не знал про lag репликации и отправлял запросы туда, где было свободнее, а не туда, где данные свежее. Пользователь читал с реплики А, которая уже применила обновление, потом со следующим запросом попадал на реплику Б с задержкой в пару секунд.
Решили через read-your-writes: после write-запроса клиент на время сессии читает с мастера или с реплики, у которой lag меньше порога. Это не устраняет асинхронность репликации — это просто не даёт пользователю увидеть собственный откат назад во времени.
Дальше спросят: а что если у вас read-реплики в другом регионе с задержкой в десятки миллисекунд по сети сверху репликационного лага — как считать SLA на staleness и стоит ли давать клиенту выбор между «быстро, но может быть старое» и «дольше, но гарантированно свежее».
Асинхронная репликация — это не баг конфигурации, это плата за то, что мастер не ждёт реплики перед ответом клиенту.
Тренажёр: 600 вопросов, мок с таймером, план повторов
senior·base — что спрашивают на самом деле
В этом посте были ссылки, но мы их удалили по правилам Сетки