Skip to content

Operations and recovery

Start with the failing boundary: timer, ESPN, private state, public publication, email, or browser fetch. The Function’s logs intentionally avoid printing provider exception bodies and recipient details.

Routine checks

  1. Inspect the check_pool invocation in Function monitoring/Application Insights.
  2. Confirm that the configured season is active and DRY_RUN is false for real runs.
  3. Check whether results.json reflects expected names, dates, and scores.
  4. If a game is eligible but incomplete, inspect the cached schedule and final status.
  5. Review delivery markers alongside ACS records when email is missing.
  6. Review Sentry browser errors in the production environment and match the event release to the deployed commit.

as_of_utc is the last published snapshot timestamp, not an hourly heartbeat. Unchanged public content is intentionally not rewritten. The browser fetches once on load; refresh it to fetch a newly published result.

Troubleshooting by symptom

SymptomFirst checksRelevant behavior
No work on an hourly invocationSeason window, DRY_RUN, configured pool datesOff-season and dry-run exits avoid all external work
No halftime email during the gameESPN final statusBoth periods are notified after the game finishes
Final game missingCached kickoff, NFL week, ESPN status, score parsingFirst check is kickoff + 3 hours; old unresolved games retry daily
Roster change not visibleFunction deployed, active-season invocation, browser reloadPublic content refresh is independent of email completion
Browser says results cannot loadNetwork panel, Blob URL, anonymous read, Blob CORSAzure website hosting and Blob endpoint permissions differ
JSON is current but open page is staleReload the pageThe minute timer changes the featured week without refetching JSON
State fails to loadResults connection string, container access, state versionpool-state must be private and state version must be 1
Write fails due to concurrencyOverlapping execution or manual editsETag conditions reject stale writers
Email failure or delivery requires reviewsending marker and ACS submission recordsAmbiguous submissions are not automatically resent
Function deploy cannot authenticateFederated subject, audience, variable scope, Azure roleThe OIDC credential must match the branch/project exactly
Function deploy succeeds but code does not loadRemote-build logs, Python version, required templatesCLI success alone is not an application smoke check
Docs are missing on Pagesdocs:build, docs:pages, Pages access and URLUse the URL shown by GitLab, including any unique-domain setting

Verify Sentry reporting

After a website deployment, open the public site and run this in the browser developer console to send a deliberate test event:

Sentry.captureException(new Error("Public site Sentry test"));

Confirm the event appears in the configured Sentry project with environment production and the deployed commit SHA as its release. For a local preview, expect development; the checked-in configuration has no release value. Successful configuration checks alone do not verify delivery to Sentry.

If the event is missing, check that sentry-config.js contains a nonempty DSN, the SDK loads from browser.sentry-cdn.com, and the browser’s Network panel shows the event request to the configured Sentry ingest host. Browser extensions or network filtering may block these requests. Check the project’s environment filters as well. An explicitly empty GitLab SENTRY_DSN disables reporting; redeploy after changing deployment variables.

For the site’s results error message, look for operation:load-results in Sentry and inspect the browser’s Blob request and CORS response. Sentry does not monitor the Python Function; use Azure logs for publishing, scoring, and email failures.

Ambiguous email delivery

Each delivery key starts with a configured game date followed by a colon and one of summary, winner:<player-id>, season:halfway, or season:season-end. Season summaries use the last configured game date in their reporting period. The scheduler saves a claim before contacting ACS. Milestone claims use the same recovery procedure, even when every game is already marked completed.

stateDiagram-v2
    [*] --> Unclaimed
    Unclaimed --> Sending: Conditional state write succeeds
    Sending --> Sent: ACS succeeds and state save succeeds
    Sending --> Review: Timeout, process stops, or uncertain response
    Review --> Sent: Operator confirms acceptance
    Review --> Unclaimed: Operator confirms no send and removes only that claim
    Sent --> [*]: Subsequent runs skip delivery

A retained sending value is deliberately blocking. It could mean ACS accepted an email but the worker stopped before recording success. Do not remove all state to force another run.

  1. Stop the Function before manually editing its state.
  2. Back up the private pool-state/<season>.json blob.
  3. Inspect ACS submission/delivery records for the affected attempt.
  4. If accepted, change only the relevant delivery marker to sent.
  5. If definitely not submitted and a retry is wanted, remove only that delivery entry.
  6. Preserve cached results and unrelated delivery, publication, and completion markers.
  7. Restart the Function and monitor the next eligible invocation.

The normal statuses persisted are sending and sent; Review in the diagram is an operator action, not another persisted value. This design reduces duplicate emails but is not a transactional exactly-once guarantee.

State and history

State entryPurpose
versionState format, currently 1
schedule, schedule_refreshedESPN kickoff cache and UTC refresh date
checkedLast UTC-day check for long-delayed games
resultsVerified completed-game scores and winners
snapshot_hashDetects changes to public content
publishedGames whose result publication was recorded
deliveriesWeekly, winner, and season-summary send claims
completedGames whose processing and delivery handling finished

A missing season-state file begins a fresh catch-up workflow. It does not import emails sent by older implementations and can resend prior notifications. Public daily snapshots cannot restore delivery markers. Keep a private backup before any intentional state repair or migration.

Roster or season updates

Use the pool data checklist, run the tests, and deploy the Function. An active-season invocation publishes changed public configuration even if no newly completed game exists. This refresh preserves existing email and completion markers.

For emergency suspension, stop the Function or set DRY_RUN=true; the latter skips the scheduled workflow entirely. Resume only after confirming configuration and state.