Skip to content

fix(rivetkit-core): keep sleeping actors reachable during grace#4939

Merged
NathanFlurry merged 1 commit intomainfrom
05-04-fix_rivetkit_sleep_grace_active_actor
May 5, 2026
Merged

fix(rivetkit-core): keep sleeping actors reachable during grace#4939
NathanFlurry merged 1 commit intomainfrom
05-04-fix_rivetkit_sleep_grace_active_actor

Conversation

@NathanFlurry
Copy link
Copy Markdown
Member

Description

Please include a summary of the changes and the related issue. Please also include relevant motivation and context.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

Please describe the tests that you ran to verify your changes.

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Copy link
Copy Markdown
Member Author

NathanFlurry commented May 4, 2026

This stack of pull requests is managed by Graphite. Learn more about stacking.

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 4, 2026

PR #4939 Review: fix(rivetkit-core): keep sleeping actors reachable during grace

Overview

This PR fixes a bug where actors entering the sleep grace period (SleepGrace lifecycle state) were being treated as unreachable, causing dispatched actions and new connections to be rejected. The fix makes actors in sleep grace remain accessible, mirroring how Started actors are handled.


Code Quality

task.rs — transition_to indentation bug

The diff introduces a stray closing brace at 3-tab indent inside the if matches!(lifecycle, LifecycleState::Started) block:

if matches!(lifecycle, LifecycleState::Started) {
    self.ctx.reset_abort_signal_for_start();
    self.ctx.clear_sleep_requested();
    }                                     // closes if (stray brace, misindented at 3 tabs)
    self.ctx.set_started(matches!(...));  // looks like it's inside the if, but isn't
}                                         // closes transition_to

The code is semantically correct (braces balance, rustfmt will fix at merge), but the indentation makes set_started look like it's guarded by the Started check when it's actually unconditional. Worth a quick manual fix before merge for readability.

The semantic intent is correct: reset_abort_signal_for_start and clear_sleep_requested should only fire on a fresh Started transition, not on SleepGrace. set_started(true) should fire on both.

registry/mod.rs — indentation inconsistencies

Several blocks shifted indent by one extra level (e.g. let should_remove in remove_stopping_actor_instance, the let _ = self.starting_instances... block in on_actor_started, the transition_actor_to_stopping method signature). rustfmt will clean these at merge, but they make the diff harder to audit.


Correctness

Core logic — correct

The active_actor change for Stopping { reason: Sleep } is the right fix:

ActorInstanceState::Stopping { instance, reason } => {
    match reason {
        ShutdownKind::Sleep if instance.ctx.started() => return Ok(instance),
        // ...
    }
}

Because set_started now returns true for SleepGrace, this correctly lets work through to an actor still in its grace window.

Active branch — sleep_requested guard removed

The previous code rejected Active+started+sleep_requested actors with Stopping. The new code lets them through. This is correct: an actor that has requested sleep but hasn't yet entered grace should remain accessible.

The comment at context.rs:401 (// into SleepGrace / DestroyGrace, so started=false covers both) is now stale — it describes behavior this PR intentionally changes. Should be removed or updated.

Exhaustive match on ShutdownKind

The new match reason in active_actor explicitly handles ShutdownKind::Sleep (guarded and unguarded) and ShutdownKind::Destroy with no _ => fallthrough — consistent with CLAUDE.md conventions.


Potential Concerns

  1. Race at SleepFinalize transition: When an actor finishes grace and moves to SleepFinalize, set_started(false) is called. A concurrent active_actor call reading ctx.started() could still see true. The AtomicBool with SeqCst ordering should prevent torn reads, but worth confirming the shutdown sequence doesn't admit a double-dispatch window.

  2. New long-lived connections during grace: The un-skipped test "new connections rejected during sleep shutdown" covers this. Confirm the WebSocket admission path also enforces this check independently of active_actor, since a sleeping actor should reject new long-lived connections even while remaining reachable for in-flight actions.


Tests

Un-skipping 4 tests from issue #4705 is correct — this is the proper fix, not a retry workaround. No new test cases for the specific new reachability behavior, but the existing suite covering dispatch and WebSocket behavior during sleep shutdown provides sufficient coverage.


Summary

The fix is logically sound. Two pre-merge asks:

  • Fix the stray } indentation in transition_to (low effort, avoids misleading future readers)
  • Remove/update the stale comment at context.rs:401 that describes the now-changed started=false behavior for SleepGrace

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

Preview packages published to npm

Install with:

npm install rivetkit@pr-4939

All packages published as 0.0.0-pr.4939.2da0d18 with tag pr-4939.

Engine binary is shipped via @rivetkit/engine-cli on linux-x64-musl, linux-arm64-musl, darwin-x64, and darwin-arm64. Windows users should use the release installer or set RIVET_ENGINE_BINARY.

Docker images:

docker pull rivetdev/engine:slim-2da0d18
docker pull rivetdev/engine:full-2da0d18
Individual packages
npm install rivetkit@pr-4939
npm install @rivetkit/react@pr-4939
npm install @rivetkit/rivetkit-napi@pr-4939
npm install @rivetkit/workflow-engine@pr-4939

@NathanFlurry NathanFlurry force-pushed the 05-04-fix_rivetkit_sleep_grace_active_actor branch from 6512e52 to a877360 Compare May 4, 2026 15:36
Base automatically changed from 05-04-test_rivetkit_websocket_close_db to main May 5, 2026 14:57
@NathanFlurry NathanFlurry merged commit a877360 into main May 5, 2026
22 of 28 checks passed
@NathanFlurry NathanFlurry deleted the 05-04-fix_rivetkit_sleep_grace_active_actor branch May 5, 2026 14:58
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