Skip to main content
This flow is legacy and scheduled for significant refactoring. The current implementation documented below will change. Refer to this page for understanding the existing behavior, but expect breaking changes.

How it works

PrematchNotificationJob runs on a periodic schedule and looks for matches that are about to start.

The flow

  1. The job queries the fixtures table for matches starting within a configurable window (default: 30 minutes).
  2. For each upcoming match, it queries cricket-match-follows to find all users who have followed that match.
  3. Before sending, it cross-checks the cricket-prematch-notifications table to see if this user has already been notified about this match. This prevents duplicate notifications if the job runs multiple times within the window.
  4. If the user hasn’t been notified, it sends a standard alert push notification via APNs. This is a regular push, not a Live Activity update.
  5. After sending, the notification is recorded in cricket-prematch-notifications so it won’t be sent again.
This is a regular APNs push notification, not a Live Activity push. The Live Activity is only started when the match goes live and the user has the app open (or via push-to-start if supported). The prematch notification is just a heads-up alert.

Edge cases

Match time changes: If a match start time is pushed back, users who were already notified won’t receive a second notification. The deduplication key is (userId, matchId) — not time-based. If you need re-notification on schedule changes, the dedup record would need to be cleared.
Configurable window: The 30-minute default is configurable. For high-profile matches, this could be extended. The window is checked against the fixture’s scheduled start time from SportMonks. No-follow, no-notification: Users only receive prematch notifications for matches they have explicitly followed. There is no broadcast to all users.

Key tables

TablePurpose
fixturesSource of match schedule and start times
cricket-match-followsWhich users follow which matches
cricket-prematch-notificationsDeduplication: tracks which notifications have been sent

Key jobs

JobPurpose
PrematchNotificationJobPeriodic check for upcoming matches, sends alert pushes

Areas of improvement

No invalid-token cleanup on prematch sends. The prematchNotificationJob.ts calls sendPushToStartBatch and sendAlertPushBatch without passing an onInvalidToken callback. The APNs service supports this callback (it detects BadDeviceToken / Unregistered responses), but the prematch job ignores it. This means stale tokens accumulate and APNs receives repeated pushes to dead tokens, which can cause Apple to throttle the app’s push certificate.
Partial-send then crash leaves inconsistent state. In prematchNotificationJob.ts lines 175-209, the job sends push-to-start notifications, then alert notifications, then calls markNotificationSent. If the process crashes after sending push-to-start but before markNotificationSent, the next tick will re-send push-to-start notifications to devices that already received them. The dedup marker is only written after both batches succeed.
Job downtime during the prematch window means missed notifications. The prematch window filter (timeUntilStart >= 0 && timeUntilStart <= PREMATCH_WINDOW_MS) only catches matches whose start time is in the future and within the window. If the job is down for the entire 30-minute window and comes back after startTime, timeUntilStart goes negative and the match is skipped permanently. There is no catch-up mechanism for matches that passed through the window while the job was offline.
Dedup key is per-match, not per-user. The prematchNotificationStore.ts uses matchId as the sole key. Once markNotificationSent is called, all users are considered notified. If a user follows a match after the notification was already sent (but before the match starts), they will never receive a prematch notification. A per-user dedup key (matchId + userId) would allow late followers to be notified on subsequent ticks.
No followers triggers a retry loop with no backoff. When getFollowersForMatch returns an empty array (line 148-150), the job logs “skipping (will retry)” and continues without calling markNotificationSent. This means every subsequent tick (every 60 seconds) will re-query followers for that match until the window expires. For matches nobody follows, this is wasted DynamoDB read capacity for the full 30-minute window.
TTL-based cleanup is generous. The prematchNotificationStore.ts uses a 48-hour TTL. This is fine for correctness but means the table retains records well past their usefulness. Consider aligning the TTL closer to the prematch window (e.g., 2-4 hours) to reduce table size.