Skip to main content

Documentation Index

Fetch the complete documentation index at: https://285e39fd5e337e58f16290.sightscreen.app/llms.txt

Use this file to discover all available pages before exploring further.

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.
KeyTypeDescription
matchId (PK)StringSportMonks fixture ID
GSIs:
GSI namePartition keySort keyPurpose
leagueCode-startTime-indexleagueCodestartTimeQuery fixtures by league, sorted by start time
Accessed by: FixtureService (read/write), FixtureSyncJob (write), DisplayStateBuilder (read) Access patterns:
PatternTypeOperationKey conditionTriggered byFrequency
Get fixture by IDBothGetItemPK=matchIdGET /matches/:id, ScorePollerPer request / every poll tick
Batch get fixturesBothBatchGetItemPK=matchId (up to 100)GET /matches, FixtureSyncJobPer request / per sync cycle
Get matches by date rangeUserQuery (GSI)leagueCode-startTime-index, leagueCode = :code AND startTime BETWEEN :from AND :toGET /matches?league=&from=&to=Per user request
Batch write fixturesSystemBatchWriteItemPK=matchId (batches of 25)FixtureSyncJobPer sync cycle
Update fixture fieldsSystemUpdateItemPK=matchId with ConditionExpression: attribute_exists(matchId)ScorePoller, FixtureSyncJobPer live match per tick
Archive fixtureSystemUpdateItemPK=matchId, SETs archivedAt, REMOVEs liveUpdatedAtLiveMatchReconcilerWhen match leaves livescores
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.
KeyTypeDescription
matchId (PK)StringFixture match ID
userId#deviceId (SK)StringComposite sort key: Cognito user ID + device identifier
GSIs:
GSI namePartition keySort keyPurpose
userId-indexuserId-Query all Live Activities for a user across matches
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:
PatternTypeOperationKey conditionTriggered byFrequency
Get all LA sessions for matchSystemQueryPK=matchIdLACoordinator deliveryEvery poll tick per live match
Get user’s LA sessionsUserQuery (GSI)userId-index, userId = :uidGET /user/activitiesPer request
Register LA sessionUserUpdateItem (upsert)PK=matchId, SK=userId#deviceIdPOST /user/activity/:matchIdWhen iOS starts Live Activity
Update tokenUserUpdateItemPK=matchId, SK=userId#deviceIdToken rotation from iOSOn token refresh
End LA sessionBothUpdateItemPK=matchId, SK=userId#deviceId, SET status=ended, endedAt, endReasonDELETE /user/activity/:matchId, match completionUser action / match end
Suspend LA sessionSystemUpdateItemPK=matchId, SK=userId#deviceId, SET status=suspended, suspendedAtAPNs token rejectionOn push failure
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.
KeyTypeDescription
matchId (PK)StringFixture match ID
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:
PatternTypeOperationKey conditionTriggered byFrequency
Get active match stateSystemGetItemPK=matchIdScorePoller, MatchEventBusEvery poll tick per match
Get all active matchesSystemScanFull table scan (typically 5-20 items)MatchOrchestrator reconciliationEvery reconciliation cycle
Register active matchSystemPutItem (conditional)PK=matchId with ConditionExpression: attribute_not_exists(matchId) OR status = :idleMatchOrchestratorWhen match goes live
Update match stateSystemUpdateItemPK=matchId, SET previousScore, previousBatting, previousBowling, last30Balls, cooldowns, lastTickAtScorePoller after each tickEvery poll tick
Update cooldownsSystemUpdateItemPK=matchId, SET cooldownsCooldownFilterAfter event filtering
Deactivate matchSystemUpdateItemPK=matchId, SET status=ended, ttlMatchOrchestratorWhen match completes
Delete active matchSystemDeleteItemPK=matchIdCleanup after TTL expiryRare, manual
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.
KeyTypeDescription
teamId (PK)StringSportMonks team ID
Accessed by: TeamService (read/write), DisplayStateBuilder (read) Access patterns:
PatternTypeOperationKey conditionTriggered byFrequency
Get team by IDBothGetItemPK=teamIdGET /teams/:id, DisplayStateBuilderPer request / per enrichment
Get all teamsUserScanFull table scan (< 100 items)GET /teamsPer request, cached
Batch write teamsSystemBatchWriteItemPK=teamId (batches of 25)TeamSyncJobPer sync cycle
Update team fieldsUserUpdateItemPK=teamId with ConditionExpression: attribute_exists(teamId)PATCH /teams/:id (admin)Rare, admin-only

cricket-team-seasons

Per-season stats for each team within a league season.
KeyTypeDescription
seasonId (PK)StringSportMonks season ID
teamId (SK)StringSportMonks team ID
Accessed by: TeamSeasonService (read/write) Access patterns:
PatternTypeOperationKey conditionTriggered byFrequency
Get teams for a seasonBothQueryPK=seasonIdGET /seasons/:id/teams, DisplayStateBuilderPer request / per enrichment
Write season-team recordsSystemBatchWriteItemPK=seasonId, SK=teamId (batches of 25)TeamSyncJobPer sync cycle

cricket-leagues

League/competition reference data.
KeyTypeDescription
leagueId (PK)StringSportMonks league ID
Accessed by: LeagueService (read/write), DisplayStateBuilder (read) Access patterns:
PatternTypeOperationKey conditionTriggered byFrequency
Get league by IDBothGetItemPK=leagueIdGET /leagues/:id, DisplayStateBuilderPer request / per enrichment
Get all leaguesUserScanFull table scan (< 30 items)GET /leaguesPer request, cached in-memory
Get active leaguesSystemScan + filterFilterExpression: active = trueMatchOrchestrator, FixtureSyncJobPer job cycle, cached
Create/replace leagueSystemPutItemPK=leagueIdLeagueSyncJobPer sync cycle
Update league fieldsUserUpdateItemPK=leagueId with ConditionExpression: attribute_exists(leagueId)PATCH /leagues/:id (admin)Rare, admin-only
Delete leagueUserDeleteItemPK=leagueIdAdmin endpointRare

cricket-devices

Maps users to their registered push notification device tokens. Now supports multi-device with additional device metadata.
KeyTypeDescription
userId (PK)StringCognito user ID
deviceToken (SK)StringAPNs device token
Key attributes: platform, apnsToken, pushToStartToken, osVersion, appVersion, ttl TTL: Yes — stale device registrations expire automatically. Accessed by: DeviceService (read/write), ApnsDeliveryService (read) Access patterns:
PatternTypeOperationKey conditionTriggered byFrequency
Get device for userBothQueryPK=userId, Limit: 1GET /user/device, ApnsDeliveryServicePer push send / per request
Register device (upsert)UserQuery + Delete + UpdateItemQuery PK=userId to find stale records, Delete orphans, then UpdateItem PK=userId, SK=deviceToken with osVersion, appVersionPOST /user/deviceOn app launch
Update push-to-start tokenUserGetItem + UpdateItemPK=userId, SK=deviceTokenPUT /user/device/push-to-startOn token refresh
Delete deviceUserDeleteItemPK=userId, SK=deviceToken with ReturnValues: ALL_OLDDELETE /user/deviceUser action
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).
KeyTypeDescription
matchId (PK)StringFixture match ID
userId (SK)StringCognito user ID
Accessed by: MatchFollowService (read/write), ApnsDeliveryService (read) Access patterns:
PatternTypeOperationKey conditionTriggered byFrequency
Check if user follows matchUserGetItemPK=matchId, SK=userIdGET /user/follow/:matchIdPer request
Get all followers for matchSystemQueryPK=matchId (paginated)LACoordinatorEvery poll tick per live match
Follow a matchUserUpdateItemPK=matchId, SK=userId, SET teamId, expiresAt, createdAt = if_not_exists(...)POST /user/follow/:matchIdUser action
Unfollow a matchUserDeleteItemPK=matchId, SK=userId with ReturnValues: ALL_OLDDELETE /user/follow/:matchIdUser action
Update follow teamUserUpdateItemPK=matchId, SK=userId with ConditionExpression: attribute_exists(matchId)PATCH /user/follow/:matchIdUser action
Remove all follows for matchSystemBatchDeleteItemQuery PK=matchId, then batch delete allMatch completion cleanupOnce per match end
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.
KeyTypeDescription
lockKey (PK)StringLock identifier (usually match:{matchId})
Key attributes: owner (instance ID), expiresAt (epoch timestamp for auto-release) Accessed by: LockService (read/write) Access patterns:
PatternTypeOperationKey conditionTriggered byFrequency
Acquire lockSystemPutItem (conditional)PK=lockId with ConditionExpression: attribute_not_exists(lockId) OR expiresAt < :nowScorePoller, MatchOrchestratorEvery poll tick per live match
Release lockSystemDeleteItem (conditional)PK=lockId with ConditionExpression: holder = :holderScorePoller completionAfter each processing tick
Extend lockSystemUpdateItem (conditional)PK=lockId with ConditionExpression: holder = :holderLong-running jobsDuring processing
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.
KeyTypeDescription
matchId (PK)StringFixture match ID
TTL: Yes — cache entries expire and are rebuilt on next request. Accessed by: DisplayStateCacheService (read/write) Access patterns:
PatternTypeOperationKey conditionTriggered byFrequency
Get cached display stateBothGetItemPK=cacheKey (composite: DISPLAY#{matchId}#{teamId})GET /matches/:id/display, ScorePollerPer request / per tick
Set cached display stateBothPutItemPK=cacheKey with TTLDisplayStateBuilder after computationAfter each state computation
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.
KeyTypeDescription
matchId (PK)StringFixture match ID
TTL: Yes — cached data expires based on match state (live matches have shorter TTLs). Accessed by: FixtureCacheService (read/write), SportMonksClient (via provider) GSIs:
GSI namePartition keySort keyPurpose
leagueId-startingAt-indexleagueIdstartingAtQuery cached fixtures by league
Access patterns:
PatternTypeOperationKey conditionTriggered byFrequency
Get cached fixtureBothGetItemPK=matchIdGET /matches/:id, ScorePollerPer request / per tick
Batch get cached fixturesBothBatchGetItemPK=matchId (up to 100, with retry)GET /matches, FixtureSyncJobPer request / per sync
Get fixtures by leagueBothQuery (GSI)leagueId-startingAt-index, leagueId = :lid with filter ttl > :nowGET /matches?league=, ScorePollerPer request / per tick
Get all cached fixturesSystemScanFull table scan (30-50 items)FixtureSyncJobPer sync cycle
Put single fixtureSystemPutItemPK=matchIdScorePollerPer live match per tick
Batch put fixturesSystemBatchWriteItemPK=matchId (batches of 25, with retry)FixtureSyncJobPer sync cycle

Removed tables

The following tables existed pre-LA v2 and have been removed:
Old tableReplaced byReason
cricket-activity-subscriptionslive-activitiesNew table with composite SK (userId#deviceId), GSI, and session lifecycle tracking
match-processing-stateactive-matchesNew table stores full previous state for event detection diffing
cricket-prematch-notificationsMatchOrchestratorOrchestrator manages the full match lifecycle including pre-match, no separate tracking needed
cricket-state-hashes(removed)The pipeline skips delivery if nothing changed — no need for explicit hash comparison

Access pattern summary

Service / ProviderTables accessedMode
FixtureServicecricket-fixturesRead/Write
TeamServicecricket-teamsRead/Write
TeamSeasonServicecricket-team-seasonsRead/Write
LeagueServicecricket-leaguesRead/Write
DeviceServicecricket-devicesRead/Write
MatchFollowServicecricket-match-followsRead/Write
LiveActivityRepositorylive-activitiesRead/Write
ActiveMatchRepositoryactive-matchesRead/Write
LockServicecricket-locksRead/Write
DisplayStateCacheServiceDisplayStateCacheRead/Write
FixtureCacheServicecricket-fixture-cacheRead/Write
LACoordinatorlive-activities, cricket-match-followsRead
ApnsDeliveryServicecricket-devices, live-activitiesRead
DisplayStateBuildercricket-fixtures, cricket-teams, cricket-leaguesRead
MatchOrchestratoractive-matches, cricket-fixturesRead/Write
ScorePolleractive-matches, cricket-fixture-cacheRead/Write
HubDataServiceactive-matches, live-activities, cricket-fixturesRead