@rivetkit/engine-runner 2.1.9 → 2.1.11-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rivetkit/engine-runner",
3
- "version": "2.1.9",
3
+ "version": "2.1.11-rc.1",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -21,8 +21,8 @@
21
21
  "uuid": "^12.0.0",
22
22
  "pino": "^9.9.5",
23
23
  "ws": "^8.18.3",
24
- "@rivetkit/engine-runner-protocol": "2.1.9",
25
- "@rivetkit/virtual-websocket": "2.0.33"
24
+ "@rivetkit/virtual-websocket": "2.0.33",
25
+ "@rivetkit/engine-runner-protocol": "2.1.11-rc.1"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/node": "^22.18.1",
package/src/actor.ts CHANGED
@@ -37,6 +37,13 @@ export class RunnerActor {
37
37
  **/
38
38
  hibernationRestored: boolean = false;
39
39
 
40
+ /**
41
+ * Set when the actor has explicitly requested to stop (e.g. c.destroy()).
42
+ * Used to send StopCode.Ok (graceful) vs StopCode.Error (ungraceful) so
43
+ * the engine crash policy handles sleepable actors correctly.
44
+ **/
45
+ stopIntentSent: boolean = false;
46
+
40
47
  constructor(
41
48
  actorId: string,
42
49
  generation: number,
package/src/mod.ts CHANGED
@@ -217,6 +217,7 @@ export class Runner {
217
217
  runnerId?: string;
218
218
  #started: boolean = false;
219
219
  #shutdown: boolean = false;
220
+ #draining: boolean = false;
220
221
  #reconnectAttempt: number = 0;
221
222
  #reconnectTimeout?: NodeJS.Timeout;
222
223
 
@@ -303,6 +304,24 @@ export class Runner {
303
304
  // The server will send a StopActor command if it wants to fully stop
304
305
  }
305
306
 
307
+ /**
308
+ * Like stopActor but marks the actor for graceful destruction.
309
+ * This ensures the engine destroys the actor instead of sleeping it.
310
+ *
311
+ * NOTE: If a drain (GoingAway) occurs after this is called but before the
312
+ * stop completes, the engine's going_away flag overrides graceful_exit and
313
+ * the actor will sleep instead of being destroyed. The destroy intent is
314
+ * lost in this race. This is acceptable since the actor will be rescheduled
315
+ * elsewhere and can be destroyed on the next wake.
316
+ */
317
+ destroyActor(actorId: string, generation?: number) {
318
+ const actor = this.getActor(actorId, generation);
319
+ if (!actor) return;
320
+
321
+ actor.stopIntentSent = true;
322
+ this.#sendActorIntent(actorId, actor.generation, "stop");
323
+ }
324
+
306
325
  async forceStopActor(actorId: string, generation?: number) {
307
326
  this.log?.debug({
308
327
  msg: "force stopping actor",
@@ -502,6 +521,7 @@ export class Runner {
502
521
  return;
503
522
  }
504
523
  this.#shutdown = true;
524
+ this.#draining = !immediate;
505
525
 
506
526
  this.log?.info({
507
527
  msg: "starting shutdown",
@@ -1233,7 +1253,9 @@ export class Runner {
1233
1253
  actorState = {
1234
1254
  tag: "ActorStateStopped",
1235
1255
  val: {
1236
- code: protocol.StopCode.Ok,
1256
+ code: actor.stopIntentSent || this.#draining
1257
+ ? protocol.StopCode.Ok
1258
+ : protocol.StopCode.Error,
1237
1259
  message: null,
1238
1260
  },
1239
1261
  };