Skip to content

Conversation

@NathanFlurry
Copy link
Member

No description provided.

@vercel
Copy link

vercel bot commented Nov 9, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
rivetkit-serverless Ready Ready Preview Comment Nov 9, 2025 5:08am
3 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
rivet-cloud Ignored Ignored Nov 9, 2025 5:08am
rivet-inspector Ignored Ignored Preview Nov 9, 2025 5:08am
rivet-site Ignored Ignored Preview Nov 9, 2025 5:08am

Copy link
Member Author

NathanFlurry commented Nov 9, 2025

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more


How to use the Graphite Merge Queue

Add the label merge-queue to this PR to add it to the merge queue.

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

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

@claude
Copy link

claude bot commented Nov 9, 2025

PR Review: Split ActorInstance logic into multiple classes

Summary

This is a well-executed refactoring that improves code organization by splitting the monolithic ActorInstance class (~2200 lines) into focused, single-responsibility manager classes. The changes enhance maintainability while preserving existing functionality.

Positive Aspects ✅

1. Excellent Code Organization

  • Clear separation of concerns with dedicated managers:
    • ConnectionManager - connection lifecycle and hibernation
    • StateManager - state persistence and change tracking
    • EventManager - event broadcasting and handling
    • ScheduleManager - scheduled task management
  • Each manager has well-defined responsibilities and encapsulation

2. API Improvements

  • Changed from underscored private-style methods (_onAlarm, _broadcast, _sendMessage) to proper public methods (onAlarm, broadcast, sendMessage)
  • Better use of TypeScript symbols for internal state (CONN_PERSIST_SYMBOL, ACTOR_INSTANCE_PERSIST_SYMBOL)
  • More intuitive driver interface with factory functions (createWebSocketSocket, createHttpSocket)

3. Improved Driver Architecture

  • Simplified driver interface by moving driver-specific logic into factory functions
  • Eliminated discriminated union pattern (ConnDriverState) in favor of closure-based approach
  • Better encapsulation with drivers holding their own state

4. File Organization

  • Logical directory structure (actor/conn/, actor/contexts/, actor/instance/)
  • Clear module boundaries with appropriate re-exports

Issues & Recommendations ⚠️

1. Missing Error Handling in WebSocket Driver 🔴

Location: rivetkit-typescript/packages/rivetkit/src/actor/conn/drivers/websocket.ts:92-94

terminate: () => {
    (websocket as any).terminate();
},

Issue: The terminate() method uses type assertion to call a method that may not exist on WSContext.

Recommendation: Add proper type checking or error handling:

terminate: () => {
    if ('terminate' in websocket && typeof websocket.terminate === 'function') {
        (websocket as any).terminate();
    } else {
        actor.rLog.warn('WebSocket does not support terminate(), using close instead');
        websocket.close(1011, 'Connection terminated');
    }
},

2. Potential Memory Leak in Close Promises 🟡

Location: rivetkit-typescript/packages/rivetkit/src/actor/router-endpoints.ts:142

const closePromiseResolvers = promiseWithResolvers<void>();

Issue: If closePromiseResolvers.resolve() is never called (e.g., due to errors in WebSocket lifecycle), the promise will hang indefinitely. The disconnect method awaits this promise.

Recommendation: Add timeout handling:

disconnect: async (actor, conn, reason) => {
    websocket.close(1000, reason);
    
    // Wait for close with timeout
    const timeoutMs = 5000;
    await Promise.race([
        closePromise,
        new Promise((_, reject) => 
            setTimeout(() => reject(new Error('Close timeout')), timeoutMs)
        )
    ]).catch(err => {
        actor.rLog.warn({ msg: 'websocket close timeout', error: err });
    });
},

3. Inconsistent Type Annotations 🟡

Location: Multiple files in actor/instance/

Several manager constructors use any for config parameters:

// state-manager.ts:48
#config: any; // ActorConfig type

// connection-manager.ts:99
eventManager: any, // EventManager type

Recommendation: Use proper generic types or define specific config interfaces to maintain type safety throughout the refactoring.

4. Deleted File May Have Active Usage 🟡

Location: rivetkit-typescript/packages/rivetkit/src/actor/unstable-react.ts

This file was completely removed (110 lines). Ensure there are no external consumers of this unstable API before merging.

Recommendation: Verify with a grep for any imports:

grep -r "unstable-react" rivetkit-typescript/

5. Symbol Export Strategy 🟢

Location: actor/conn/mod.ts:25-26

export const CONN_PERSIST_SYMBOL = Symbol("persist");
export const CONN_DRIVER_SYMBOL = Symbol("driver");

Minor concern: Exporting symbols makes them accessible but doesn't truly enforce privacy since symbols are unique but discoverable.

Recommendation: Document in comments that these are internal-only and should not be used by consumers.

Security Considerations 🔒

1. Type Assertions on User Input

The WebSocket message handling uses type assertions that could be exploited if validation is insufficient upstream. Ensure all message deserialization is properly validated.

2. Closure-based State

The new driver pattern uses closures to hold state (encoding, websocket reference, etc.). This is secure as long as the factory functions are only called by trusted code.

Performance Considerations ⚡

Positive:

  • Manager delegation should have minimal overhead
  • Closure-based drivers avoid repeated state lookups

Neutral:

  • Additional indirection through managers (acceptable tradeoff for maintainability)

Testing Coverage 📋

Concern: Only one test file was modified (tests/actor-types.test.ts). Such a large refactoring should include:

  • Unit tests for each new manager class
  • Integration tests verifying manager interactions
  • Tests for edge cases in connection lifecycle
  • Tests for the new driver factory pattern

Recommendation: Add comprehensive test coverage before merging.

Migration Impact 🔄

Breaking Changes:

  • Method renames (_onAlarmonAlarm, etc.) may affect internal consumers
  • Symbol changes may affect code using __persist and __socket
  • File reorganization requires import updates

Recommendation: Document migration path for any internal consumers.

Final Verdict

Overall Assessment:Approve with minor changes

This is a solid refactoring that significantly improves code organization and maintainability. The architectural changes are sound, and the code quality is high. The identified issues are minor and can be addressed before or shortly after merge.

Priority Fixes:

  1. Fix WebSocket terminate() error handling
  2. Add timeout to WebSocket close promise
  3. Replace any types with proper generics
  4. Verify no external consumers of deleted unstable-react.ts
  5. Add test coverage for new manager classes

Nice to Have:

  • Document symbol export strategy
  • Add migration guide for breaking changes

Great work on improving the codebase architecture! 🎉

@pkg-pr-new
Copy link

pkg-pr-new bot commented Nov 9, 2025

More templates

@rivetkit/actor

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/actor@3421

@rivetkit/cloudflare-workers

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/cloudflare-workers@3421

@rivetkit/core

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/core@3421

@rivetkit/db

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/db@3421

@rivetkit/framework-base

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/framework-base@3421

@rivetkit/next-js

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/next-js@3421

@rivetkit/react

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/react@3421

rivetkit

pnpm add https://pkg.pr.new/rivet-dev/rivet/rivetkit@3421

@rivetkit/sql-loader

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/sql-loader@3421

@rivetkit/engine-runner

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/engine-runner@3421

@rivetkit/engine-runner-protocol

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/engine-runner-protocol@3421

commit: 47cb55f

@NathanFlurry NathanFlurry changed the base branch from 11-08-chore_rivetkit_move_conns_to_separate_persisted_kv_keys to graphite-base/3421 November 9, 2025 05:44
@NathanFlurry NathanFlurry changed the base branch from graphite-base/3421 to 11-08-chore_rivetkit_move_conns_to_separate_persisted_kv_keys November 9, 2025 23:02
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.

2 participants