Skip to main content

Table overview

All persistent storage lives in DynamoDB. There are no relational databases. Each table is owned by a single repository, though multiple services may read from it via that repository.
LA v2 replaced several tables: activity-subscriptions is now live-activities, match-processing-state is now active-matches, and prematch-notifications and state-hashes have been removed entirely.

Table details

cricket-fixtures

The central table. Stores every match the system knows about. GSIs: Accessed by: FixtureService (read/write), FixtureSyncJob (write), DisplayStateBuilder (read) Access patterns:
The live-index GSI has been removed. The active-matches table now serves as the source of truth for which matches are currently being polled, replacing the sparse GSI pattern.

live-activities

Tracks Live Activity sessions. When a user starts a Live Activity on iOS, their update token and session metadata are stored here. Replaces the old activity-subscriptions table. GSIs: Key attributes: updateToken (APNs Live Activity push token), status (active/ended/suspended), sessionStartedAt, endedAt, endReason, suspendedAt, ttl TTL: Yes — sessions expire after the match ends. Accessed by: LiveActivityRepository (read/write), LACoordinator (read), ApnsDeliveryService (read) Access patterns:
The SK is userId#deviceId (composite), enabling multi-device support. A user with iPhone + iPad has 2 separate session records for the same match. The userId-index GSI allows querying all of a user’s active sessions across matches.

active-matches

Tracks all matches currently being polled, along with their processing state. Replaces both the old match-processing-state table and the live-index GSI on fixtures. Each record is approximately 5KB and contains everything the event detection pipeline needs to detect changes. Key attributes: status, instanceId (which server instance owns this match), lastTickAt, matchPhase, previousBallId, previousScore, previousStatus, previousBatting, previousBowling, last30Balls, cooldowns, ttl TTL: Yes — records expire after the match ends + buffer. Accessed by: ActiveMatchRepository (read/write), MatchOrchestrator (write), ScorePoller (read/write), MatchEventBus (read) Access patterns:
The previousScore, previousBatting, previousBowling, and last30Balls fields store the state from the last poll tick. The event detection pipeline diffs current data against these previous values to determine what changed. The cooldowns map tracks per-event-type cooldown expiry timestamps.
The Scan on this table is intentional and acceptable — at peak there are only 5-20 active matches, so a full scan costs less than 1 RCU. This replaces the old sparse GSI pattern on the fixtures table.

cricket-teams

Static team reference data synced from SportMonks. Accessed by: TeamService (read/write), DisplayStateBuilder (read) Access patterns:

cricket-team-seasons

Per-season stats for each team within a league season. Accessed by: TeamSeasonService (read/write) Access patterns:

cricket-leagues

League/competition reference data. Accessed by: LeagueService (read/write), DisplayStateBuilder (read) Access patterns:

cricket-devices

Maps users to their registered push notification device tokens. Now supports multi-device with additional device metadata. Key attributes: platform, apnsToken, pushToStartToken, osVersion, appVersion, ttl TTL: Yes — stale device registrations expire automatically. Accessed by: DeviceService (read/write), ApnsDeliveryService (read) Access patterns:
Device registration enforces single-device-per-user: it queries all records for the user, deletes any with a different deviceToken, then upserts the current one. The osVersion and appVersion fields are captured on registration for debugging push delivery issues.

cricket-match-follows

Tracks which users are following which matches (for push notifications on score changes). Accessed by: MatchFollowService (read/write), ApnsDeliveryService (read) Access patterns:
expiresAt is set to match start time + 48h buffer. DynamoDB TTL automatically cleans up follow records for completed matches — no cron job needed.

cricket-locks

Distributed locking table. Prevents multiple backend instances from processing the same match concurrently. Key attributes: owner (instance ID), expiresAt (epoch timestamp for auto-release) Accessed by: LockService (read/write) Access patterns:
All three operations use conditional writes for atomicity. ConditionalCheckFailedException on acquire means another instance holds the lock — this is expected behavior, not an error. Default TTL is 10 seconds, acting as an auto-release safety net if the holder crashes.

DisplayStateCache

Caches the fully-computed display state for a match. Avoids recomputing on every API request. TTL: Yes — cache entries expire and are rebuilt on next request. Accessed by: DisplayStateCacheService (read/write) Access patterns:
The cache key is DISPLAY#{matchId}#{teamId}, allowing per-team display state caching. Client-side TTL validation is performed in addition to DynamoDB TTL — if ttl has passed, the GetItem returns null even before DynamoDB deletes the record.

cricket-fixture-cache

Caches raw fixture data from SportMonks to reduce API calls. TTL: Yes — cached data expires based on match state (live matches have shorter TTLs). Accessed by: FixtureCacheService (read/write), SportMonksClient (via provider) GSIs: Access patterns:

Removed tables

The following tables existed pre-LA v2 and have been removed:

Access pattern summary