Skip to content

Commit 2e0e6b0

Browse files
committed
fix(rivetkit): keep actor sqlite open until core finishes destroy shutdown
1 parent c00ab5f commit 2e0e6b0

4 files changed

Lines changed: 75 additions & 1 deletion

File tree

‎rivetkit-typescript/packages/rivetkit/fixtures/driver-test-suite/destroy.ts‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// @ts-nocheck
22
import { actor, queue } from "rivetkit";
3+
import { db } from "@/common/database/mod";
34
import type { registry } from "./registry-static";
45

56
export const destroyObserver = actor({
@@ -88,6 +89,27 @@ export const destroyActor = actor({
8889
},
8990
});
9091

92+
export const destroyDbActor = actor({
93+
state: { value: 0 },
94+
db: db({
95+
onMigrate: async (db) => {
96+
await db.execute("CREATE TABLE IF NOT EXISTS entries (value TEXT)");
97+
},
98+
}),
99+
actions: {
100+
getValue: (c) => {
101+
return c.state.value;
102+
},
103+
destroy: (c) => {
104+
c.destroy();
105+
},
106+
setValueThenDestroy: (c, newValue: number) => {
107+
c.state.value = newValue;
108+
c.destroy();
109+
},
110+
},
111+
});
112+
91113
export const destroyAbortSignalActor = actor({
92114
state: {
93115
abortEventCount: 0,

‎rivetkit-typescript/packages/rivetkit/fixtures/driver-test-suite/registry-static.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ import { dbStressActor } from "./db-stress";
5858
import {
5959
destroyAbortSignalActor,
6060
destroyActor,
61+
destroyDbActor,
6162
destroyObserver,
6263
} from "./destroy";
6364
import { customTimeoutActor, errorHandlingActor } from "./error-handling";
@@ -311,6 +312,7 @@ export const registry = setup({
311312
destroyActor,
312313
destroyObserver,
313314
destroyAbortSignalActor,
315+
destroyDbActor,
314316
// From hibernation.ts
315317
hibernationActor,
316318
hibernationSleepWindowActor,

‎rivetkit-typescript/packages/rivetkit/src/registry/native.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4846,7 +4846,6 @@ export function buildNativeFactory(
48464846
}
48474847
} finally {
48484848
resolveNativeDestroy(runtime, ctx);
4849-
await actorCtx.closeDatabase();
48504849
clearNativeRuntimeState(runtime, ctx);
48514850
await actorCtx.dispose();
48524851
}

‎rivetkit-typescript/packages/rivetkit/tests/driver/actor-destroy.test.ts‎

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,5 +433,56 @@ describeDriverMatrix("Actor Destroy", (driverTestConfig) => {
433433
expect(await handle.getValue()).toBe(0);
434434
expect(await handle.resolve()).toBe(await recreated.resolve());
435435
});
436+
437+
type DriverTestClient = Awaited<
438+
ReturnType<typeof setupDriverTest>
439+
>["client"];
440+
441+
async function startDbActor(client: DriverTestClient, name: string) {
442+
const actorKey = `test-destroy-db-${name}-${crypto.randomUUID()}`;
443+
const handle = client.destroyDbActor.getOrCreate([actorKey]);
444+
expect(await handle.getValue()).toBe(0);
445+
const actorId = await handle.resolve();
446+
return { actorKey, actorId, handle };
447+
}
448+
449+
async function expectDbActorDestroyed(
450+
client: DriverTestClient,
451+
{ actorKey, actorId }: { actorKey: string; actorId: string },
452+
) {
453+
// Poll because actor lookup only turns into not_found once destroy teardown fully completes.
454+
await vi.waitFor(async () => {
455+
let actorRunning = false;
456+
try {
457+
await client.destroyDbActor.getForId(actorId).getValue();
458+
actorRunning = true;
459+
} catch (error) {
460+
expectActorNotFound(error);
461+
}
462+
expect(actorRunning, "actor still running").toBeFalsy();
463+
});
464+
465+
const recreated = client.destroyDbActor.getOrCreate([actorKey]);
466+
expect(await recreated.getValue()).toBe(0);
467+
expect(await recreated.resolve()).not.toBe(actorId);
468+
}
469+
470+
test("actor with a database destroys with clean state", async (c) => {
471+
const { client } = await setupDriverTest(c, driverTestConfig);
472+
const dbActor = await startDbActor(client, "clean-state");
473+
474+
await dbActor.handle.destroy();
475+
476+
await expectDbActorDestroyed(client, dbActor);
477+
});
478+
479+
test("actor with a database destroys after an unsaved state change", async (c) => {
480+
const { client } = await setupDriverTest(c, driverTestConfig);
481+
const dbActor = await startDbActor(client, "unsaved-state");
482+
483+
await dbActor.handle.setValueThenDestroy(7);
484+
485+
await expectDbActorDestroyed(client, dbActor);
486+
});
436487
});
437488
});

0 commit comments

Comments
 (0)