Skip to content

Cut GitHub REST quota burn: ETag conditional GETs + throttled waiter pumps#18

Merged
kristofferR merged 3 commits into
mainfrom
feat/etag-conditional-requests
Jul 4, 2026
Merged

Cut GitHub REST quota burn: ETag conditional GETs + throttled waiter pumps#18
kristofferR merged 3 commits into
mainfrom
feat/etag-conditional-requests

Conversation

@kristofferR

@kristofferR kristofferR commented Jul 4, 2026

Copy link
Copy Markdown
Owner

Problem

With ~7 concurrent crq loop waiters plus the autoreview daemon running, steady-state polling burned a measured ~7,300 REST requests/hour — 1.45× GitHub's 5,000/hr per-account limit — so the account spent part of every hour rate-limited, starving every other tool on the token (gh CLI included). Each 15s wait tick cost ~4 uncached calls: Feedback() fetches, a full Pump(), and a store.Load() (GET /git/ref + GET /git/blobs).

Fixes

ETag conditional requests (github.go): every 200 GET response is cached under its ETag (per-process, bounded at 1024 entries / 1 MB per body) and repeat requests send If-None-Match. GitHub serves 304 Not Modified without charging quota, so polling unchanged PRs, comments, reviews, and the crq-state ref/blobs is free between actual changes. 304 replays preserve headers, so Link-based pagination stitches pages exactly as before — covered by a dedicated requestPaged test. Blob GETs are content-addressed, so those revalidations hit every time.

Throttled waiter pumps (feedback.go): feedback waiters no longer Pump() on every poll tick — each waiter pumps at most once a minute (or once per poll when polling slower than that). The autoreview daemon already pumps every pass, so per-tick pumping from N concurrent waiters was pure redundancy.

Impact

Steady-state quota cost of a waiting loop drops to near zero (revalidations are free; only actual changes pay). Even before considering ETags, pump throttling alone removes the largest per-tick write-path cost. The 7-loop scenario that burned 146% of quota now fits comfortably with room for interactive gh use.

Testing

  • New: conditional-GET replay on 304, cache refresh on content change, paginated fetch through the cache, pump cadence clamp
  • go vet ./... and full go test ./... pass

https://claude.ai/code/session_015TSx8Erp1Q2yB6snkYPbAh

Summary by CodeRabbit

  • New Features
    • Improved background polling to pump work less often while waiting for feedback, reducing unnecessary activity.
    • Added conditional request caching so unchanged content can be reused when nothing has changed (including paginated results).
  • Bug Fixes
    • Reduced redundant reloads of unchanged paged data by revalidating via ETags and replaying cached responses on “not modified”.
    • Avoid caching very large responses to prevent excessive resource usage.

…pumps

With ~7 concurrent 'crq loop' waiters plus the autoreview daemon, steady-state
polling burned ~7300 REST requests/hour — 1.45x GitHub's 5000/hr account limit —
so the account spent part of every window rate-limited.

Two fixes:

- The GitHub client now caches every 200 GET response under its ETag and sends
  If-None-Match on repeat requests. GitHub serves 304 Not Modified for free
  (no quota charge), so polling unchanged PRs, comments, reviews, and the
  crq-state ref/blobs costs nothing between actual changes. The cache is
  per-process, bounded (1024 entries, 1 MB/body), and 304 replays preserve
  headers so Link-based pagination stitches pages exactly as before.

- Feedback waiters no longer Pump() on every poll tick. Each waiter now pumps
  at most once a minute (or once per poll when polling slower than that);
  the autoreview daemon already pumps every pass, so per-tick pumping from N
  concurrent waiters was pure redundancy.

Claude-Session: https://claude.ai/code/session_015TSx8Erp1Q2yB6snkYPbAh
@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6c6d8916-ac2e-425c-b0ee-5164eccf0507

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds throttled queue pumping in Service.Loop and an in-process ETag cache in GitHub.send for conditional GETs, including cache replay on 304 Not Modified, cache refresh on new 200 OK responses, and tests covering paging and oversized bodies.

Changes

Pump Throttling

Layer / File(s) Summary
Pump timing control
internal/crq/feedback.go, internal/crq/feedback_test.go
Service.Loop now tracks lastPump, derives pumpEvery, and uses pumpEveryFor to limit pumping frequency; the test checks clamping for fast and slow poll intervals.

ETag Caching for Conditional GETs

Layer / File(s) Summary
Cache data and helpers
internal/crq/github.go
GitHub adds mutex-protected ETag cache state plus bounded lookup, store, replay, and body-capping helpers.
Conditional GET handling
internal/crq/github.go
GitHub.send looks up cached entries, sends If-None-Match, replays cached responses on 304, and caches eligible 200 responses.
Cache behavior tests
internal/crq/github_test.go
New tests cover cached body replay, cache refresh, oversized bodies, and paginated revalidation through cached page responses.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant GitHub.send
  participant etags map
  participant GitHub API
  Caller->>GitHub.send: GET request
  GitHub.send->>etags map: lookup cached entry
  GitHub.send->>GitHub API: request with If-None-Match
  GitHub API-->>GitHub.send: 304 Not Modified or 200 OK
  GitHub.send->>etags map: replay cached body or store new response
  GitHub.send-->>Caller: response
Loading

Possibly related PRs

Poem

A rabbit taps the queue with care,
Then naps between each pump-aware.
ETags wink: “I’ve not changed yet,”
Cached pages hop without regret,
And carrots hum through cloud and air.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the two main changes: ETag conditional GETs and throttled waiter pumps to reduce GitHub REST usage.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/etag-conditional-requests

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ab2a967719

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/crq/github.go Outdated
@kristofferR

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@kristofferR

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/crq/github.go`:
- Around line 556-564: The cache/replay path in `github.go` is discarding return
values from `io.Copy(io.Discard, resp.Body)` and the `resp.Body.Close()` call in
`cacheGET`, which triggers `errcheck`. Update both spots to explicitly ignore
the errors by assigning the results to `_` so the intent is clear, and keep the
behavior in the conditional response handling and `cacheGET` unchanged.
- Around line 108-118: The cacheGET helper in GitHub is truncating oversized
responses by reading only maxETagBody+1 bytes and then replacing resp.Body with
that partial buffer, so large CRQ-state blobs get corrupted instead of just
bypassing the cache. Update cacheGET to fully consume the response body, then
decide whether to store it in etagStore based on the full length and ETag; if it
exceeds maxETagBody, return the original full body to the caller without
caching. Keep the fix localized to cacheGET and its ETag/body handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 24e7ba48-4745-414e-8008-794a8a012cc4

📥 Commits

Reviewing files that changed from the base of the PR and between 50fc61d and ab2a967.

📒 Files selected for processing (4)
  • internal/crq/feedback.go
  • internal/crq/feedback_test.go
  • internal/crq/github.go
  • internal/crq/github_test.go
📜 Review details
🧰 Additional context used
🪛 golangci-lint (2.12.2)
internal/crq/github.go

[error] 110-110: Error return value of resp.Body.Close is not checked

(errcheck)


[error] 557-557: Error return value of io.Copy is not checked

(errcheck)

🔇 Additional comments (3)
internal/crq/feedback.go (1)

264-267: LGTM!

Also applies to: 301-309, 510-519

internal/crq/feedback_test.go (1)

665-672: LGTM!

internal/crq/github_test.go (1)

344-472: LGTM!

Comment thread internal/crq/github.go
Comment thread internal/crq/github.go
@kristofferR

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@kristofferR

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2f9b68106c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/crq/github.go Outdated
@kristofferR

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@kristofferR
kristofferR merged commit c5abdd7 into main Jul 4, 2026
1 check passed
@kristofferR
kristofferR deleted the feat/etag-conditional-requests branch July 4, 2026 21:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant