Rolling Service
Status
Active
Date
2026-04-28
Owners
- Platform Backend
Last Verified Commit
56362a7a
Runtime
API + standalone worker
Purpose
rolling_service owns rolling records and the rolling lifecycle:
- create
- complete
- cancel
- expire
- retry completion
Primary Entry Points
/internal/rolling/*
Protection model:
/internal/rolling/*requiresX-Internal-Service-Tokengateway,admin_service, andpromotion_serviceare the intended callers
Dependencies
- PostgreSQL
- Redis
wallet_service
Background Work
rolling_worker runs:
- wallet-event consumer loop
- rolling outbox publisher
- expiry scheduler
- completion retry loop
Owned Data
- rolling records
- rolling inbox/outbox state
- rolling retry and expiry semantics
Events
Emits:
ROLLING_COMPLETEDROLLING_CANCELED
Consumes:
- wallet stream events including:
BET_SETTLED_CONFIRMEDBET_ROLLED_BACK_CONFIRMED
Health
- API
/healthchecks DB and Redis rolling_workerreadiness is freshness-based across:- event consumer loop
- outbox loop
- expiry loop
- completion retry loop
Key Env Vars
DATABASE_URLREDIS_URLWALLET_SERVICE_URLMULTI_BRAND_ENFORCEMENT— required; one ofoff/observe/enforce. Production target isenforcepost-Phase-16. Drivesmulti_brand_enforcement_mode{service="rolling_service"}gauge and the cross-brand reject decision inbrand_check.py(rolling_cross_brand_rejected_total).BRAND_SIGNING_KEY— required in production; HMAC-SHA256 secret used to signX-Brand-Signatureon outbound brand-scoped wallet writes from rolling-completion / cancel paths.INTERNAL_SERVICE_TOKEN_ROLLING— required in production; per-caller token presented to wallet when rolling calls it.PER_CALLER_TOKEN_REQUIRED—onis the Phase 16 target; activates the legacy-token hard-reject (T4-D-I2) on inbound/internal/rolling/*calls.INTERNAL_SERVICE_TOKEN— legacy single-shared-token; deprecated. Phase 16 release gate requires the bare variant to be absent.ENABLE_EVENT_CONSUMERENABLE_OUTBOX_POLLERENABLE_EXPIRY_SCHEDULERENABLE_COMPLETION_RETRY
Multi-Brand Constraints
Per ADR-009:
- rolling records, rolling inbox/outbox, and completion/cancel retry state carry
brand_id - the rolling event consumer reads
brand_idfrom inbound wallet/game outbox payloads and persists it into rolling rows - internal rolling routes require
X-Brand-Id; mismatched-player-row behavior is gated byMULTI_BRAND_ENFORCEMENT(observelogs + counts and proceeds;enforcerejects) - per-brand rolling completion ratios and cancellation policies resolve from
brand_configfirst, then documented global defaults
Tests
cd servers_v2/rolling_service && uv run pytest- key suites:
tests/test_events_health.pytests/test_retry_tasks.pytests/test_wallet_response_compat.pytests/test_outbox_poller.pytests/test_event_consumer_contracts.pytests/test_internal_auth.py