Skip to content

Thiết kế: Matching & Matchmaking Feature

1. Tổng quan kiến trúc

flowchart TB
    subgraph Client
        WS["WebSocket\nJOIN/LEAVE/QUERY commands"]
    end

    subgraph WebSocket Module
        MC["MatchMatchingCmdConsumer"]
        OPC["OpponentPoolCmdHandler"]
        IC["InvitationCmdConsumer"]
        CRS["CreateRoomService\n(routing + validation)"]
    end

    subgraph ActiveMQ
        Q_PRIZE["queue-prize-allocation-PRIZE"]
        Q_PS["queue-prize-allocation-PRIZE_FOR_STREAMER"]
        Q_UN["queue-prize-allocation-UNLIMITED"]
        Q_INV["queue-invitation"]
        Q_PVPM["queue-{settingId}-PVP-MULTI"]
        Q_PVPS["queue-{settingId}-PVP"]
    end

    subgraph Batch Module
        PAL_P["JMSPrizeAllocationListener\n(PRIZE)"]
        PAL_PS["JMSPrizeForStreamerAllocationListener"]
        IL["JMSInvitationListener"]
        MM_P["JMSMatchingGameModePvPMessageListener"]
        MM_PM["JMSMatchingGameModePvPMultiMatchMessageListener"]
        SCHED["MatchMakingForAllGameModeConfig\n(scheduler)"]
    end

    subgraph Core
        UMS["UserMatchingService\n(Redis matching state)"]
        OPS["OpponentPoolService\n(Redis pool state)"]
        InvSvc["InvitationCacheService\n(Redis invitation)"]
    end

    WS --> MC & OPC & IC
    MC --> CRS
    OPC --> CRS
    IC --> CRS

    CRS -->|"PRIZE"| Q_PRIZE
    CRS -->|"PRIZE_FOR_STREAMER"| Q_PS
    CRS -->|"UNLIMITED"| Q_UN
    CRS -->|"invitation"| Q_INV

    Q_PRIZE --> PAL_P --> UMS
    Q_PS --> PAL_PS --> UMS
    Q_UN --> |"opponent pool"| OPS
    Q_INV --> IL --> InvSvc
    Q_PVPM --> MM_PM
    Q_PVPS --> MM_P

    SCHED -->|"periodic matching"| UMS
    UMS --> BCAST["BroadcastPubSubService"]
    BCAST -->|"MATCH_FOUND"| Client

2. Multi-match vs Single-match routing

flowchart LR
    CMD["JOIN_QUEUE_MATCH_MATCHING"] --> CHECK{prize_ids present?}
    CHECK -->|"Yes: prizeIds[]"| MULTI["PVP_MULTI_MATCH mode\ncheckAllocationTypesByPrizeIds()"]
    CHECK -->|"No: prizeId"| SINGLE["Single prize mode\ndetermineAllocationType(prizeId)"]

    MULTI --> STREAM{any streaming prize?}
    STREAM -->|Yes| PS_Q["queue-prize-allocation-PRIZE_FOR_STREAMER"]
    STREAM -->|No| P_Q["queue-prize-allocation-PRIZE"]

    SINGLE --> TYPE{reservedType?}
    TYPE -->|STREAMING| PS_Q
    TYPE -->|default| P_Q

3. Sequence diagrams

3.1 JOIN_QUEUE_MATCH_MATCHING (multi-match)

sequenceDiagram
    participant C as Client
    participant MC as MatchMatchingCmdConsumer
    participant CRS as CreateRoomService
    participant Q as queue-prize-allocation-PRIZE_FOR_STREAMER
    participant L as JMSPrizeForStreamerAllocationListener
    participant UMS as UserMatchingService
    participant R as Redis

    C->>MC: JOIN_QUEUE_MATCH_MATCHING\n{playableGameBoothSettingId, prize_ids: [p1, p2]}
    MC->>MC: detect prize_ids (multi-match mode)
    MC->>CRS: joinAutoMatchMatching(userId, settingId, prizeIds)
    CRS->>CRS: validatePrizeIds(prizeIds)
    CRS->>CRS: checkAllocationTypesByPrizeIds(prizeIds)
    Note over CRS: Any streaming prize → PRIZE_FOR_STREAMER
    CRS->>Q: sendMessageWithActionTypeMultiMatch(prizeIds, PRIZE_FOR_STREAMER)
    Q->>L: onMessage(messageJson)
    L->>L: validate allocationType == PRIZE_FOR_STREAMER
    L->>UMS: joinAutoMatchMatchingSwitchMultiMatch(userId, settingId, prizeIds)
    UMS->>R: set matching state (userId → matchingRecord)
    UMS-->>L: success
    L->>BCAST: broadcast JOIN_QUEUE_SUCCESS to userId
    BCAST-->>C: {event_type: JOIN_QUEUE_SUCCESS}

3.2 SEND_INVITATION

sequenceDiagram
    participant C as Client (Sender)
    participant T as Target User
    participant IC as InvitationCmdConsumer
    participant CRS as CreateRoomService
    participant Q as queue-invitation
    participant L as JMSInvitationListener
    participant R as Redis

    C->>IC: SEND_INVITATION {targetUserId, prizeId, settingId}
    IC->>CRS: sendInvitation(senderId, targetId, prizeId, settingId)
    CRS->>Q: enqueue invitation message
    Q->>L: process invitation
    L->>R: save invitation record (with TTL)
    L->>BCAST: push INVITATION_RECEIVED to target
    BCAST-->>T: {event_type: INVITATION_RECEIVED, from: senderId}

3.3 Matchmaking scheduler

sequenceDiagram
    participant SCHED as MatchMakingForAllGameModeConfig
    participant UMS as UserMatchingService
    participant R as Redis
    participant CRJ as CreateRoomJMSService
    participant BCAST as BroadcastPubSubService

    loop Every N seconds
        SCHED->>UMS: runMatchmaking(settingId, gameMode)
        UMS->>R: get all waiting users per settingId
        UMS->>UMS: group users by matching criteria
        loop per matchable pair
            UMS->>CRJ: createMatchedRoom(userIds, prizeIds)
            CRJ->>BCAST: broadcast MATCH_FOUND + room info
            BCAST-->>Client: {event_type: MATCH_FOUND, room: {...}}
        end
    end

4. State machine - Matching state

stateDiagram-v2
    [*] --> QUEUED: JOIN_QUEUE_MATCH_MATCHING

    QUEUED --> MATCHED: Scheduler finds match
    QUEUED --> CANCELLED: LEAVE_QUEUE_MATCH_MATCHING
    MATCHED --> [*]: Room created

    note right of QUEUED: Redis key: userId → matchingRecord
    note right of MATCHED: Broadcast MATCH_FOUND
    note right of CANCELLED: Redis key deleted

5. Invitation TTL flow

stateDiagram-v2
    [*] --> PENDING: SEND_INVITATION
    PENDING --> ACCEPTED: ACCEPT_INVITATION
    PENDING --> DENIED: DENY_INVITATION
    PENDING --> EXPIRED: TTL elapsed

    ACCEPTED --> [*]: Create room triggered
    DENIED --> [*]: Notify sender
    EXPIRED --> [*]: Batch cleanup

    note right of EXPIRED: DeleteExpiredInvitationConfig\ncleans up stale records

6. Error handling

Tình huống Event Reason
Inconsistent prize_ids allocation types VALIDATE_GAME INVALID_PRIZE_ALLOCATION_TYPE
Invalid prize_id/prize_ids VALIDATE_GAME INVALID_PRIZE_IDS
Leave queue khi không có record Log warning, no error -
Invitation expired khi accept VALIDATE_GAME INVITATION_EXPIRED
Target user busy (in room) VALIDATE_GAME TARGET_USER_BUSY

7. Điểm cần chú ý

# Vấn đề Chi tiết
1 prize_id vs prize_ids Cả 2 format đều phải support (backward compat)
2 Matchmaking scheduler timing Cần tune interval vs matching latency
3 Invitation TTL Cần config TTL phù hợp UX (không quá ngắn)
4 Leave graceful Leave khi không có record phải silent (current implementation đúng)