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.
Sightscreen runs three background jobs inside the main server process. They execute on independent timers and coordinate through DynamoDB state and an internal EventBus.
Job overview
| Job | Purpose | Interval | Status |
|---|
MatchOrchestrator | Detect live transitions, create LA records, recovery | 30s | Active |
ScorePoller | Poll for live score data with cockatiel resilience | 5s (idle) / 2.5s (live) | Active |
FixtureSyncJob | Sync fixture metadata from SportMonks | 60s | Active |
LiveScoreJob and ReplayScoreJob still exist as thin wrappers around ScorePoller. LiveScoreJob points at SportMonks; ReplayScoreJob points at mock-sportmonks.
System diagram
MatchOrchestrator
Runs every 30 seconds. Responsible for detecting when matches go live and managing Live Activity lifecycle.
What it does
- Queries the matches table for matches that have transitioned to
live status.
- For each newly live match, creates pending Live Activity records for all users following that match.
- Sends push-to-start tokens via APNs to boot Live Activities on user devices.
- Writes the match into the ActiveMatches table so ScorePoller picks it up.
Startup recovery
On process startup, MatchOrchestrator scans the ActiveMatches table and reconciles with current match state. If matches are live but have no active orchestration (e.g., after a deploy or crash), it re-creates the necessary records and resumes polling.
Stale instance takeover
If a match record in ActiveMatches has not been updated within a configurable threshold, MatchOrchestrator assumes the owning instance is dead and takes over. This prevents matches from going “dark” during rolling deploys or instance failures.
Tables read/written
| Table | Operation |
|---|
| Matches table | Read (detect live transitions) |
| ActiveMatches table | Read/Write (track active matches) |
| LiveActivities table | Write (create pending LA records) |
| Subscriptions table | Read (users following the match) |
| Devices table | Read (push-to-start tokens) |
Relevant env vars
| Variable | Purpose |
|---|
ACTIVE_MATCHES_TABLE | Table for tracking actively orchestrated matches |
LIVE_ACTIVITIES_TABLE | Table for Live Activity records |
MARQUEE_LEAGUE_ID | League ID for marquee match detection |
Failure modes
- DynamoDB read failure: Cycle skipped, retried next interval.
- Push-to-start failure: LA record remains pending. Next cycle retries delivery.
- Stale takeover race condition: Two instances may briefly both claim a match. The distributed lock in ScorePoller prevents duplicate score pushes.
ScorePoller
Reusable polling component used by both LiveScoreJob (real matches) and ReplayScoreJob (mock-sportmonks replay). Polls a score source at a configurable interval and publishes updates to the EventBus.
What it does
- Acquires a distributed lock for the target match (prevents duplicate polling across instances).
- Fetches score data from the configured source (SportMonks or mock-sportmonks).
- Compares with the last known state. If changed, publishes a score update event to the EventBus.
- The EventBus fans out to downstream services:
ApnsDeliveryService, LiveActivityService, HubDataService.
Polling interval
- 5 seconds in idle mode (waiting for ball-by-ball data).
- 2.5 seconds when a match is actively live with ball data flowing.
Cockatiel resilience
ScorePoller uses cockatiel for resilience policies:
- Retry: Exponential backoff on transient failures (network timeouts, 5xx responses).
- Circuit breaker: Opens after consecutive failures to avoid hammering a down upstream. Half-open probe after cooldown.
- Timeout: Per-request timeout to prevent hung connections from blocking the poll loop.
- Bulkhead: Limits concurrent outbound requests to prevent resource exhaustion.
During a circuit breaker open state, the last known scores remain in DynamoDB. Clients continue to see the most recent data, just not real-time updates.
Distributed lock
Each ScorePoller instance acquires a DynamoDB-based lock for the match it is polling. The lock has a TTL and is renewed on each successful cycle. If an instance dies, the lock expires and another instance (via MatchOrchestrator stale takeover) picks up the match.
Tables read/written
| Table | Operation |
|---|
| ActiveMatches table | Read/Write (distributed lock, last poll state) |
| Scores table | Write (updated score data) |
Dependencies
- SportMonks API (LiveScoreJob) or mock-sportmonks (ReplayScoreJob) as the data source.
- EventBus for downstream fan-out.
Relevant env vars
| Variable | Purpose |
|---|
SPORTMONKS_API_TOKEN | API token for SportMonks |
SPORTMONKS_SEASON_ID | Season to query |
MOCK_SPORTMONKS_URL | URL of mock-sportmonks (replay mode) |
MOCK_SPORTMONKS_TOKEN | Token for mock-sportmonks |
Failure modes
- SportMonks down: Circuit breaker opens. Scores freeze at last known state. Auto-recovers when SportMonks returns.
- DynamoDB write failure: Retried on next cycle. No data loss since the upstream API is the source of truth.
- Lock contention: If two instances race, only one acquires the lock. The other backs off.
- Malformed response: Logged and skipped. Existing score record preserved.
ReplayScoreJob (ScorePoller pointed at mock-sportmonks) should never be enabled in production. It is gated behind mock mode configuration.
FixtureSyncJob
Syncs fixture metadata (start times, venues, team assignments) from SportMonks.
What it does
- Fetches the fixture list for the configured season from SportMonks.
- Compares with existing fixture records in DynamoDB.
- Updates any changed fields (start time, venue, status).
- Creates records for newly discovered fixtures.
Polling interval
Runs every 60 seconds.
Tables read/written
| Table | Operation |
|---|
| Matches table | Read/Write (fixture metadata) |
| Leagues table | Read (to map SportMonks league IDs) |
Dependencies
- SportMonks API — fixture list endpoint.
Relevant env vars
| Variable | Purpose |
|---|
SPORTMONKS_API_TOKEN | API token for SportMonks |
SPORTMONKS_SEASON_ID | Season to sync |
FIXTURE_SYNC_INTERVAL_MS | Override the sync interval |
Failure modes
- SportMonks down: Sync skipped. Existing fixture data remains unchanged. Retries on next cycle.
- Partial response: The job processes whatever fixtures are returned. Missing fixtures remain unchanged.