Skip to content

fix(contest-sync): add a timeout to Codeforces API requests #281

Description

@coderabbitai

Summary

Follow-up to #280: bound the Codeforces API request used by contest synchronization so a hung HTTP response cannot outlive the MongoDB distributed-lock lease and permit a concurrent sync on another application instance.

Failure chain

  1. startContestSyncJob() acquires the contestSync MongoDB lease with a fixed five-minute TTL.
  2. The job invokes cfGetContestList to fetch Codeforces contests.
  3. If Codeforces accepts the TCP connection but never sends a response, the request has no application-level timeout and can remain pending until the host/OS network timeout (potentially 10–30 minutes).
  4. While the original process is still awaiting that request, the five-minute lease expires.
  5. A different replica sees the lease as expired, successfully acquires it, and starts a second syncCodeforcesContests() execution.
  6. Both processes can then perform the same sync concurrently.

The database upserts are idempotent, so this should not corrupt contest records. However, it defeats the PR #280 guarantee of one synchronization execution per scheduled interval and causes duplicate external API traffic and avoidable database work.

Why isRunning does not prevent this

isRunning is process-local. It blocks a second runSync call only within the instance that is already waiting on the hung request. It is not stored in MongoDB and is not shared with PM2 workers, containers, Kubernetes pods, or other application replicas. Once the distributed lease expires, another process has no visibility into the first process's isRunning state and can legally acquire the lock.

Affected areas

  • server/modules/contests/service.jscfGetContestList / Codeforces HTTP request configuration
  • server/jobs/contestSync.js — the existing fixed five-minute distributed-lock lease relies on sync work being bounded

Required changes

  • Configure an explicit, finite HTTP timeout for the Codeforces request (15 seconds is the initial target proposed during review).
  • Ensure timeout failures reject through the existing sync error-handling path, are logged with actionable context, and do not crash the server.
  • Preserve the existing behavior that releases the owner-scoped lock in finally.
  • Document the timeout choice and ensure it is configurable if project conventions support environment-based HTTP settings.

Acceptance criteria

  • A Codeforces request that does not return within the configured timeout fails predictably rather than waiting for OS-level TCP timeouts.
  • The failed sync is logged and the owner-scoped distributed lock is released through the existing cleanup path.
  • A hung/simulated slow request cannot remain active beyond the five-minute lock lease; the configured timeout must remain safely below that lease.
  • Add or update tests covering the timeout/failure path and verifying that a later sync attempt can proceed.
  • Verify normal successful Codeforces synchronization remains unchanged.

References

Metadata

Metadata

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions