Кейс: лидерборд турнира отдавал разные топ-100 разным пользователям одновременно

Во время турнира с высокой активностью топ-100 игроков в разных запросах отличался — один пользователь видел себя на 45 месте, обновлял страницу через секунду и видел себя на 52-м, хотя счёт не менялся.

Лидерборд хранился в Redis Sorted Set на одном узле, но за узлом стояла реплика на чтение, и часть запросов шла через read-реплику Redis, часть — на мастер. Между мастером и репликой Redis есть асинхронная репликация, и под нагрузкой лаг реплики достигал секунд.

Проблема не в самом Sorted Set — структура данных отдаёт консистентный снепшот в момент чтения. Проблема в том, что два клиента читали два разных снепшота: один свежий с мастера, другой отстающий с реплики.

Что спросили следом на разборе: почему нельзя просто писать на мастер и всегда читать с мастера. Ответ — тогда весь трафик чтения топа идёт через один узел, а лидерборд читают в разы чаще, чем обновляют счёт. Решение, которое применили: топ-100 (самый горячий диапазон) кэшировали отдельно с коротким TTL и обновляли пушем при каждом изменении верхних позиций, а позицию конкретного игрока за пределами топа читали с реплики и явно помечали в ответе как «может отставать на несколько секунд».

Реплика для чтения снимает нагрузку с мастера, но переносит проблему консистентности на уровень API. Если это не проговорено явно в контракте, пользователь воспринимает лаг репликации как баг, а не как компромисс архитектуры.

Тренажёр: 600 вопросов, мок с таймером, план повторов

senior·base — что спрашивают на самом деле


В этом посте были ссылки, но мы их удалили по правилам Сетки