-
Notifications
You must be signed in to change notification settings - Fork 131
feat(rivetkit): add ability to destroy actors #3458
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(rivetkit): add ability to destroy actors #3458
Conversation
|
Deployment failed with the following error: Learn More: https://vercel.com/docs/limits#rate-limits |
PR Review: Add Ability to Destroy ActorsSummaryThis PR adds actor destruction functionality to RivetKit, allowing actors to be properly destroyed and resurrected with generation tracking. The implementation is comprehensive and well-tested, but there are several areas that need attention. Critical Issues1. Race Condition in Destroy Logic (High Priority)Location: The startDestroy(actorId: string): void {
const handler = this.#globalState.actors.get(doId);
// Actor not loaded, nothing to destroy
if (!handler || !handler.actor) {
return; // ⚠️ Actor exists in storage but isn't loaded - silent failure
}
// Check if already destroying
if (handler.destroying) {
return;
}
handler.destroying = true;
// ...
}Problem: If an actor exists in Durable Object storage but hasn't been loaded into memory yet, the destroy operation silently fails. This could lead to:
Recommendation: Either:
2. Missing Error Handling in Background KV Deletion (Medium Priority)Location: doState.ctx.waitUntil(
env.ACTOR_KV.delete(GLOBAL_KV_KEYS.actorMetadata(actorId)),
);Problem: The background KV deletion has no error handling. If this fails:
Recommendation: Add error handling and logging: doState.ctx.waitUntil(
env.ACTOR_KV.delete(GLOBAL_KV_KEYS.actorMetadata(actorId))
.catch(err => {
this.#log.error({
msg: "failed to delete actor metadata from KV",
actorId,
error: stringifyError(err),
});
})
);3. Generation Mismatch Error Handling (Medium Priority)Location: if (generation !== expectedGeneration) {
throw new Error(
`Actor ${actorId} generation mismatch - expected ${expectedGeneration}, got ${generation}`,
);
}Problem: This throws a generic
Recommendation: Create a specific error type: export class ActorGenerationMismatch extends ActorError {
constructor(actorId: string, expected: number, actual: number) {
super(
"actor",
"generation_mismatch",
`Actor ${actorId} generation mismatch - expected ${expected}, got ${actual}`,
{ public: true, metadata: { actorId, expected, actual } }
);
}
}Code Quality Issues4. Inconsistent SQL Schema Comments (Low Priority)Location: The SQL table creation comments are helpful, but the schema definition could be clearer: CREATE TABLE IF NOT EXISTS _rivetkit_metadata(
id INTEGER PRIMARY KEY CHECK (id = 1), // Not obvious why id=1 constraint
name TEXT NOT NULL,
key TEXT NOT NULL,
destroyed INTEGER DEFAULT 0,
generation INTEGER DEFAULT 0
);Recommendation: Add a comment explaining the id=1 constraint ensures single-row table. 5. Potential Memory Leak in Global State (Medium Priority)Location: #dos: Map<string, DurableObjectGlobalState> = new Map();
#actors: Map<string, ActorHandler> = new Map();Problem: The Recommendation: Consider cleanup strategy or document the lifecycle expectations. 6. Type Safety in SQL Queries (Low Priority)Location: Multiple files using The SQL queries use type assertions without validation: const name = result.value[0] as string;
const key = JSON.parse(result.value[1] as string) as ActorKey;
const destroyed = result.value[2] as number;
const generation = result.value[3] as number;Recommendation: Add runtime validation for critical fields: invariant(typeof name === 'string', 'name must be string');
invariant(typeof destroyed === 'number', 'destroyed must be number');Security Concerns7. Actor Key Storage Security (Low Priority)Location: Actor keys are stored as JSON strings in SQLite: key TEXT NOT NULL,
// ...
const key = JSON.parse(result.value[1] as string) as ActorKey;Analysis: This is acceptable since:
Recommendation: Add a comment or documentation noting that actor keys should not contain sensitive information. Performance Considerations8. Serial KV Operations (Medium Priority)Location: async kvBatchPut(actorId: string, entries: [Uint8Array, Uint8Array][]): Promise<void> {
const sql = this.#getDOCtx(actorId).storage.sql;
for (const [key, value] of entries) {
kvPut(sql, key, value); // Sequential INSERTs
}
}Problem: Each Recommendation: Use a single SQL transaction with multiple inserts: sql.exec('BEGIN TRANSACTION');
try {
for (const [key, value] of entries) {
kvPut(sql, key, value);
}
sql.exec('COMMIT');
} catch (err) {
sql.exec('ROLLBACK');
throw err;
}9. Full Table Scan in kvListPrefix (High Priority)Location: export function kvListPrefix(sql: SqlStorage, prefix: Uint8Array): [Uint8Array, Uint8Array][] {
const cursor = sql.exec("SELECT key, value FROM _rivetkit_kv_storage"); // ⚠️ Full table scan!
const entries: [Uint8Array, Uint8Array][] = [];
for (const row of cursor.raw()) {
const key = toUint8Array(row[0]);
const value = toUint8Array(row[1]);
if (hasPrefix(key, prefix)) { // Filtering in application code
entries.push([key, value]);
}
}
return entries;
}Problem: This does a full table scan and filters in memory. For actors with many KV entries, this is very inefficient. Recommendation: Unfortunately, SQLite doesn't support BLOB prefix matching well. Consider:
Test Coverage10. Excellent Test Coverage ✅The test suite (
Minor Suggestion: Add test cases for:
Best Practices & Style11. Good Use of Generation Tracking ✅The generation tracking pattern is excellent:
Observation: The 12. Logging Consistency (Low Priority)Most logging follows the structured logging pattern correctly: logger().debug({ msg: "actor creating", key: req.key, generation });However, some logs could include more context for debugging destroyed actors. Documentation13. Missing Documentation (Medium Priority)Missing:
Recommendation: Add JSDoc comments to key methods and a README section explaining:
Breaking Changes14. Wrangler Configuration Change (Breaking Change)Location: -"new_classes": ["ActorHandler"]
+"new_sqlite_classes": ["ActorHandler"]Impact: This is a breaking change for existing deployments. Users will need to update their wrangler.json files. Recommendation:
SummaryStrengths:
Must Fix Before Merge:
Should Fix:
Nice to Have:
Overall, this is a solid implementation. The core functionality is well-designed, but the race condition and performance issues need to be addressed before merging. Estimated Time to Fix Critical Issues: 2-3 hours |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
3 Skipped Deployments
|
PR Review: Actor Destroy FunctionalitySummaryThis PR adds the ability to destroy actors in RivetKit, introducing a generation-based tracking system for Cloudflare Workers actors to handle actor lifecycle management including resurrection. The implementation is comprehensive with good test coverage. 🎯 StrengthsArchitecture & Design
Testing
🐛 Potential IssuesCritical
Moderate
Minor
⚡ Performance Considerations
🔒 Security Considerations
📝 Code Quality & Best PracticesPositive
Suggestions
🧪 Test CoverageExcellent coverage with:
Missing test cases to consider:
📋 RecommendationsHigh Priority
Medium Priority
Low Priority
✅ VerdictThis is a well-designed feature with good architecture and comprehensive testing. The generation-based tracking is a clever solution for handling actor resurrection. The critical issues identified are mostly around edge cases and performance optimization. Recommendation: Approve with requested changes to address the race condition and performance issues. The memory leak and error handling should be fixed before merge. Great work overall! 🚀 |
72318fc to
cc00181
Compare
892e9cb to
90de47a
Compare
PR Review: Add ability to destroy actorsSummaryThis PR implements actor destruction functionality for RivetKit, adding the ability to explicitly destroy actors and clean up their state. The implementation includes generation tracking to handle actor resurrection and prevent stale references. Code Quality & ArchitectureStrengths
Areas for Improvement1. Race condition in actor loading (rivetkit-typescript/packages/cloudflare-workers/src/actor-driver.ts:132-160)// Check if actor is already loaded
let actorState = this.#globalState.getActorState(doState.ctx);
if (actorState?.actorInstance) {
return actorState.actorInstance;
}
// Create new actor state if it doesn't exist
if (!actorState) {
actorState = new ActorGlobalState();
actorState.actorPromise = promiseWithResolvers();
this.#globalState.setActorState(doState.ctx, actorState);
}
// Another request is already loading this actor, wait for it
if (actorState.actorPromise) {
await actorState.actorPromise.promise;
// ...
}Issue: There's a potential race between checking Recommendation: Initialize the promise immediately when creating actorState: if (!actorState) {
actorState = new ActorGlobalState();
actorState.actorPromise = promiseWithResolvers();
this.#globalState.setActorState(doState.ctx, actorState);
} else if (actorState.actorPromise) {
// Already loading
await actorState.actorPromise.promise;
return actorState.actorInstance!;
}
// Continue with loading...2. Generation validation inconsistency (rivetkit-typescript/packages/cloudflare-workers/src/actor-driver.ts:178-183)// Check if generation matches
if (generation !== expectedGeneration) {
throw new Error(
`Actor ${actorId} generation mismatch - expected ${expectedGeneration}, got ${generation}`,
);
}Issue: This throws a generic Recommendation: Use if (generation !== expectedGeneration) {
throw new ActorNotFound(actorId);
}3. Missing error handling in async destroy (rivetkit-typescript/packages/cloudflare-workers/src/actor-driver.ts:295-320)async #callOnStopAsync(
actorId: string,
doId: string,
actor: CoreAnyActorInstance,
) {
// Stop
await actor.onStop("destroy");
// ... cleanup
}Issue: There's no try-catch around the destroy logic. If Recommendation: Add error handling and logging: async #callOnStopAsync(
actorId: string,
doId: string,
actor: CoreAnyActorInstance,
) {
try {
await actor.onStop("destroy");
const doState = this.#globalState.getDOState(doId);
const sql = doState.ctx.storage.sql;
sql.exec("UPDATE _rivetkit_metadata SET destroyed = 1 WHERE 1=1");
sql.exec("DELETE FROM _rivetkit_kv_storage");
await doState.ctx.storage.deleteAlarm();
const env = getCloudflareAmbientEnv();
doState.ctx.waitUntil(
env.ACTOR_KV.delete(GLOBAL_KV_KEYS.actorMetadata(actorId)),
);
const actorHandle = this.#globalState.getActorState(doState.ctx);
actorHandle?.reset();
} catch (error) {
logger().error({ msg: "error during actor destroy", actorId, error });
// Consider: should we still mark as destroyed? Or roll back?
}
}4. SQL injection vulnerability (rivetkit-typescript/packages/cloudflare-workers/src/actor-kv.ts)While reviewing the diff, I notice SQL execution with blob data. Ensure all SQL queries use parameterized queries, not string concatenation. The implementation appears to use proper parameterization, but verify this throughout. 5. Unclear destroy vs destroyed state (rivetkit-typescript/packages/cloudflare-workers/src/actor-driver.ts:85-89)/**
* Indicates if `startDestroy` has been called.
*
* This is stored in memory instead of SQLite since the destroy may be cancelled.
*
* See the corresponding `destroyed` property in SQLite metadata.
*/
destroying: boolean = false;Question: The comment mentions "destroy may be cancelled" but there's no cancellation logic in the implementation. Is this intended for future functionality? If not, this comment is misleading. Performance Considerations
Security Concerns
Test CoverageThe test suite in
Missing tests:
Minor Issues
export class RequestHandlerNotDfeined extends ActorError {Should be
Recommendations SummaryHigh Priority:
Medium Priority: Low Priority: Overall AssessmentThis is a well-architected implementation of actor destruction with thoughtful consideration for generation tracking and state cleanup. The core logic is solid, but would benefit from additional error handling and edge case tests. The SQL migration is a good architectural choice for blob key support. Recommendation: Approve with suggested improvements, particularly around error handling and race conditions. 🤖 Generated with Claude Code |
90de47a to
6cd3c7d
Compare
|
Deployment failed with the following error: Learn More: https://vercel.com/docs/limits#rate-limits |
Pull Request Review: Actor Destruction FeatureI've completed a comprehensive review of this PR that adds actor destruction capabilities to RivetKit. Overall, this is a well-implemented feature with good architectural decisions. Below are my findings organized by category. ✅ Strengths1. Excellent Generation-Based ArchitectureThe generation counter system is a clean solution for handling actor resurrection:
2. Proper State ManagementThe separation of concerns between memory and persistent state is well-designed:
3. Good Test CoverageThe PR includes driver tests with proper fixtures (destroy.ts, driver-tests.test.ts) that validate the destroy functionality. 4. Cloudflare Workers Migration FixThe wrangler.json fix changing 🔴 Critical Issues1. Race Condition in
|
| Category | Rating | Notes |
|---|---|---|
| Architecture | ⭐⭐⭐⭐⭐ | Generation-based resurrection is excellent |
| Error Handling | ⭐⭐⭐ | Missing some edge cases |
| Performance | ⭐⭐⭐⭐ | Good, but kvListPrefix needs optimization |
| Security | ⭐⭐⭐⭐ | Generally safe, watch for SQL performance issues |
| Test Coverage | ⭐⭐⭐⭐ | Good test fixtures included |
| Documentation | ⭐⭐⭐ | Code is readable but could use more inline comments |
🎯 Recommendations Summary
Must Fix Before Merge:
- Fix race condition in
startDestroy - Add destroying check in
loadActor - Optimize
kvListPrefixto avoid loading all rows
Should Fix:
4. Add try-finally to #callOnStopAsync
5. Fix parseActorId to handle colons in DO IDs
6. Add defensive deletion on resurrection
Nice to Have:
7. Add more debug logging
8. Improve inline documentation
9. Add SQL comments for schema
Conclusion
This is a solid implementation of actor destruction with a clever generation-based resurrection system. The main concerns are around race conditions during destroy and performance of the KV list operation. Once the critical issues are addressed, this will be a robust feature.
Great work on the architecture! The generation counter approach is particularly elegant. 👏
4a21d4f to
b2a85f4
Compare
8ea24ef to
a3beed3
Compare
Pull Request Review: Add ability to destroy actorsSummaryThis PR implements actor destruction functionality across the RivetKit codebase, including generation-based tracking to prevent stale references after resurrection. The implementation is comprehensive and well-tested. Code Quality & Best PracticesStrengths✅ Excellent separation of concerns - The implementation cleanly separates actor ID management, KV storage, and lifecycle handling into distinct modules Areas for Improvement1. SQL Injection PreventionIn sql.exec("UPDATE _rivetkit_metadata SET destroyed = 1 WHERE 1=1");
sql.exec("DELETE FROM _rivetkit_kv_storage");While these specific queries are safe, consider using parameterized queries consistently for all SQL operations to establish a clear pattern. The 2. Race Condition in
|
Pull Request Review: feat(rivetkit): add ability to destroy actorsSummaryThis PR implements actor destruction functionality for RivetKit, adding the ability to destroy actors and properly clean up their state. The implementation includes generation tracking to handle actor resurrection and prevent stale references. Overall AssessmentStrong implementation with good architecture decisions and comprehensive test coverage. The generation tracking system is well-designed and the SQL-based storage approach is solid. A few areas need attention for production readiness. Strengths1. Generation Tracking DesignThe generation-based actor ID system (doId:generation) is an excellent solution for handling actor resurrection:
2. SQL Storage MigrationMoving from native KV storage to SQL for blob keys is the right architectural choice:
3. Comprehensive Test CoverageThe test suite in actor-destroy.ts covers critical scenarios:
4. Separation of ConcernsClean separation between ActorGlobalState for runtime state, SQL metadata for persistent state, and WeakMap usage for proper garbage collection. Critical Issues1. Performance Issue in kvListPrefix - actor-kv.ts:34Severity: High The kvListPrefix function performs a full table scan loading all KV entries into memory. Problem: SELECT key, value FROM _rivetkit_kv_storage loads ALL keys without WHERE clause Impact: Performance degrades linearly with total KV entries, high memory usage Recommendation: Unfortunately SQLite doesnt have native BLOB prefix operator. Consider adding prefix length check in SQL, using a prefix hash column with index, or documenting the limitation. 2. Error Handling in Background Destroy - actor-driver.ts:295-310Severity: Medium Problem: If onStop throws, cleanup may be incomplete (SQLite metadata not updated, ACTOR_KV not deleted, actor state not reset) Recommendation: Wrap onStop in try-catch and always perform cleanup even if onStop fails to prevent zombie actors. 3. Generation Mismatch Error Type - actor-driver.ts:137-143Severity: Low Issue: Throws generic Error instead of custom ActorError Recommendation: Use ActorNotFound or create new ActorGenerationMismatch error type extending ActorError Suggestions
Code QualityPositive patterns:
Action ItemsBefore Merge (Required)
Post-Merge (Recommended)
Final RecommendationApprove with minor changes This is a well-architected implementation of actor destruction with solid test coverage. The generation tracking system is elegant and the SQL migration is the right architectural choice. Address the error handling in background destroy and consider the kvListPrefix optimization before merging. Great work on this implementation! The generation tracking pattern is particularly clever and will serve as a solid foundation for actor lifecycle management. |
Merge activity
|
a3beed3 to
7a3a586
Compare
b2a85f4 to
43a2a61
Compare
Code Review - PR #3458: Add ability to destroy actorsSummaryThis PR implements actor destruction functionality for RivetKit's Cloudflare Workers driver. It introduces a generation-based actor lifecycle system that allows actors to be destroyed and potentially resurrected with incremented generation numbers. Key Changes
Positive Aspects ✅
Issues & Concerns
|
Pull Request Review: Actor Destroy FunctionalityThis PR adds the ability to destroy actors in RivetKit with generation-based tracking. Overall, this is a well-implemented feature with solid testing. Here are my findings: 🎯 Positive Highlights
🐛 Potential Issues1. Race Condition in Actor Loading (rivetkit-typescript/packages/cloudflare-workers/src/actor-driver.ts:131-161)The concurrent loading logic has a potential issue: // Another request is already loading this actor, wait for it
if (actorState.actorPromise) {
await actorState.actorPromise.promise;
if (!actorState.actorInstance) {
throw new Error(...);
}
return actorState.actorInstance;
}Issue: If the first load fails, Recommendation: Set 2. Missing Generation Validation in
|
7a3a586 to
d58f1db
Compare
43a2a61 to
6c6242e
Compare
PR Review: Add Actor Destroy FunctionalityOverviewThis PR adds the ability to destroy actors in RivetKit with a comprehensive implementation that includes:
Strengths1. Excellent Architecture - Generation-Based Actor IDsThe implementation of generation tracking (
2. Comprehensive Test CoverageThe test suite (
3. Clean State ManagementThe
4. KV Storage MigrationMoving from Cloudflare's string-based KV to SQLite (
Issues & Concerns1. 🔴 Critical: Race Condition in Destroy CleanupLocation: // Delete from ACTOR_KV in the background - use full actorId including generation
const env = getCloudflareAmbientEnv();
doState.ctx.waitUntil(
env.ACTOR_KV.delete(GLOBAL_KV_KEYS.actorMetadata(actorId)),
);Problem: The global KV metadata deletion happens in the background via
Recommendation: Either:
2. 🟡 Memory Leak Potential in Global StateLocation: // Map of actor ID -> DO state
#dos: Map<string, DurableObjectGlobalState> = new Map();Problem: The Recommendation: Either:
3. 🟡 Inconsistent Error HandlingLocation: The code checks both if (destroyed) {
throw new Error(`Actor ${actorId} is destroyed`);
}
if (generation !== expectedGeneration) {
throw new Error(
`Actor ${actorId} generation mismatch - expected ${expectedGeneration}, got ${generation}`,
);
}Recommendation: Use custom error types from
4. 🟡 Missing Transaction HandlingLocation: The this.ctx.storage.sql.exec(
`INSERT INTO _rivetkit_metadata (id, name, key, destroyed, generation)
VALUES (1, ?, ?, 0, ?)
ON CONFLICT(id) DO UPDATE SET...`,
req.name,
JSON.stringify(req.key),
generation,
);
// ... followed by KV initializationRecommendation: Wrap the metadata insert and KV initialization in an explicit transaction to ensure atomicity. 5. 🟢 Minor: Logging ConventionsLocation: Multiple files Several log messages use uppercase, which contradicts the CLAUDE.md conventions: logger().debug({
msg: "Found actor metadata", // Should be lowercase
// ...
});Recommendation: Lowercase all log messages per CLAUDE.md guidelines: "Log messages should be lowercase unless mentioning specific code symbols." 6. 🟢 Minor: Unused ImportLocation: import { stringifyError } from "rivetkit/utils";This import appears to be unused in the file. 7. 🟢 Documentation: Wrangler Migration ChangeLocation: "new_sqlite_classes": ["ActorHandler"]Good catch! This change from Recommendation: Document this breaking change in the PR description or migration guide, as existing deployments will need to update their wrangler.json. Performance Considerations1. ✅ Good: Async DestroyThe 2. ✅ Good: Efficient KV OperationsThe new SQLite-based KV implementation supports efficient batch operations without serialization overhead. 3.
|
PR Review: Add Actor Destroy CapabilityThis PR adds the ability to destroy actors in RivetKit, including proper cleanup of state, generation tracking to prevent stale references, and comprehensive test coverage. Overall, this is a well-designed and thorough implementation. Here are my findings: ✅ Strengths
|
d58f1db to
fc21704
Compare
Code Review: Actor Destroy FunctionalitySummaryThis PR adds the ability to destroy actors in RivetKit, implementing a generation-based resurrection mechanism for Cloudflare Workers. The changes are substantial (+1378/-346 lines) and introduce important new functionality. Positive AspectsArchitecture & Design
Code Quality
Issues & ConcernsCritical Issues1. SQL Injection Risk (actor-handler-do.ts:350-357)Location: this.ctx.storage.sql.exec(
\`INSERT INTO _rivetkit_metadata (id, name, key, destroyed, generation)
VALUES (1, ?, ?, 0, ?)
ON CONFLICT(id) DO UPDATE SET
name = excluded.name,
key = excluded.key,
destroyed = 0,
generation = excluded.generation\`,
req.name,
JSON.stringify(req.key),
generation,
);Issue: While parameterized queries are used for values, this is good. However, ensure Recommendation: Add validation for 2. Race Condition in Actor Destruction (actor-driver.ts:227-295)Location: startDestroy(actorId: string): void {
// ... checks ...
actorState.destroying = true;
// Spawn onStop in background
this.#callOnStopAsync(actorId, doId, actorState.actorInstance);
}Issue: The destroy process is started asynchronously without any locking mechanism. Multiple concurrent Recommendation: Consider adding atomic check-and-set semantics or proper locking to prevent race conditions during destruction. 3. Memory Leak Potential (actor-handler-do.ts:166-171)Location: // HACK: This leaks the DO context, but DO does not provide a native way
// of knowing when the DO shuts down. We're making a broad assumption
// that DO will boot a new isolate frequenlty enough that this is not an issue.
const actorId = this.ctx.id.toString();
globalState.setDOState(actorId, { ctx: this.ctx, env: env });Issue: Acknowledged leak of DO context. While the comment suggests this is acceptable, it could accumulate over time. Recommendation: Consider implementing cleanup on actor destruction or periodic garbage collection of stale entries. High Priority Issues4. Missing Error Handling in Background Destroy (actor-driver.ts:268-295)Location: async #callOnStopAsync(actorId: string, doId: string, actor: CoreAnyActorInstance) {
// Stop
await actor.onStop("destroy");
// Remove state
const doState = this.#globalState.getDOState(doId);
const sql = doState.ctx.storage.sql;
sql.exec("UPDATE _rivetkit_metadata SET destroyed = 1 WHERE 1=1");
sql.exec("DELETE FROM _rivetkit_kv_storage");
// ...
}Issue: No try-catch around the destroy operations. If Recommendation: Wrap in try-catch and log errors. Ensure state cleanup happens even if 5. Inconsistent Generation Validation (actor-driver.ts:100-122)Location: // Check if generation matches
if (generation !== expectedGeneration) {
throw new Error(
\`Actor \${actorId} generation mismatch - expected \${expectedGeneration}, got \${generation}\`,
);
}Issue: Generation mismatch throws a generic Recommendation: Create a custom error like 6. Missing waitUntil Error Handling (actor-handler-do.ts:379-386)Location: const env = getCloudflareAmbientEnv();
const actorData = { name: req.name, key: req.key, generation };
this.ctx.waitUntil(
env.ACTOR_KV.put(
GLOBAL_KV_KEYS.actorMetadata(actorId),
JSON.stringify(actorData),
),
);Issue: Background KV operations could fail silently without logging or error handling. Recommendation: Wrap in a promise that logs failures: this.ctx.waitUntil(
env.ACTOR_KV.put(...)
.catch(err => logger().error({ msg: "failed to update actor metadata", err }))
);Medium Priority Issues7. Unbounded SQL Results (actor-driver.ts:91-104)Location: const cursor = sql.exec(
"SELECT name, key, destroyed, generation FROM _rivetkit_metadata LIMIT 1",
);
const result = cursor.raw().next();Issue: While Recommendation: Add explicit check that ensures metadata exists and is unique. 8. Typo in Error Class Name (errors.ts:331)Location: export class RequestHandlerNotDfeined extends ActorError {Issue: Typo - "Dfeined" should be "Defined" Recommendation: Rename to 9. Test Coverage GapsLocation: Test files only cover basic destroy scenarios Missing Coverage:
Recommendation: Add tests for edge cases, especially concurrent operations. 10. Documentation for new_sqlite_classes MigrationLocation: "new_sqlite_classes": ["ActorHandler"]Issue: Changed from Recommendation: Add migration notes or comments explaining this breaking change for existing users. Low Priority / Style Issues11. Magic Number for Destroyed FlagMultiple locations use Recommendation: Define constants: const ACTOR_DESTROYED = 1;
const ACTOR_ACTIVE = 0;12. Inconsistent Logging PatternsSome logs use logger().debug({ msg: "...", generation }); // ✓
logger().debug("actor is destroyed, cannot load"); // ✗Recommendation: Standardize on structured logging with Performance Considerations
Security Considerations
Testing Recommendations
Overall AssessmentQuality: Good ⭐⭐⭐⭐ (4/5)
Concerns: Medium
RecommendationApprove with Changes: The core implementation is solid, but the critical and high-priority issues should be addressed before merging. The generation-based approach is sound, and the code quality is generally good. Must-Fix Before Merge
Should-Fix Soon
Great work on this feature! The destroy functionality is a valuable addition to RivetKit. 🚀 |

No description provided.