Caching Transactions and Gaming Outcomes: Approaches and Risks
1) Why cache and where you really need it
Cache is a tool for reducing latency and load on the core. In iGaming, this is critical for:- Reading balance sheets and transaction statuses (frequent GET requests);
- History of games/spins and aggregates (tops of the leaderboard, last N results);
- Metadata of games/providers, betting limits, static directories;
- Feeds of coefficients and "quick" references for UX (banners, promotional statuses).
But cache is never the source of truth for money and outcomes. Truth - ledger/wallet and confirmed results from the provider.
2) Red line: that you can't cache
Recording money: debiting/crediting the balance (recording operations) - only through the/ledger database with transactions and idempotency.
Bet/win decisions prior to provider confirmation.
KYC/AML and compliance flags affecting payouts.
Secrets/tokens (cache in process memory is valid, but not shared cache).
3) Basic caching patterns
Cache-aside (lazy): the application first looks in the cache, if it misses, it reads from the database and puts it in the cache ('get → miss → load → set'). Versatile and safe to read.
Write-through: writing to the database goes through the cache; ensures the key is up to date, but increases the latency of the record.
Write-behind (write-back): writing first to the cache, then asynchronously to the database. Forbidden for money/results - risk of loss when falling.
Read-through: the cache itself knows how to get out of the database (proxy cache, for example, Redis with modules/sidecar). Good for metadata.
Recommendation: cache-aside for reads, write-through only where safe, write-behind - never for money/game truths.
4) Consistency and idempotency
Source of truth: ledger (append-only), operations with 'operation _ id' and idempotent processing.
Balance: we read from the cache, but any discrepancy is confirmed from the database before critical actions (deposit/withdrawal/large rate).
Disability: if the corresponding balance/status keys are successfully written to the → del/expire database.
Deduplication: outbox/inbox + idempotency keys for webhooks/payments; the cache does not participate in dedup, it only speeds up reading.
5) TTL, disability and "right to obsolescence"
Short-TTL for balance: 1-5 seconds (or soft-TTL with background refresh).
Transaction statuses: short TTL (5-30 s) with active disability by events ('deposit _ completed', 'settled').
Game history: TTL 1-10 minutes, disability due to 'new _ round' event.
Metadata/directories: TTL 10-60 minutes, warm-up when depleted.
Event-driven disability: event bus (Kafka/PubSub) publishes' wallet _ updated ',' bet _ settled ',' bonus _ changed '→ subscribers delete/update keys.
6) Anti-Storm Patterns (Miss Storm and Dogon)
Request coalescing: one thread "leads" the request to the database, the rest are waiting (mutex per key).
Stale-while-revalidate: give out "slightly outdated," simultaneously update in the background.
Jitter for TTL: Randomize TTL (± 20%) so keys do not expire at the same time.
Back-off on misses: with constant misses/errors - temporary negative-cache (see below).
7) Negative-caching and grey cardinal errors
For "not found" (for example, there is no transaction status yet) - a short negative TTL 1-3 s.
Do not cache database/provider errors for more than a few seconds - otherwise fix the accident.
Enter canary keys for observability: an increase in the share of negative hits is a reason for alert.
8) Key structure and segmentation
Именование: `wallet:{userId}`, `txn:{txnId}:status`, `game:{provider}:{tableId}:last_results`, `leaderboard:{tournamentId}:top100`.
Segments/namespaces by env/region/brand: 'prod: eu: wallet: {userId}' - exclude intersections and cross-region garbage.
Limit the cardinality - especially for leaderboards and history.
9) Cache on edge, in cluster and in memory
Edge cache (CDN/WAF): only for non-personal data (game metadata, public leaders, media). Query parameters - whitelist; cache-busting protection.
Redis/Memcached (cluster): basis for personal readings; Include AOF/RDB snapshots, replica, and quotas.
In-process cache: microsecond access for hot directories; disabling mechanisms (broadcast, version key) are required.
10) Money Cases: Safe Accelerations
Player balance
Read: cache-aside with TTL 1-5 s.
Record: transaction in the balance → del cache database; at a critical action (output/large bet) - "recheck from DB."
Antigone: optimistic locking version of the balance sheet.
Payment status
Scenario: the user presses "update status."
Solution: cache-aside + negative TTL to "pending "/" unknown "2-5 s; PSP Webhook Update → Disability.
Bonuses/vager
Aggregates (progress in%): cache 10-30 s; disability due to 'bet _ placed/settled' event.
11) Game cases: a high-speed front without distortions of truth
Spin/betting history
Last N events: cache list with restriction (for example, 100), TTL 1-10 minutes, replenishment by the 'round _ finished' event.
You cannot show "win" until there is confirmation from the provider → the intermediate status is "pending."
Live games (WebSocket)
Short-term cache of recent messages/table status for 1-3 seconds for fast connected clients.
Segment state keys by 'tableId/market'.
Leaderboards
Precompute + cache for 10-60 s; for mass updates - batch updates and partial disability of "windows."
12) Risks and how to close them
Double charge/phantom wins: read-only from cache; all charges/credits - through DB and idempotence.
Old data → dispute with the player: short TTL, "strict reality" before payment, transparent statuses ("awaiting confirmation").
Split-brain cache cluster: quorum/sentinel, timeouts, refuse write-behind.
Cache stampede on hot keys: coalescing, jitter, stale-while-revalidate.
Cache injection/poisoning: strong keys, signatures/signature for cached API responses, canary checks.
Privacy/PII: channel encryption (mTLS), cache prohibition on edge for personal data, short TTL, logout cleaning.
13) Cache observability
Metrics per layer:- Hit/Miss ratio by key category; redis_ops/sec, latency p95/p99, evictions, memory_usage.
- Canary keys: 'cache _ health: {segment}' - checks the share of negative cache and update time.
- Logs: misses "in batches," frequent 'del' on one segment = a sign of a "noisy" service.
- Trails: spans "cache get/set/del" with key tags (without PII).
14) Mini-architecture (reference)
1. Application (API/WS) → Redis cluster (TLS, auth).
2. Source of truth: Wallet DB (ledger), Game results store.
3. Event bus: 'wallet _ updated', 'bet _ settled', 'promo _ changed'.
4. Disabled: → 'del '/' set' hot key event subscriber.
5. Edge cache: public resources/leadership boards only.
6. Observability: cache dashboards, stampede alerts, negative hits.
15) TTL policies (sample matrix)
16) Sample Pseudo Code (Safe Balance Read)
python def get_balance(user_id):
key = f"wallet:{user_id}"
bal = cache. get(key)
if bal is not None:
return bal miss: take it from the database and put it with a short 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):
atomic entry in the database with idempotency if db. exists_op(op_id):
return db. get_result(op_id)
res = db. apply_ledger (op_id, user_id, delta) # cache transaction. delete (f "wallet: {user _ id}") # disability return res
17) Production readiness checklist
- Clear delimitation: truth in the database, cache - for reads only.
- Patterns: cache-aside for reads; write-behind is prohibited.
- Event disability: 'wallet _ updated', 'bet _ settled', 'promo _ changed'.
- Short TTL + jitter; negative-cache ≤ 3 с.
- Anti-storm: coalescing, stale-while-revalidate.
- Key segmentation by env/region/brand; cardinality limit.
- Observability: hit/miss, evictions, p95, alerts on stampede/negative-spikes.
- Edge cache for public data only; personal - only in Redis/TLS.
- Runbook: what to do when out of sync (forced refresh, temporarily disabling the segment cache).
- Regular tests: hot key load, stampede exercises.
Resume Summary
The cache in iGaming is a reading accelerator, not a "second database for money." Keep the truth in the ledger, ensure idempotence and event disability, keep short TTL and anti-storm mechanics, separate edge cache and personal data, monitor cache metrics. So you get a quick UX without the "illusion of winning," double charges and regulatory problems.