Политика работы с Redis‑кэшем
Документ описывает, как мы именуем, наполняем и очищаем Redis в проекте MARV. Соблюдайте единые правила — это избавляет от конфликтов и непредсказуемых очисток.
1. Пространства ключей
| Префикс | Назначение | Пример | Очищается при старте |
|---|---|---|---|
marv:cache:* | Волатильные данные, которые можно удалять | marv:cache:users:1:12345 | Да, полностью |
marv:persist:* | Очереди/служебные флаги, которые должны жить долго | marv:persist:updates:users:42 | Нет |
cachekeys.Normalizeпереписывает только устаревшиеcache:*ключи вmarv:cache:*; ключи с префиксомmarv:пропускаются как есть, любые другие строки остаются без изменений (функция не оборачивает произвольные строки в namespace).- Всё, что должно переживать рестарт, хранится под
marv:persist:*.
2. Сборка ключей (pkg/cachekeys)
Build(parts ...any)— собирает ключ вmarv:cache:<parts...>(разделитель:).Normalize(key string)— нормализует legacy-ключи (см. ниже).- Паттерны для SCAN/KEYS собираются тем же
Build, где последним сегментом идёт"*"(напримерBuild("messages", "unread", "*")). - Для персистентных структур есть отдельные хелперы:
UserUpdatesKey/UserUpdatesPattern,UserDataQuarantineKey/UserDataQuarantinePattern,UserDataQuarantineAlertKey,OnlineUsersKey,SessionsDayKey,LeaderElectionKey.
Правило: никаких ручных конкатенаций строк — только cachekeys.
3. Очистка при запуске
- Конфиг
app.cache_clear.enabled(по умолчанию true) включает/выключает очистку. application/container.Container.clearVolatileCaches:- Удаляет весь
marv:cache:*(черезcachekeys.Build("*")). - Обрабатывает
app.cache_clear.patterns. Строки безmarv:автоматически оборачиваются вcachekeys.Build(...).
- Удаляет весь
- Пространство
marv:persist:*не затрагивается.
Пример настройки:
yaml
app:
cache_clear:
enabled: true
patterns:
- "marv:cache:legacy_service:*"
- "tmp:*" # станет marv:cache:tmp:*4. Семейства кэшей
Пользователи
marv:cache:users:<api_type>:<api_uid>marv:cache:users:by_operator_email:<email>- Инвалидация: все операции
domain/services/users, плюс глобальная очистка.
Миры
marv:cache:worlds:<user_id>:<type>(<user_id>— внутренний числовой ID пользователя)- Очищается репозиторием миров и при рестарте.
Remote Configs
marv:cache:remote_configs:<api_type>(включаяglobal)- Инвалидация при create/update/delete и на старте.
Сообщения
marv:cache:messages:unread:<user_id>(<user_id>— внутренний числовой ID пользователя)marv:cache:messages:operator_stats— счётчики почты ACP по статусам и типам (полный проход по таблице). TTL 30 секунд; чистится при каждой записи сообщения из ACP. Игрок, открывший личное письмо, доходит до счётчика за TTL.- Чистятся при доставке сообщений и глобальной очисткой.
События / Event Results
marv:cache:events:active:<YYYYMMDD>marv:cache:events:lookback:<YYYYMMDD>marv:cache:events:starting:<YYYYMMDD>:<YYYYMMDD>(диапазон from:to)marv:cache:event_results:table:<event_id>:<group>:<group_num>:<with_data>- Инвалидация в сервисах + при рестарте.
Потоки и live inputs
marv:cache:streams:list,marv:cache:streams:id:<id>marv:cache:stream_inputs:<user_id>:<stream_id>(<user_id>— внутренний числовой ID пользователя)marv:cache:stream_inputs:list:<user_id>- Очищаются сервисами Streams/Inputs и при старте.
Traffic Flow
marv:cache:traffic_flows:*marv:cache:traffic_flow_entries:<user_id>(<user_id>— внутренний числовой ID пользователя)- Инвалидация при любых мутациях и при рестарте.
Rivals
marv:cache:rivals:<user_id>(<user_id>— внутренний числовой ID пользователя)- Чистится при справочных операциях и при запуске.
Transactions
marv:cache:transactions:<api_type>:<api_uid>- Удаляется после завершения транзакции и на старте.
Ads
marv:cache:ads:enabled- Инвалидация при любой операции над объявлениями + общий вайп.
Эксперименты (A/B)
marv:cache:experiments:list— полный список экспериментов.marv:cache:experiments:<id>— эксперимент по ID.- Инвалидация при create/update/delete/toggle, изменении назначений и на старте. Оба ключа известны по имени и удаляются напрямую, без SCAN: путь вступления в эксперимент проходит по ним на каждом назначении.
- Выборка включённых экспериментов, группы, счётчики и назначения не кэшируются: по ним под блокировкой строки принимается решение о вступлении, и устаревшая копия продолжала бы набирать в выключенный или отредактированный эксперимент.
Кланы
marv:cache:clan:id:<clan_id>— клан по ID.marv:cache:clan:member_by_user:<user_id>— членство игрока (<user_id>— внутренний числовой ID).marv:cache:clan:roster:<clan_id>— ростер клана.marv:cache:clan:req_count:<clan_id>— число заявок на вступление.marv:cache:clan:requests:<clan_id>:<status>— заявки клана по статусу.marv:cache:clan:count:<side>:<world>— число кланов для лидерборда:<side>— сторона мобильного разделения (all,mobile,other),<world>— мир кланов (all,normal,shadow).- TTL 30 секунд: эти чтения делят кэш с мутационными путями, поэтому кэш намеренно короткоживущий.
- Инвалидация — на каждой мутации ростера/заявок, плюс общая очистка на старте. Счётчик кланов не инвалидируется: создание, роспуск и смена мира клана доходят до него за TTL.
Клановый чат
marv:cache:clan_chat:cd:<user_id>— счётчик минимального интервала между сообщениями (TTL =domain.clan_chat.send_min_interval).marv:cache:clan_chat:burst:<user_id>:<минута unix-времени>— окно burst-лимита за минуту (TTL 70 секунд).- Оба ключа ведёт рейт-лимитер
ClanChatService; сами сообщения не кэшируются.
Клановые войны
marv:cache:war:active:<clan_id>— активная война клана.marv:cache:war:id:<war_id>— война по ID.marv:cache:war:member:<war_id>:<api_type>:<api_uid>— участник войны.marv:cache:war:roster:<war_id>:<clan_id>— строки участников войны стороны (clan_war_members; строка появляется с первой атакой игрока).marv:cache:war:phases:<war_id>,marv:cache:war:phase:<war_id>:<phase_index>— фазы войны.marv:cache:war:beaten:<war_id>:<phase_index>— побеждённые цели в фазе.marv:cache:war:rewards:<account_id>— награды игрока.marv:cache:war:final:<war_id>— финал войны.- TTL 15 секунд. Статус боя (attack reserve/resolve) не кэшируется вообще — он меняется на каждой атаке.
- Инвалидация — на продвижении фаз, атаках, начислении наград и роспуске клана. Уход игрока из клана сбрасывает только записи его удалённых строк участника (
war:memberиwar:rosterэтих войн): вклады не кэшируются, тали фаз не меняются.
Ограничения аккаунта
marv:cache:restrictions:active:<user_id>— действующие ограничения аккаунта (кэшируется и пустой набор: глобальный бан проверяется на каждом запросе).- TTL 5 минут, но не дальше ближайшего
untilв наборе — истёкшее ограничение не переживает свой срок в кэше. - Инвалидация — на выдаче и снятии ограничения и при слиянии аккаунтов. Запись при этом ставит метку
marv:cache:restrictions:active:<user_id>:chgна 10 секунд: чтение, начатое до записи, не кладёт в кэш набор, прочитанный до неё.
Планировщик
marv:cache:scheduler:manual_run:<tag>— очередь ручных запусков leader-only задач (нелидер кладёт тег, лидер вычитывает и выполняет). TTL 10 минут.
5. Персистентные ключи (не очищаются)
marv:persist:updates:users:<user_id>— очередь отложенных обновлений (читается задачейuser_data_batch_update).marv:persist:quarantine:user_data:<user_id>— сейв, который БД отказалась принять (невалидный UTF-8, NUL-байт, либо отбитая построчная запись). Хранит сам payload и диагностику: смещение и значение битого байта, окно вокруг него, текст ошибки БД. TTL —domain.user_updates.quarantine_ttl(по умолчанию 30 дней). КладутUserService(на приёме) и задачаuser_data_batch_update; ключ из очереди при этом не удаляется.marv:persist:quarantine:alerted:<user_id>— маркер дедупликации алертов по карантину, TTLdomain.user_updates.quarantine_alert_window(по умолчанию 24 часа). Нужен, чтобы навсегда сломанный сейв не поднимал алерт на каждом прогоне крона.marv:persist:leader[:suffix]— ключи выборов лидера (реализация — общий модульshared/leader, ключ строитLeaderElectionKeyизpkg/cachekeys; значение =app.server.id).marv:persist:online_users— sorted set присутствия: member — id пользователя, score — время его последнего аутентифицированного запроса (V1 и V2) или логина в unix ms. Пользователь онлайн, пока это время моложеapp.online_ttl(по умолчанию 5 мин).OnlineTrackerсчитает онлайн однимZCOUNTпо score, без обхода ключей. Ушедших из набора убираетZREMRANGEBYSCORE— каждый процесс не чаще раза в минуту, на очередной отметке; на счёт это не влияет, только на размер набора. Каждая отметка продлевает ключуPEXPIREна TTL, так что у игры без запросов набор исчезает сам. Команд sorted set вCacheStoreнет, поэтому трекер ходит черезEvalоднострочными Lua-скриптами.marv:persist:sessions:<YYYY-MM-DD>— счётчик сессий за день. Инкрементируется (INCR) при каждом успешном/v2/login. TTL устанавливается до конца следующего дня при первом инкременте.- Любые другие долгоживущие структуры размещайте здесь, если их нельзя терять на рестарте.
