Skip to content

fix(rivetkit-napi): key actor runtime state by generation - #5825

Draft
abcxff wants to merge 1 commit into
release/2.3.23from
stack/fix-rivetkit-napi-key-actor-runtime-state-by-generation-ruyuonlz
Draft

abcxff wants to merge 1 commit into
release/2.3.23from
stack/fix-rivetkit-napi-key-actor-runtime-state-by-generation-ruyuonlz

Conversation

@abcxff

@abcxff abcxff commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

No description provided.

@railway-app

railway-app Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

🚅 Deployed to the rivet-pr-5825 environment in rivet-frontend

Service Status Web Updated
website ❌ Build Failed (View Logs) Web Oct 3, 2026 at 2:05 am UTC
kitchen-sink 😴 Sleeping (View Logs) Web Oct 2, 2026 at 10:19 pm UTC
frontend-cloud 😴 Sleeping (View Logs) Web Oct 2, 2026 at 10:19 pm UTC
frontend-inspector 😴 Sleeping (View Logs) Web Oct 2, 2026 at 10:19 pm UTC
ladle ✅ Success (View Logs) Web Oct 2, 2026 at 10:11 pm UTC
mcp-hub ✅ Success (View Logs) Web Oct 2, 2026 at 10:08 pm UTC

@abcxff

abcxff commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor Author

@claude

claude Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Review

The fix looks correct. Keying ACTOR_CONTEXT_SHARED by (actor_id, generation) stops an older generation's reset_runtime_state and clear from touching the newer generation's JS state. The Rust unit test covers the key behavior directly.

Suggestions

  • Test helpers vs. CLAUDE.md: serverless-lost-generation.test.ts defines a custom waitFor that swallows errors and retries every 250ms. CLAUDE.md says not to paper over races with retry loops, and any polling needs a one-line justification. Use vi.waitFor with an adjacent // comment, or await events directly (for example promises resolved from the hooks instead of the hookEvents array). check:wait-for-comments only checks vi.waitFor, so this helper escapes that check.
  • Prototype patching: observeRuntimeStateClears monkey-patches NapiCoreRuntime.prototype.actorClearRuntimeState. This is close to the module-level mocking CLAUDE.md prohibits. It is restored in finally, but it breaks if tests run concurrently in one process.
  • None generation key: generation() returns None before an envoy is configured, so all such contexts for one actor id share one entry. That matches the old behavior, but a short comment on the ActorContextSharedKey alias would make it explicit.
  • generation() naming: it is a thin alias for sleep_generation(). Consider reusing the existing name, or note that they are intentionally the same.
  • Test weight: The driver test depends on a TCP reset timing a Lost generation, with a 90s timeout, so it could be flaky in CI. The unit test already covers the invariant. Check whether this test belongs in the fast-driver list, and gate or document it if it is slow.
  • Process: The PR body is empty. Add a short bullet list of what changed.

No security or performance concerns. The tuple-key allocation per context construction is negligible.

@the-company-company the-company-company 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.

🟠 1 medium · 🔵 1 low

Reviewed commit 976d08b.

Comment on lines 45 to +47

static ACTOR_CONTEXT_SHARED: LazyLock<SccHashMap<String, Weak<ActorContextShared>>> =
LazyLock::new(SccHashMap::new);
// Keyed by generation so a newer generation of the same actor never shares, resets, or clears the
// JS runtime state of an older generation that is still shutting down on this host.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 Medium · Format the shared-state declaration so Rustfmt passes

The required Rustfmt check fails on this declaration at this head. Apply the formatter’s single-line generic layout (with the initializer on the next line) so the Rust CI gate can pass.

Comment on lines +358 to +360
} finally {
stopObservingClears();
heldSleep = undefined;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Low · Release the held sleep during failure cleanup

If any wait or assertion fails after heldSleep is installed but before releaseOldSleep() runs, assigning undefined here does not resolve the promise already awaited by onSleep. That leaves the old generation’s shutdown task permanently suspended and can leak native/registry work into the rest of the shared-engine suite. Keep the resolver in teardown scope and invoke it in finally before clearing the fixture.

This branch had an error being deployed

1 failed deployment
rivet-frontend / rivet-pr-5825 — dcf7fcbe Deployed Oct 3, 2026 by railway-app[bot]
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