Skip to content

failover: retry the same host when no fallback origin exists, and cover the Cloud API hosts - #723

Open
u9g wants to merge 2 commits into
mainfrom
jason/cloud-api-retry
Open

failover: retry the same host when no fallback origin exists, and cover the Cloud API hosts#723
u9g wants to merge 2 commits into
mainfrom
jason/cloud-api-retry

Conversation

@u9g

@u9g u9g commented Sep 11, 2026

Copy link
Copy Markdown

Why

A request to cloud-api.livekit.io was lost between Cloudflare and the origin on 2026-09-11: the edge acknowledged it, but no LiveKit system ever saw it, and the client waited out its full timeout. Every surrounding call succeeded, so a single retry would have recovered it.

The SDK's failover loop could not help, for two reasons:

  • The attempts policy only matches *.livekit.cloud, so a cloud-api.livekit.io request always got exactly one attempt.
  • Even with failover engaged, the loop gives up as soon as no untried origin exists, which is always the case for cloud-api: it has a single origin and /settings/regions returns 404.

What

Two commits, each test-first:

  1. Retry the same host when a retryable failure has no fallback origin. When pickNext finds no untried origin, a retryable failure (a thrown fetch error or an HTTP 5xx) now re-sends the request to the same host instead of surfacing it. Transport errors and 5xx are treated alike: a transport error can also mean the server executed the request and only the response was lost, so distinguishing them buys no idempotency safety, and cross-region failover already retries 5xx. Every attempt carries the same X-Livekit-Request-Id, so the server can dedup a replay. Max attempts and backoff are unchanged; a 4xx is still terminal.
  2. Enable retries for the LiveKit Cloud API hosts. A new isCloudApi check matches cloud-api.livekit.io and cloud-api.<env>.livekit.io (case-insensitively) and joins isCloud in the attempts policy. A Cloud API host has a single origin, so the retry loop never fetches /settings/regions for it and goes straight to the same-host retry; otherwise every failed attempt would also pay the discovery timeout. The existing .livekit.cloud check is unchanged.

Includes a patch changeset for livekit-server-sdk.

Behavior change

For cloud-api calls with a request timeout at or above MIN_FAILOVER_TIMEOUT_SECONDS (5s, including the 10s default), a lost request or a 5xx now costs up to FAILOVER_MAX_ATTEMPTS (3) attempts with exponential backoff instead of failing after one. A truly dead or persistently erroring origin takes up to three times the per-attempt budget to surface. Requests with a shorter timeout keep getting a single attempt. For .livekit.cloud project hosts, the same-host retry only engages once every discovered region has been tried, so their behavior is unchanged in practice.

Mirrors livekit/server-sdk-go#1002.

Testing

pnpm exec vitest --environment node run in packages/livekit-server-sdk: 8 files, 115 tests passing, including the test/api integration suite against a local livekit/test-server container (LK_TEST_SERVER_URL).

New tests, each confirmed failing before its implementation commit:

  • TwirpRPC.test.ts: "without a fallback origin, a transport error retries the same host", "without a fallback origin, a 5xx retries the same host", and "a Cloud API host retries without consulting region discovery" (asserts zero /settings/regions fetches).
  • failover.test.ts: cloud-api.livekit.io, cloud-api.staging.livekit.io, and CLOUD-API.LIVEKIT.IO get max attempts; cloud-api.example.com gets one.

pnpm lint and prettier --check are clean on the changed files (the 3 existing lint warnings are pre-existing and untouched).

@changeset-bot

changeset-bot Bot commented Sep 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 3f51ac7

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
livekit-server-sdk Patch
agent-dispatch Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Devin Review

@u9g
u9g force-pushed the jason/cloud-api-retry branch 2 times, most recently from 69eff5c to d19c32b Compare September 11, 2026 18:00
u9g added 2 commits September 11, 2026 14:01
A retryable failure with no untried origin left used to be surfaced after
a single attempt. Retry it against the same origin instead, bounded by the
existing attempt count and backoff. This matches the cross-region path,
which already retries both transport errors and 5xx responses.
@u9g u9g changed the title failover: retry a lost request on the same host, and cover the Cloud API hosts failover: retry the same host when no fallback origin exists, and cover the Cloud API hosts Sep 11, 2026
@u9g
u9g force-pushed the jason/cloud-api-retry branch from d19c32b to 3f51ac7 Compare September 11, 2026 18:01
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