Кеширование транзакций и игровых результатов: подходы и риски
1) Зачем кешировать и где это действительно нужно
Кеш — инструмент снижения латентности и нагрузки на ядро. В iGaming это критично для:- Чтения балансов и статусов транзакций (частые GET-запросы);
- Истории игр/спинов и агрегатов (верхушки лидерборда, последние N результатов);
- Метаданных игр/провайдеров, лимитов ставок, статических справочников;
- Фидов коэффициентов и «быстрых» справок для UX (баннеры, промо-статусы).
Но кеш никогда не является источником истины для денег и исходов. Истина — ledger/кошелёк и подтверждённые результаты от провайдера.
2) Красная линия: что кешировать нельзя
Запись денег: списание/зачисление баланса (операции записи) — только через БД/ledger с транзакциями и идемпотентностью.
Решения по ставке/выигрышу до подтверждения провайдера.
KYC/AML и комплаенс-флаги, влияющие на выплаты.
Секреты/токены (кеш в памяти процесса допустим, но не общий кэш).
3) Базовые паттерны кеширования
Cache-aside (lazy): приложение сначала ищет в кеше, при промахе — читает из БД и кладёт в кеш (`get → miss → load → set`). Универсальный и безопасный для чтения.
Write-through: запись в БД проходит через кеш; обеспечивает актуальность ключа, но повышает латентность записи.
Write-behind (write-back): запись сначала в кеш, потом асинхронно в БД. Запрещён для денег/результатов — риск потери при падении.
Read-through: кеш сам знает, как достать из БД (прокси-кеш, например, Redis with modules/sidecar). Хорош для метаданных.
Рекомендация: cache-aside для чтений, write-through только там, где безопасно, write-behind — никогда для денежных/игровых истин.
4) Консистентность и идемпотентность
Источник истины: ledger (append-only), операции с `operation_id` и идемпотентной обработкой.
Баланс: читаем из кеша, но любое расхождение подтверждаем из базы перед критичными действиями (депозит/вывод/крупная ставка).
Инвалидация: при успешной записи в БД → del/expire соответствующих ключей баланса/статуса.
Дедупликация: outbox/inbox + idempotency keys для вебхуков/платежей; кеш не участвует в дедупе, он только ускоряет чтение.
5) TTL, инвалидация и «право на устаревание»
Short-TTL для баланса: 1–5 секунд (или soft-TTL с background refresh).
Статусы транзакций: короткий TTL (5–30 с) с активной инвалидацией по событиям (`deposit_completed`, `settled`).
История игр: TTL 1–10 минут, инвалидация по событию `new_round`.
Метаданные/справочники: TTL 10–60 минут, warm-up при деплое.
Event-driven инвалидация: шина событий (Kafka/PubSub) публикует `wallet_updated`, `bet_settled`, `bonus_changed` → подписчики удаляют/обновляют ключи.
6) Антишторм-паттерны (шторм промахов и догон)
Request coalescing: один поток «ведёт» запрос к базе, остальные ждут (mutex per key).
Stale-while-revalidate: выдаём «слегка устаревшее», параллельно обновляем в фоне.
Jitter для TTL: рандомизируйте TTL (±20%), чтобы ключи не истекали одновременно.
Бэк-офф на промахах: при постоянных промахах/ошибках — временный negative-cache (см. ниже).
7) Negative-caching и серый кардинал ошибок
Для «не найдено» (например, ещё нет статуса транзакции) — краткий negative TTL 1–3 с.
Не кешируйте ошибки БД/провайдера дольше нескольких секунд — иначе закрепите аварию.
Введите canary-ключи для наблюдаемости: рост доли negative-хитов — повод для алерта.
8) Структура ключей и сегментация
Именование: `wallet:{userId}`, `txn:{txnId}:status`, `game:{provider}:{tableId}:last_results`, `leaderboard:{tournamentId}:top100`.
Сегменты/неймспейсы по env/region/brand: `prod:eu:wallet:{userId}` — исключите пересечения и кросс-регионный мусор.
Ограничивайте кардинальность — особенно для лидербордов и истории.
9) Кеш на edge, в кластере и в памяти
Edge-кеш (CDN/WAF): только для неперсональных данных (игровые метаданные, публичные лидерборды, медиа). Параметры запросов — whitelist; защита от cache-busting.
Redis/Memcached (кластер): основа для персональных чтений; включайте AOF/RDB-снапшоты, реплику и квоты.
In-process кеш: микросекундный доступ для горячих справочников; требуются механизмы инвалидации (broadcast, version key).
10) Денежные кейсы: безопасные ускорения
Баланс игрока
Чтение: cache-aside с TTL 1–5 с.
Запись: транзакция в БД → del кеша баланса; при критичном действии (вывод/крупная ставка) — «recheck from DB».
Антигонка: optimistic locking версии баланса.
Статус платежа
Сценарий: пользователь жмёт «обновить статус».
Решение: cache-aside + negative TTL на «pending»/«unknown» 2–5 с; обновление по вебхуку PSP → инвалидация.
Бонусы/вэйджер
Агрегаты (прогресс в %): кешируем 10–30 с; инвалидация по событию `bet_placed/settled`.
11) Игровые кейсы: скоростной фронт без искажений истины
История спинов/ставок
Последние N событий: кеш-список с ограничением (например, 100), TTL 1–10 мин, пополнение по событию `round_finished`.
Нельзя показывать «выигрыш», пока нет подтверждения от провайдера → промежуточный статус «pending».
Лайв-игры (WebSocket)
Кратковременный кеш последних сообщений/состояния стола на 1–3 с для быстро подключившихся клиентов.
Стейт-ключи сегментируйте по `tableId/market`.
Лидерборды
Precompute + кеш на 10–60 с; для массовых апдейтов — батчевые обновления и частичная инвалидация «окон».
12) Риски и как их закрыть
Двойное списание / фантомные выигрыши: только чтение из кеша; все списания/зачисления — через БД и идемпотентность.
Старые данные → спор с игроком: короткие TTL, «строгая реальность» перед выплатой, прозрачные статусы («ожидает подтверждения»).
Сплит-брейн кеш-кластера: кворум/сентинел, timeouts, отказ от write-behind.
Cache stampede на горячих ключах: coalescing, jitter, stale-while-revalidate.
Кэш-инжекция/poisoning: строгие ключи, сигнатуры/подпись для кэшируемых API-ответов, канареечные проверки.
Приватность/PII: шифрование канала (mTLS), запрет кеша на edge для персональных данных, короткие TTL, очистка при логауте.
13) Наблюдаемость кеша
Метрики на каждый слой:- Hit/Miss ratio по категориям ключей; redis_ops/sec, latency p95/p99, evictions, memory_usage.
- Канареечные ключи: `cache_health:{segment}` — проверка доли negative-кеша и времени обновления.
- Логи: промахи «пачками», частые `del` по одному сегменту = признак «шумного» сервиса.
- Трейсы: спаны «cache get/set/del» с ключевыми тэгами (без PII).
14) Мини-архитектура (референс)
1. Приложение (API/WS) → Redis-кластер (TLS, auth).
2. Источник истины: Wallet DB (ledger), Game results store.
3. Шина событий: `wallet_updated`, `bet_settled`, `promo_changed`.
4. Инвалидатор: подписчик на события → `del`/`set` горячих ключей.
5. Edge-кеш: только публичные ресурсы/лидерборды.
6. Наблюдаемость: дашборды кеша, алерты по stampede, отрицательным хитам.
15) Политики TTL (примерная матрица)
16) Примерный псевдокод (безопасное чтение баланса)
python def get_balance(user_id):
key = f"wallet:{user_id}"
bal = cache.get(key)
if bal is not None:
return bal промах: берём из БД и кладём с коротким TTL + jitter bal = db.get_wallet_balance(user_id)
cache.set(key, bal, ttl=randint(1,5))
return bal
def apply_transaction(op_id, user_id, delta):
атомарная запись в БД с идемпотентностью if db.exists_op(op_id):
return db.get_result(op_id)
res = db.apply_ledger(op_id, user_id, delta) # транзакция cache.delete(f"wallet:{user_id}")      # инвалидация return res17) Чек-лист продакшен-готовности
- Ясное разграничение: истина в БД, кеш — только для чтений.
- Паттерны: cache-aside для чтений; write-behind запрещён.
- Событийная инвалидация: `wallet_updated`, `bet_settled`, `promo_changed`.
- Короткие TTL + jitter; negative-cache ≤ 3 с.
- Антишторм: coalescing, stale-while-revalidate.
- Сегментация ключей по env/region/brand; лимит кардинальности.
- Наблюдаемость: hit/miss, evictions, p95, alерты на stampede/negative-spikes.
- Edge-кеш только для публичных данных; персональные — только в Redis/TLS.
- Runbook: что делать при рассинхроне (forced refresh, временное отключение кеша сегмента).
- Регулярные тесты: нагрузка на горячие ключи, учения stampede.
Резюме
Кеш в iGaming — это ускоритель чтения, а не «вторая база данных для денег». Храните истину в ledger, обеспечьте идемпотентность и событийную инвалидацию, держите короткие TTL и антишторм-механику, разделяйте edge-кеш и персональные данные, следите за метриками кеша. Так вы получите быстрое UX без «иллюзий выигрыша», двойных списаний и регуляторных проблем.
