Skip to content

fix(combos): shorten request-rate cooldowns and send Retry-After on combo 503 - #3294

Merged
lidge-jun merged 1 commit into
lidge-jun:devfrom
Veritas-7:fix/combo-request-rate-cooldown
Sep 2, 2026
Merged

fix(combos): shorten request-rate cooldowns and send Retry-After on combo 503#3294
lidge-jun merged 1 commit into
lidge-jun:devfrom
Veritas-7:fix/combo-request-rate-cooldown

Conversation

@Veritas-7

@Veritas-7 Veritas-7 commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Summary

When every combo target is in cooldown, the proxy returns HTTP 503 combo_unavailable with no Retry-After. Clients that retry immediately then spend the cooldown window on local failures instead of waiting for a target to thaw.

Request-rate 429s that omit Retry-After currently inherit the same 60s target cooldown as usage-window exhaustion. That is longer than the recovery time for a short request-rate limit.

This change:

  1. Uses a 5s combo-target cooldown for transient request-rate codes (1302, 1305) and the Rate limit reached for requests message, when the upstream response has no Retry-After.
  2. Leaves usage-window codes (1308 and related) and generic 429s on the existing 60s default.
  3. Continues to honor an explicit Retry-After or cooldownMs.
  4. Sets Retry-After on 503 combo_unavailable to the soonest remaining target cooldown.

Quota 429s, auth failures, and explicit backoff headers are unchanged. This is not the first-byte stall hop in #3266.

Test plan

  • bun test tests/combos.test.ts

Review readiness checklist

This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:

  • All CI tests are green on my local testing.

  • I pushed my PR to the latest dev commit.

  • I resolved all correct Codex and CodeRabbit findings.

  • My PR is ready for review.

Summary by CodeRabbit

  • New Features

    • Added short cooldown handling for transient request-rate limits.
    • Combo-unavailable responses now include a Retry-After value when a cooldown remains.
    • Cooldown status and retry timing can be reported to callers.
  • Bug Fixes

    • Prevented transient request-rate limits from using the longer default cooldown.
    • Preserved longer cooldown behavior for usage-limit errors.
    • Explicit retry instructions continue to override automatic cooldown timing.

…ombo 503

Request-rate 429s without Retry-After no longer inherit the 60s quota cooldown.
Combo unavailable responses advertise the soonest remaining target cooldown.
@github-actions github-actions Bot added the bug Something isn't working label Sep 2, 2026
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

⏳ DRAFT

  • review readiness checklist open (0/4 boxes ticked).

What to do

  • Tick all four boxes in the PR description once you're done (currently 0/4).

Review readiness checklist

  • ⬜ All CI tests are green on my local testing.
  • ⬜ I pushed my PR to the latest dev commit.
  • ⬜ I resolved all correct Codex and CodeRabbit findings.
  • ⬜ My PR is ready for review.

0/4 boxes ticked.

Automatic draft conversion failed. Please convert this pull request to a draft manually until every box above is ticked.

@github-actions
github-actions Bot marked this pull request as draft September 2, 2026 18:04
@coderabbitai

coderabbitai Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Team

Run ID: 7b83722b-744a-4b96-9592-a6fa3077d905

📥 Commits

Reviewing files that changed from the base of the PR and between 938c013 and 5de7d92.

📒 Files selected for processing (5)
  • src/combos/failover.ts
  • src/combos/index.ts
  • src/combos/resolve.ts
  • src/server/responses/core.ts
  • tests/combos.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

Changes

The PR adds transient request-rate cooldown handling for combo failover. It propagates failure metadata, computes remaining cooldown time, and includes validated Retry-After headers in combo-unavailable responses.

Combo cooldown handling

Layer / File(s) Summary
Cooldown policy and exported helpers
src/combos/failover.ts, src/combos/index.ts
Lines 16–30 add cooldown constants and provider-code sets. Lines 154–217 add request-rate classification, remaining-cooldown calculations, and short-cooldown selection. Lines 34–40 re-export the new APIs.
Failure metadata propagation
src/combos/resolve.ts, src/server/responses/core.ts
advanceComboAfterFailure accepts status, code, and message fields. The response flow passes these fields to cooldown selection at lines 2561–2563.
Retry-After response integration
src/server/responses/core.ts, tests/combos.test.ts
Lines 1439–1461 validate optional Retry-After values and derive cooldown seconds. Five unavailable-target paths use the helper. Tests cover transient codes, quota codes, explicit overrides, remaining cooldowns, and response headers.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Merge Risk: 🔵 Low · up to 5de7d

The proxy now shortens selected request-rate cooldowns and reports the soonest combo cooldown through Retry-After on 503 responses. It is mergeable with owner awareness that removed targets or concurrent cooldown updates could produce an inaccurate retry delay, causing either premature retries or unnecessary client waiting.

Sequence Diagram(s)

sequenceDiagram
  participant Request as Request handling
  participant Resolve as advanceComboAfterFailure
  participant Failover as coolComboTarget
  participant Response as comboUnavailableResponse

  Request->>Resolve: pass status, code, and message
  Resolve->>Failover: forward failure metadata
  Failover->>Failover: select 5-second or default cooldown
  Request->>Response: build unavailable response
  Response->>Failover: query remaining cooldown
  Failover-->>Response: return Retry-After seconds
  Response-->>Request: return 503 with Retry-After
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 10 functions across 5 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main changes: shorter request-rate cooldowns and Retry-After headers for combo 503 responses.
  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@lidge-jun

Copy link
Copy Markdown
Owner

리뷰 · 우선순위 58 / 80

이 PR은 콤보(여러 타깃을 묶어서 쓰는 길)에서 잠깐 막히는 요청 속도 제한과, 타깃이 전부 식고 있을 때 클라이언트가 어떻게 기다리게 할지 고칩니다. 지금 dev HEAD는 fc08fc2f7 (#3290 GUI 로그 패널 떨림)이고, 바로 앞에는 #3289 responses-state 쓰기 폭풍 완화, #3256 Kiro의 Retry-After 없는 쿨다운 정렬이 있습니다. 이 변경은 그 #3256과 같은 계열입니다. 업스트림이 Retry-After를 안 줄 때 너무 길게 식히거나, 반대로 클라이언트가 503을 받자마자 바로 다시 두드리지 않게 만드는 쪽입니다.

지금 devsrc/combos/failover.ts에서는 coolComboTarget이 Retry-After/cooldownMs가 없으면 무조건 60초(DEFAULT_COOLDOWN_MS)를 씁니다. 그래서 짧은 요청 속도 제한(코드 1302/1305, 또는 "Rate limit reached for requests" 문구)도 사용량 창이 끝난 것처럼 60초를 식힙니다. 그동안 클라이언트는 combo_unavailable 503을 받아도 Retry-After가 없어서, 식기 전에 로컬에서 계속 실패를 쌓을 수 있습니다. 이 PR은 (1) 그 짧은 요청 속도 제한만 5초(COMBO_REQUEST_RATE_COOLDOWN_MS)로 줄이고, (2) 사용량 창 코드(1308 등)·quota 문구는 60초를 유지하며, (3) 명시적 Retry-After/cooldownMs는 그대로 이기고, (4) 503 combo_unavailable에 남은 쿨다운 중 가장 빠른 값을 Retry-After로 붙입니다. src/server/responses/core.tshandleComboResponses / NoAvailableComboTargetsError 경로가 comboUnavailable(comboId)로 바뀌어 comboCooldownRetryAfterSeconds를 씁니다. 테스트는 tests/combos.test.ts에 1302 짧은 쿨다운, 1308 기본 60초, 명시 Retry-After 우선, soonest Retry-After, 503 헤더까지 다섯 케이스가 추가됐습니다. #3266(첫 바이트 지연 홉)은 본문에서 제외했다고 했고, 실제 diff에도 그 경로가 없습니다. types.ts/config.ts 분리 캠페인과도 겹치지 않습니다.

라인 - isTransientRequestRateLimit 입력의 status 필드는 받지만 함수 안에서 한 번도 쓰지 않습니다. coolComboTarget/advanceComboAfterFailure로 전달되는 status도 분류에는 안 들어갑니다. 지금은 코드·문구만으로 판단하므로, 쓰지 않는 인자는 빼거나 쓰게 만드는 편이 읽기 쉽습니다.
경로 src/combos/failover.ts isTransientRequestRateLimit - 코드/문구가 없는 일반 429는 계속 60초입니다. 의도한 보수적 기본값으로 보이지만, 업스트림이 코드 없이 애매한 문구만 주면 짧은 요청 제한인데도 60초로 남을 수 있습니다. 유지할지, 429+요청-rate 휴리스틱을 더 넓힐지는 한 번 결정이 필요합니다.
경로 PR base 938c0136a - 현재 dev tip fc08fc2f7(#3290)보다 #3289·#3290 두 머지 뒤에 있습니다. mergeable이지만 머지 전에 dev로 리베이스하는 게 안전합니다. 겹치는 파일은 거의 없을 가능성이 큽니다.
경로 PR 본문 checklist / draft - 리뷰 준비 체크리스트가 비어 있고 draft 상태입니다. CI·로컬 테스트·CodeRabbit 정리는 작성자 쪽에서 마친 뒤 ready로 올리는 게 맞습니다.
경로 tests/combos.test.tssrc/server/responses/core.ts - 쿨다운 단위 테스트가 comboUnavailableResponse를 core에서 직접 import합니다. 동작은 맞지만 core는 무거운 모듈이라, 헤더만 검증하려면 failover/헬퍼 쪽으로 얇게 빼는 편이 결합도가 낮습니다. 급하지는 않습니다.

메인테이너의 판단이 필요한 지점

  • 코드/문구가 없는 일반 429를 60초에 남겨 둘지, 요청-rate로 더 넓게 볼지
  • draft 체크리스트를 채운 뒤에만 머지할지, 테스트가 이미 있으면 바로 ready로 끌어올릴지
  • fix(oauth): honor Kiro reset-aligned cooldown without Retry-After #3256(Kiro)과 쿨다운 상수·정책을 나중에 한곳으로 모을지, 콤보 전용 5초를 그대로 둘지

너의 추천
draft 체크리스트를 채우고 dev(fc08fc2)로 리베이스한 뒤, 쓰지 않는 status 인자만 정리하면 머지해도 됩니다. #3266과는 겹치지 않으니 같이 묶지 마세요. types/config 분리와 무관하니 close-don't-rebase 대상이 아닙니다.

이 댓글은 grok-bot이 작성했습니다

@lidge-jun
lidge-jun marked this pull request as ready for review September 2, 2026 18:34
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 2, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-02T18:44:30.597995Z 5de7d92 Draft marked ready
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions
github-actions Bot marked this pull request as draft September 2, 2026 18:35

@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: 5de7d9227b

ℹ️ 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 src/combos/failover.ts
targetCooldowns.delete(key);
continue;
}
if (soonest === undefined || remaining < soonest) soonest = remaining;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Calculate Retry-After from request-eligible targets

remainingComboCooldownMs takes the minimum across every stored cooldown for the combo, even though handleComboResponses may exclude targets through payloadEligible and pickComboTarget also excludes disabled providers. For example, if an encrypted worker request can use only a native target cooled for 60 seconds while an ineligible non-native target is cooled for 5 seconds, the resulting 503 advertises Retry-After: 5; retrying then produces another local 503 because the only usable target remains cooled. Pass the current eligibility predicate/configured target set into this calculation so the header reflects the earliest target that could actually serve the request.

Useful? React with 👍 / 👎.

Comment thread src/combos/failover.ts
Comment on lines +213 to +217
?? (isTransientRequestRateLimit({
status: options?.status,
code: options?.code,
message: options?.message,
}) ? COMBO_REQUEST_RATE_COOLDOWN_MS : DEFAULT_COOLDOWN_MS);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Update the documented combo cooldown contract

This branch changes request-rate failures from the documented 60-second default to 5 seconds, and the response path now exposes a Retry-After header on cooldown-backed combo_unavailable responses, but docs-site/src/content/docs/guides/combos.md still states that hopped targets use a 60-second default and documents only the 503 body; translated guides repeat that contract. Update the English guide/reference and synchronize the localized pages so users do not rely on obsolete cooldown and response-header behavior.

AGENTS.md reference: AGENTS.md:L343-L344

Useful? React with 👍 / 👎.

@lidge-jun
lidge-jun marked this pull request as ready for review September 2, 2026 18:40
@lidge-jun
lidge-jun merged commit 6b2dfde into lidge-jun:dev Sep 2, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants