You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The chat diff-status worker polls open pull requests on a fixed 10s interval and re-downloads the full JSON body on every tick, even when nothing changed. The GitHub client (coderd/externalauth/gitprovider/github.go) never sends If-None-Match / ETag, so GitHub always returns a full 200 OK instead of a cheap 304 Not Modified.
Why it matters
304 responses don't count against GitHub's primary rate limit; full 200s do. Every unchanged poll needlessly burns a user's/installation's API quota — multiplied by the number of tracked PRs and the 10s cadence. On busy instances this worker can quietly exhaust the quota that the same token needs for interactive Git and API operations, so users hit rate-limit errors and stalls elsewhere in Coder. Adopting conditional requests fixes that with no behavior change — unchanged PRs revalidate for free, and only real changes transfer a body.
Per refresh (all full 200s today): ResolveBranchPullRequest → GET /repos/{owner}/{repo}/pulls?...; FetchPullRequestStatus → GET /repos/{owner}/{repo}/pulls/{n} and .../pulls/{n}/reviews?per_page=100.
githubProvider.decodeJSON sets Accept, X-GitHub-Api-Version, and a bearer token but no If-None-Match, and keeps no ETag cache. The worker is a long-lived singleton, so an in-process cache persists across ticks.
Proposed fix
Add a small, concurrency-safe, bounded ETag+body cache and wire it into decodeJSON: key on request URL + a hash of the token (never serve one token's response under another; don't retain raw tokens); send If-None-Match when an ETag is cached; on 304 decode the cached body; on 200 cache {etag, body} under a size cap. Bound by entry count and per-body size. Leave the raw-diff path (fetchDiff) out of scope to avoid caching large bodies.
Summary
The chat diff-status worker polls open pull requests on a fixed 10s interval and re-downloads the full JSON body on every tick, even when nothing changed. The GitHub client (
coderd/externalauth/gitprovider/github.go) never sendsIf-None-Match/ ETag, so GitHub always returns a full200 OKinstead of a cheap304 Not Modified.Why it matters
304responses don't count against GitHub's primary rate limit; full200s do. Every unchanged poll needlessly burns a user's/installation's API quota — multiplied by the number of tracked PRs and the 10s cadence. On busy instances this worker can quietly exhaust the quota that the same token needs for interactive Git and API operations, so users hit rate-limit errors and stalls elsewhere in Coder. Adopting conditional requests fixes that with no behavior change — unchanged PRs revalidate for free, and only real changes transfer a body.Where
coderd/x/gitsync/worker.go:defaultInterval = 10 * time.Second,defaultBatchSize = 50.200s today):ResolveBranchPullRequest→GET /repos/{owner}/{repo}/pulls?...;FetchPullRequestStatus→GET /repos/{owner}/{repo}/pulls/{n}and.../pulls/{n}/reviews?per_page=100.githubProvider.decodeJSONsetsAccept,X-GitHub-Api-Version, and a bearer token but noIf-None-Match, and keeps no ETag cache. The worker is a long-lived singleton, so an in-process cache persists across ticks.Proposed fix
Add a small, concurrency-safe, bounded ETag+body cache and wire it into
decodeJSON: key on request URL + a hash of the token (never serve one token's response under another; don't retain raw tokens); sendIf-None-Matchwhen an ETag is cached; on304decode the cached body; on200cache{etag, body}under a size cap. Bound by entry count and per-body size. Leave the raw-diff path (fetchDiff) out of scope to avoid caching large bodies.