feat(proxy): configurable per-app transport-retry interval#300
Open
VioletVenti wants to merge 1 commit into
Open
feat(proxy): configurable per-app transport-retry interval#300VioletVenti wants to merge 1 commit into
VioletVenti wants to merge 1 commit into
Conversation
The local proxy retried upstream connection/timeout errors immediately (hardcoded 0s interval between attempts) with no way to configure it. Add a per-app `retry_interval_seconds` field so the wait between transport retries is tunable. - New `retry_interval_seconds` column on `proxy_config` (INTEGER NOT NULL DEFAULT 0) via schema migration v11 -> v12; default 0 preserves the prior immediate-retry behavior (fully backward compatible). Fresh DBs get the column in CREATE TABLE; the migration guards on table existence and is idempotent. - DAO read/write (per-app SELECT x2, UPDATE, defaults) carries the field. - Forwarder sleeps the configured interval between retries at all four transport-retry continue points (streaming/buffered x connect/timeout); None or Duration::ZERO skips the sleep, so the fast path adds no async overhead. Only the pre-first-byte transport-retry layer is affected. - CLI: `cc-switch proxy config --retry-interval-seconds <N>` (0-300, Claude/Codex/Gemini only), shown per-app in `cc-switch proxy show` under a new "Retry interval" section. - Tests: migration (column + default + data preserved), DAO round-trip, forwarder timing (proves the proxy actually sleeps >= interval on retry, plus a no-interval control), CLI setter persistence across DB reopen. Auth is untouched; mid-stream disconnect retries remain out of scope. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The local proxy retried upstream connection/timeout (transport) errors immediately — the interval between retries was hardcoded to 0s with no field, CLI flag, or
proxy showline to configure it. This PR makes that interval configurable per app.retry_interval_secondsfield onproxy_config(default0= immediate retry).cc-switch proxy config --retry-interval-seconds <N>.cc-switch proxy showunder a new Retry interval section.Motivation
RequestForwarderretries retryable transport errors (connect/timeout) for non-Claude apps (uses_internal_transport_retry), but the retry loop only didattempt += 1; continue;— no sleep between attempts. Under upstream instability, hammering an unresponsive endpoint with zero backoff is rarely what you want, yet there was no knob to tune it. This adds that knob.Changes
Config / DB
proxy_config.retry_interval_secondsINTEGER NOT NULL DEFAULT 0, added toCREATE TABLE(fresh DBs) and via schema migration v11 → v12 (add_column_if_missing, guarded on table existence, idempotent).SCHEMA_VERSION11 → 12.SELECTs, theUPDATE, anddefault_app_proxy_configcarry the field.Runtime (forwarder)
ForwardOptions.retry_interval_seconds: Option<Duration>, threaded fromHandlerContextat the 4 handler sites.maybe_sleep_retry_intervalhelper sleeps the configured interval at all four transport-retrycontinuepoints (streaming × {connect, timeout}, buffered × {connect, timeout}).None/Duration::ZEROskips the sleep entirely, so the existing fast path adds zero async overhead.CLI
cc-switch proxy config --retry-interval-seconds <N>(range 0–300; Claude/Codex/Gemini only). Read-modify-write via the standard per-app DAO path.cc-switch proxy show: new per-app Retry interval section (e.g.Codex: 3s;0rendered as立即/immediate).Backward compatibility
0, preserving the exact prior immediate-retry behavior.0for the new column.CREATE TABLE; the migration is a no-op there.Tests
schema_migration_v11_adds_retry_interval_seconds— migration adds the column (type/default/not-null), preserves pre-existing row data, lands at v12.retry_interval_seconds_round_trips_per_app— DAO write/read, per-app independence.buffered_transport_retry_sleeps_configured_interval_between_attempts— proves the proxy actually sleeps the configured interval between retries (Codex + closed-port connect-refused; asserts wall-clock ≥ interval).buffered_transport_retry_with_zero_interval_does_not_sleep— control:Nonestays fast (no sleep).proxy_config_retry_interval_persists_across_reopen— CLI setter persists across DB reopen (simulates daemon restart); plus an out-of-range rejection test.ForwardOptionstest sites updated mechanically (retry_interval_seconds: None).cargo fmt --check,cargo clippy --all-targets, andcargo testare green. (One unrelated, pre-existing flaky testprovider_service_switch_claude_merges_live_and_statefails on a cleanmaincheckout as well — not introduced here.)Scope notes / out of scope
max_retries).request_timeoutbudget; the CLI caps it at 300s to avoid pathological misconfiguration.🤖 Generated with Claude Code