Получить доступ

Пользователь сохранил изменения, обновил страницу — а изменений нет. Что происходит и как это исправить?

MiddleРепликация
Ещё спрашивают так:
  • Что такое отставание реплики?
  • Как обеспечить read-your-writes при чтении с реплик?
  • Что такое монотонное чтение?
Коротко

Запись ушла в лидера, а чтение — на асинхронную реплику, которая ещё не догнала. Это нормальное поведение асинхронной репликации; приложение должно давать нужные гарантии само.

  • Read-your-writes: свои данные после записи читать с лидера (или с реплики, догнавшей нужную позицию)
  • Монотонное чтение: привязать пользователя к одной реплике, чтобы не «откатываться назад» при обновлении
  • Причинная согласованность: ответ не должен появиться раньше вопроса

А ещё Мониторить отставание и выводить из балансировки сильно отставшие реплики

Так подсказку видит пользователь sotto на живом собеседовании — за пару секунд, пока вы думаете вслух.

Подробно

При асинхронной репликации лидер подтверждает запись, не дожидаясь реплик. Обычно реплики отстают на миллисекунды, но под нагрузкой, при долгих транзакциях, тяжёлых миграциях или сетевых проблемах — на секунды и минуты. Тогда возможны аномалии:

  • не вижу своё изменение — сохранил профиль, а при перезагрузке страницы прочитал старую версию с отставшей реплики;
  • откат во времени — первый запрос попал на свежую реплику, второй — на отставшую: комментарий появился и исчез;
  • нарушение причинности — вижу ответ на вопрос, которого ещё нет (они на разных репликах или шардах).

Решения для «вижу своё» (read-your-writes):

  • читать с лидера данные, которые пользователь мог менять (свой профиль, свои заказы), а чужие — с реплик;
  • читать с лидера в течение короткого времени после записи пользователя (метка «писал в последние N секунд» в сессии или cookie);
  • запоминать позицию журнала (LSN в PostgreSQL, GTID в MySQL) после записи и читать только с реплики, которая её применила, — иначе с лидера. Так делают ProxySQL, драйверы с causal consistency в MongoDB;
  • синхронная репликация для критичных данных — дороже по задержке записи.

Монотонное чтение — закрепить пользователя за одной репликой (хеш user_id), чтобы последовательные чтения не шли назад во времени.

Причинная согласованность — писать связанные данные в один шард или передавать «версию» прочитанного, чтобы зависимые данные читались не старше неё.

В интерфейсе иногда достаточно оптимистичного обновления: показать сохранённое сразу из ответа на запись, не перечитывая.

Эксплуатация:

  • метрика отставания каждой реплики (pg_stat_replication, replay_lag; Seconds_Behind_Master) и алерт;
  • исключать из балансировки реплики с отставанием выше порога;
  • не гонять на репликах для пользовательских запросов тяжёлую аналитику — она тормозит применение журнала (или конфликтует с ним).

Пример

def get_profile(user_id, session):
    # недавно писал — читаем с лидера, иначе с реплики
    if session.get("wrote_at", 0) > time.time() - 5:
        return primary.fetch_profile(user_id)
    return replica_for(user_id).fetch_profile(user_id)    # одна реплика на пользователя — монотонно

Что хотят услышать

  • Причина: асинхронная реплика отстаёт.
  • Названия гарантий: read-your-writes, монотонное чтение, причинность.
  • Конкретные приёмы: чтение с лидера после записи, позиция журнала, привязка к реплике.
  • Мониторинг отставания и вывод отставших реплик.

Частые ошибки

  • Лечить включением синхронной репликации для всего.
  • Не различать чтение своих и чужих данных.

Что спросят дальше

Связанные вопросы

Закрытая бета

Подсказки на собеседовании — в реальном времени

sotto слышит вопрос интервьюера и показывает короткий ответ и план, как в блоке «Коротко». Оставьте контакт — пришлём доступ и промокод.

  • Промокод на 2 часа — сверх пробных 30 минут
  • Программа для Windows — скачать сразу, войти — через Яндекс ID или VK ID
  • Прямая связь с разработчиком: что улучшить — сделаем