Skip to content

fix(client-proxy): serve cached route during a brief Redis flap - #3789

Open
AdaAibaby wants to merge 2 commits into
e2b-dev:mainfrom
AdaAibaby:fix/client-proxy-route-cache-redis-flap
Open

AdaAibaby wants to merge 2 commits into
e2b-dev:mainfrom
AdaAibaby:fix/client-proxy-route-cache-redis-flap

Conversation

@AdaAibaby

@AdaAibaby AdaAibaby commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

What

client-proxy resolves every request's route from the Redis catalog (catalogResolution → GetSandbox) with a 1s timeout and no fallback. While Redis is briefly unreachable, requests to already-running sandboxes fail even though the VM and its routing record (TTL = full lifetime) are fine.

This adds a small in-memory route cache that is consulted only when the backing catalog returns a transient error, so a short Redis blip no longer breaks routing for running sandboxes. Refs #3788.

How

CachingSandboxCatalog decorates any SandboxesCatalog:

  • success → refresh the entry, return fresh;
  • transient error (Redis unreachable) and the caller's context is still live → serve the last known route if within maxStale (default 60s), else return the error;
  • caller context already cancelled → return the error untouched, nothing served (the request is gone anyway);
  • ErrSandboxNotFound → never served from cache (a stale route would mis-route to a dead worker); evicts the entry;
  • DeleteSandbox → evicts; cache is size-capped (50k) with oldest-first eviction.

Routing records only change on resume/worker-move, so a ≤60s stale route during a flap is a good trade against failing live traffic, and correctness is bounded: no stale not-found, bounded staleness, and a Redis error is already distinct from redis.Nil.

Fallback use is counted (clientproxy.catalog.cache.fallback.total, result=hit when a fresh entry is served, result=stale when nothing fresh remains and the error stands) so the cache's effect — and a flap that outlives the window — is visible on dashboards rather than silent. Wired in client-proxy/main.go; a nil meter is a no-op.

Scope

This is a read-side safety net, deliberately minimal: no new steady-state load (the cache only does work on an error path), no behavior change on the happy path, and nothing resurrected after delete/not-found. It does not touch the write side.

Test

catalog_cache_test.go: serves cached route on transient error; never serves not-found from cache (and evicts); propagates error once stale beyond the window; evicts on delete; returns the error without serving cache when the caller context is cancelled. go test -race ./packages/shared/pkg/sandbox-catalog/ passes (the cache tests; the Redis-backed tests need a live instance); client-proxy builds.

The proxy reads the Redis catalog on every request with no fallback, so a brief Redis blip 5xx-ed all traffic to already-running sandboxes even though the VM and its routing record were fine. Add CachingSandboxCatalog: an in-memory short-TTL (default 60s) route cache consulted only on a transient error; a fresh success refreshes it, a definitive ErrSandboxNotFound is never served from cache and evicts, DeleteSandbox evicts. Wire it in client-proxy. Adds unit tests.

Refs e2b-dev#3788
… hits/stale

- GetSandbox fallback now returns the backing error untouched when the
  caller context is done, instead of serving a route for a request that
  is already gone.
- Count fallback use (result=hit when a fresh entry is served, stale when
  nothing fresh remains and the error stands) via a new
  clientproxy.catalog.cache.fallback.total counter, so a flap outliving
  the window is visible rather than silent.
- Wire the client-proxy meter into the cache; nil meter stays a no-op.
- Tests for the caller-cancelled path; existing cache tests pass under -race.
@cla-bot

cla-bot Bot commented Nov 3, 2026

Copy link
Copy Markdown

Thank you for your pull request and welcome to our community. We could not parse the GitHub identity of the following contributors: AdaAibaby.
This is most likely caused by a git client misconfiguration; please make sure to:

  1. check if your git client is configured with an email to sign commits git config --list | grep email
  2. If not, set it up using git config --global user.email email@example.com
  3. Make sure that the git commit email is configured in your GitHub account settings, see https://github.com/settings/emails

@cla-bot cla-bot Bot removed the cla-signed label Nov 3, 2026

This branch has not been deployed

No deployments
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