Skip to content

GitHub sync has no rate-limit awareness or backoff against Octokit's REST API, risking hard failures during large org syncs #24

Description

@chonilius

Background

GithubSyncService in src/github/github-sync.service.ts constructs a
plain Octokit client with no rate-limit plugin, no retry/backoff
configuration, and syncIssues paginates through every issue for a repo
via the standard Octokit pagination helper — for a repo with thousands of
issues, this can be dozens of sequential API calls, and syncRepository
further calls syncIssues serially with no concurrency control if called
across many repos (check src/github/github.controller.ts/wherever sync is
triggered for whether multi-repo sync is parallelized).

Problem Statement

GitHub's REST API enforces both primary rate limits (5000 req/hr for
authenticated PAT requests, lower for unauthenticated) and secondary/abuse
rate limits (aggressive short-window limits on rapid sequential requests,
especially for search and content-heavy endpoints). Investigate and
demonstrate (with realistic reasoning, not just "add retries") the following
failure modes in the current sync implementation:

  • A single large-org bulk sync (many repos, each with many issues) can
    exhaust the primary rate limit mid-sync with no visible handling —
    confirm what actually happens today: does the pagination helper throw on
    a 429/403 rate-limit response, and does syncIssues/syncRepository
    propagate that as an unhandled error, potentially leaving a repo
    half-synced (some issues updated, the rest missing) with no resumption
    mechanism?
  • The per-issue upsert (find then merge then save) has no protection
    against a concurrent webhook-driven update landing between the sync's
    read and its write, potentially clobbering a more recent local edit made
    via a webhook that arrived mid-sync (the merge-then-save pattern is not
    optimistic-concurrency-aware).
  • No jittered backoff/retry on transient 5xx errors from GitHub's API.

Requirements

  • Add Octokit's official rate-limit and retry plugins (the retry and
    throttling plugin packages) configured with sensible backoff and a hard
    ceiling to avoid infinite retry loops.
  • Make syncRepository/syncIssues resumable: track sync progress (e.g. a
    last-synced-page/cursor or simply rely on idempotent per-issue upserts
    plus a documented "safe to re-run" contract) so a rate-limit-interrupted
    sync can be safely re-triggered without duplicating work or leaving
    permanent gaps.
  • Guard against the webhook-vs-sync race described above: add an optimistic
    check (e.g. compare an updated-at timestamp from GitHub against the
    locally stored value before overwriting) so a sync doesn't clobber a
    webhook-driven update that's newer than the data being synced.
  • Add structured logging of remaining rate-limit budget before/after large
    sync operations.
  • Tests simulating a 429 mid-pagination and asserting the sync backs off and
    eventually completes (or fails gracefully with a clear, resumable error)
    rather than silently leaving partial data.

Acceptance Criteria

  • Rate-limit/retry plugins are configured with justified backoff
    parameters.
  • A simulated 429 mid-sync (via a mocked Octokit) results in either a
    successful eventual completion or a clearly reported, resumable
    failure — never a silent partial sync with no indication.
  • Webhook updates arriving during an in-progress sync are not clobbered
    by stale sync data (test proves the ordering-independence).
  • Rate-limit budget is logged around large sync operations.

Technical Notes

Files: src/github/github-sync.service.ts, src/github/github.controller.ts,
src/github/github-webhooks.service.ts (for the race with webhook updates).

Difficulty Justification

Requires real familiarity with GitHub's REST API rate-limit semantics and
Octokit's plugin ecosystem, plus solving a genuine ordering/race problem
between two independent data-update paths (bulk sync vs. webhook) that can
run concurrently against the same rows.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial Campaign | FWC26Campaign: Official Campaign | FWC26architectureArchitecture/design issueenhancementNew feature or requestperformancePerformance/optimization issuevery hardVery difficult task, expert-level effort required

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions