You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This PR hardcodes serverless_cache_envoy: false (previously true) at three call sites — envoy_callbacks.rs::ServeConfig::from_env and both serve_config_from_js(...) calls in rivetkit-napi/src/registry.rs — with a TODO citing a need to verify the serverless token per-connection before caching can be safely re-enabled.
I traced the change through serverless.rs and the NAPI bridge. There are two real regressions worth addressing before this lands, plus one no-op hunk.
1. health() becomes permanently unhealthy for serverless registries
ensure_envoy() in rivetkit-rust/packages/rivetkit-core/src/serverless.rs:499-505 only writes into self.envoy when cache_envoy is true:
```rust
async fn ensure_envoy(&self, headers: &StartHeaders) -> Result {
if self.shutting_down.load(Ordering::Acquire) {
return Err(RuntimeShutDown.build());
}
if !self.settings.cache_envoy {
return self.start_envoy(headers).await; // never written into self.envoy
}
...
```
With cache_envoy now false everywhere, self.envoy (TokioMutex<Option<EnvoyHandle>>) is never populated, so active_envoy_status() (serverless.rs:241-247) always returns None. The NAPI-exposed #[napi] pub async fn health(&self) (rivetkit-typescript/packages/rivetkit-napi/src/registry.rs:390-401) handles that as:
None unconditionally maps to a 503. This is the method the TypeScript side actually calls (rivetkit-typescript/packages/rivetkit/src/registry/napi-runtime.ts:276: asNativeRegistry(registry).health()), so any serverless-mode host's /health route now returns 503 permanently, even while actors are starting and serving traffic fine. A platform doing liveness/readiness probing on that endpoint (Cloud Run, Fly.io, k8s) would restart-loop a healthy container.
(There's a second, separate /health handler inside serverless.rs::handle_request_inner for raw-HTTP hosts that treats a None envoy as healthy via unwrap_or(true) — that one degrades more gracefully by just losing the ping-staleness signal rather than going permanently 503. Worth noting the two /health surfaces already diverge in "no envoy yet" semantics, independent of this PR.)
2. shutdown() becomes a no-op — loses graceful drain
(serverless.rs:219-233) Since self.envoy is never populated when cache_envoy is false, shutdown() always takes None and returns immediately. Per-request envoy handles are instead built fresh in start_response (serverless.rs:371-419) and live only inside a fire-and-forget RuntimeSpawner::spawn(...) whose JoinHandle is discarded — nothing tracks them in a way shutdown() could wait on. On a deploy/rolling-restart that calls CoreRegistry::shutdown(), in-flight /start SSE streams (and the actor-start requests behind them) no longer get a bounded graceful drain via SHUTDOWN_DRAIN_TIMEOUT; they're just dropped when the process exits. That contradicts the doc comment on shutdown() and the "Run modes" guarantee in rivetkit-core/CLAUDE.md.
3. No-op edit in serve() (rivetkit-napi/src/registry.rs:200-205)
The same TODO/false edit was applied inside CoreRegistry::serve() (persistent, non-serverless mode):
```rust
let serve_config = serve_config_from_js(config, false, false);
```
serve() calls registry.serve_with_config_and_handle_observer(...), which never reads ServeConfig.serverless_cache_envoy — only CoreServerlessRuntime::new (reached via into_serverless_runtime) consumes that field. This hunk has no runtime effect, but the added TODO comment implies envoy caching behavior changed for persistent mode too, which could mislead a future reader. Consider dropping this hunk.
Suggestion
Rather than disabling caching wholesale, the actual gap described in the TODO could be closed directly: ensure_envoy's cache-hit branch (serverless.rs:507-517) checks endpoint/namespace/pool_name on reuse but explicitly skips validating headers.token against the cached handle (per the existing comment there: "the start request token ... may differ from the token used for the engine connection"). Adding a token check on cache-hit would close the real gap while keeping the health-check and bounded-shutdown-drain guarantees intact, instead of trading them away for a stopgap.
Test coverage
Existing serverless.rs tests still construct fixtures with cache_envoy: true and don't appear to cover the cache_envoy: false interaction with shutdown() or health(). If this flip lands even as a temporary stopgap, it'd be worth adding a regression test asserting shutdown() still bounds on SHUTDOWN_DRAIN_TIMEOUT while a /start request is in flight with cache_envoy: false, and one confirming the intended/accepted health() behavior in that mode (since as written it's a silent, permanent regression rather than a documented tradeoff).
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
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.
No description provided.