Tag: Caching

  • Hazelcast and Redis in Gaming

    We did not pick one cache — we mapped each workload to CAP

    On a live gaming platform, someone always asks: “Hazelcast or Redis?”

    As if one logo must win the whole cluster.

    We did not choose that way. After modularizing game engines — lobby, game, transactions on their own paths — we needed recoverable game state when a node dies, coordination across JVM services, and high-volume leaderboard reads that could tolerate a little lag. Hazelcast and Redis each fit a different CAP shape.

    This article is Hazelcast and Redis — not vs. How we use a remote Hazelcast distributed cluster for replicated table state and failover, and Redis for leaderboards. CAP is the lens; live tables, pinned game nodes, and money kept off the tick are the examples.

    Architecture, to us, is not picking logos. It is design, prototypes, and naming tradeoffs out loud. Choose C or A because the business can live with the downside — CAP is a lens, not a bible.

    CAP in one minute

    LetterPlain meaning
    C — ConsistencyEvery read sees the latest write (or you get an error)
    A — AvailabilityNon-failing nodes keep answering
    P — Partition toleranceSystem keeps running when the network splits

    P is not optional at scale. Under partition you pick C (refuse stale / fence / error) or A (keep serving, maybe stale).

    If the business says…Lean towardGaming example
    “Wrong money or double owner is unacceptable”C (refuse / fence / error)Table ownership, routing on failover
    “Empty screen or long outage hurts more than slightly stale data”A (keep serving)Leaderboard top 100, presence hints
    WorkloadCAP leanTool
    Live table state + failoverC when JVM or grid member failsHazelcast IMap + CP
    Leaderboard top 100A — empty board worse than stale rankRedis ZSET
    Wallet / payoutDurable CDB + queue

    Same company, same platform — different maps, different clusters, different backup settings.

    Our model: pinned node + IMap replica

    We pin one game to one game node. That node is the active writer — pot, seats, turn, timers — single owner, local memory, fast tick.

    Every state-changing action (fold, call, raise, timer) also replicates a snapshot to Hazelcast `IMap` on a remote cluster with sync backup. If the pinned node dies, a new node loads that snapshot and resumes — same pot, same turn.

    LayerRole
    Pinned game node (local)Active writer during play — every fold/call/raise mutates local state first (fast tick)
    Hazelcast IMap (sync backup)Recovery copy — replicate snapshot on each state change; new node loads table if pinned node dies
    CP lock + fencingOnly one game node may resume the table after failover
    Table → node routing (IMap)Where clients reconnect after failover
    Queue + DBBuy-in / payout — durable money truth, not per-action IMap

    Normal action: local mutate → IMap.set(tableId, snapshot) → broadcast

    Game node dies: CP lock → new node IMap.get → hydrate → clients reconnect

    oney still flows queue + DB — not per-action cache ledger.

    Hazelcast: remote distributed cluster (not embedded)

    Dedicated six-member grid. Game / lobby / transaction services are clients only.

    ModelWhat it meansWhat we did
    EmbeddedEach game JVM is also a Hazelcast memberNo — different scale lifecycles
    Client → remote clusterGame services are HZ clients; grid is dedicatedYes — our model
    [ Game node A — active writer ]  ──client──┐
    [ Game node B — failover target ] ──client──┼──►  [ Hazelcast cluster ]
    [ Lobby service ]                ──client──┘         game state IMap (sync backup)
                                                        routing IMap, CP locks

    Why remote cluster: scale game fleet independently; blast radius; tune sync backup on grid nodes; game JVM churn does not reshape partitions.

    Jobs we gave Hazelcast

    JobWhy Hazelcast (remote cluster)
    Replicated game state (IMap)Every state-changing action → snapshot to IMap with sync backup; new game node loads table if pinned node dies
    Table → node routingCentral IMap; all services know where each table lives
    Ownership on failoverCP subsystem + fencing — only one node may resume the table
    Soft metadataPresence, session hints — rebuildable; async OK

    CAP justification: losing a live money table mid-hand, or two nodes serving different pots, is unacceptable → sync backup on game-state `IMap`, CP + fencing on failover.

    What we did not do: two writers without fencing wallet ledger in HZ async-only zero-backup on game-state maps.

    Redis: leaderboards (AP-leaning)

    JobWhy Redis
    Global / seasonal leaderboardsZSET — score-ordered ranks, top-N in one structure
    High read volumeSimple, fast, operationally familiar
    Ranking displayProduct tolerates seconds of staleness across replicas

    Leaderboards are AP-friendly: show rank #6 when true rank just became #5 beats an empty board. Redis ZSET, high reads, replica lag OK for display.

    What we did not do: Redis as sole payout source; linearizable global #1 on every read full match simulation in Redis.

    WorkloadCAP preferenceChoiceWhy
    Live table / pot / turnStrong C per matchGame node + HZ IMapLocal active writer; replicate snapshot to sync-backed IMap for failover
    Table → node routingC on failoverHazelcastSync backup on dedicated HZ members; game nodes are clients
    Ownership after node deathCP (no split-brain)Hazelcast CPOne successor; fence old owner
    Buy-in / payout handoffDurable, orderedQueue + DBNot cache-as-ledger; gameplay stays off money path
    Leaderboard / rankingA over strict CRedisZSET top-N; brief staleness OK; high read QPS
    Presence / soft hintsEventual OKHZ async or TTLRebuildable; not money truth

    When things fail

    Game node goes down

    1. Pinned node dies mid-match

    2. One successor acquires CP lock (fences old node)

    3. ‘IMap.get(tableId)` → rebuild local state

    4. Routing updated → players reconnect

    5. Resume or void by product rules

    CAP: C on failover — we paid sync replication during play.

    Hazelcast grid member goes down

    1. HZ member loss → backup promotes → short rebalance

    2. Game clients retry `get`/`set`

    3. Replicated game state survives on promoted copy

    Avoid: state living only in one JVM with no recoverable `IMap` copy.

    Anti-patterns

    • One cache for everything — CAP differs by workload
    • Redis Redlock for table ownership — we used Hazelcast CP + pinned node
    • Hazelcast for every leaderboard — Redis ZSET won for AP serving
    • Async / zero-backup on game-state `IMap`
    • Strong consistency everywhere — unnecessary for presence and ranks

    Wrapping-Up

    Hazelcast and Redis — not vs.

    Hazelcast — remote cluster: replicated game state in `IMap`(sync backup), routing, CP on failover. Active writer on pinned node; recoverable copy on the grid.

    Redis — leaderboards: available top-N, briefly stale OK.

    CAP: pick C or A per workload. Architecture: design, prototype, name tradeoffs. Sync backup costs latency — we paid it where failure consistency mattered. Replica lag costs perfect ranks — we paid it where availability mattered.

    Draw the workload on CAP first. Then place Hazelcast, Redis, and the game server.

    if your platform debates “Hazelcast or Redis” as one winner — what workload would you map first?