rivetkit 2.0.22-rc.1 → 2.0.22
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/dist/schemas/actor-persist/v2.ts +259 -0
- package/dist/tsup/{chunk-LCQDY73V.cjs → chunk-2GJILCGQ.cjs} +3 -3
- package/dist/tsup/{chunk-LCQDY73V.cjs.map → chunk-2GJILCGQ.cjs.map} +1 -1
- package/dist/tsup/{chunk-JN6GPVFY.js → chunk-2K2LR56Q.js} +3 -3
- package/dist/tsup/{chunk-EEXX243L.js → chunk-2WVCZCJL.js} +6 -6
- package/dist/tsup/{chunk-FETQGZN4.js → chunk-3BJJSSTM.js} +272 -89
- package/dist/tsup/chunk-3BJJSSTM.js.map +1 -0
- package/dist/tsup/{chunk-ZZYMCYAY.cjs → chunk-3LFMVAJV.cjs} +14 -14
- package/dist/tsup/{chunk-ZZYMCYAY.cjs.map → chunk-3LFMVAJV.cjs.map} +1 -1
- package/dist/tsup/{chunk-NDLOG2JH.js → chunk-6YQKMAMV.js} +2 -2
- package/dist/tsup/{chunk-C2U6KGOG.cjs → chunk-AR4S2QJ7.cjs} +3 -3
- package/dist/tsup/{chunk-C2U6KGOG.cjs.map → chunk-AR4S2QJ7.cjs.map} +1 -1
- package/dist/tsup/{chunk-PELXJCJS.cjs → chunk-B4QZKOMH.cjs} +8 -8
- package/dist/tsup/{chunk-PELXJCJS.cjs.map → chunk-B4QZKOMH.cjs.map} +1 -1
- package/dist/tsup/{chunk-5EB77IQ2.cjs → chunk-CYA35VI3.cjs} +6 -6
- package/dist/tsup/{chunk-5EB77IQ2.cjs.map → chunk-CYA35VI3.cjs.map} +1 -1
- package/dist/tsup/{chunk-UBCUW7HD.js → chunk-D7AA2DK5.js} +2 -2
- package/dist/tsup/{chunk-I7EJWHYV.js → chunk-EBSGEDD3.js} +51 -47
- package/dist/tsup/chunk-EBSGEDD3.js.map +1 -0
- package/dist/tsup/{chunk-R6XOZKMU.cjs → chunk-HSO2H2SB.cjs} +467 -284
- package/dist/tsup/chunk-HSO2H2SB.cjs.map +1 -0
- package/dist/tsup/{chunk-VJLGVVGP.cjs → chunk-HZ4ZM3FL.cjs} +31 -12
- package/dist/tsup/chunk-HZ4ZM3FL.cjs.map +1 -0
- package/dist/tsup/{chunk-7FEMVD3D.cjs → chunk-LMZSOCYD.cjs} +12 -12
- package/dist/tsup/{chunk-7FEMVD3D.cjs.map → chunk-LMZSOCYD.cjs.map} +1 -1
- package/dist/tsup/{chunk-ZVEDMBFT.js → chunk-PBFLG45S.js} +3 -3
- package/dist/tsup/{chunk-GJPOIJHZ.js → chunk-ST6FGRCH.js} +26 -7
- package/dist/tsup/chunk-ST6FGRCH.js.map +1 -0
- package/dist/tsup/{chunk-BIOPK7IB.cjs → chunk-TI72NLP3.cjs} +71 -67
- package/dist/tsup/chunk-TI72NLP3.cjs.map +1 -0
- package/dist/tsup/{chunk-RPI45FGS.js → chunk-TQ4OAC2G.js} +2 -2
- package/dist/tsup/{chunk-4B25D5OW.js → chunk-UB4OHFDW.js} +385 -104
- package/dist/tsup/chunk-UB4OHFDW.js.map +1 -0
- package/dist/tsup/{chunk-6Z3YA6QR.cjs → chunk-V6C34TVH.cjs} +35 -15
- package/dist/tsup/chunk-V6C34TVH.cjs.map +1 -0
- package/dist/tsup/{chunk-OAB7ECAB.cjs → chunk-WVUAO2F7.cjs} +558 -277
- package/dist/tsup/chunk-WVUAO2F7.cjs.map +1 -0
- package/dist/tsup/{chunk-JKNDUKFI.js → chunk-WWAZJHTS.js} +36 -16
- package/dist/tsup/chunk-WWAZJHTS.js.map +1 -0
- package/dist/tsup/client/mod.cjs +9 -9
- package/dist/tsup/client/mod.d.cts +2 -2
- package/dist/tsup/client/mod.d.ts +2 -2
- package/dist/tsup/client/mod.js +8 -8
- package/dist/tsup/common/log.cjs +3 -3
- package/dist/tsup/common/log.js +2 -2
- package/dist/tsup/common/websocket.cjs +4 -4
- package/dist/tsup/common/websocket.js +3 -3
- package/dist/tsup/{conn-Clu655RU.d.ts → conn-BYXlxnh0.d.ts} +111 -102
- package/dist/tsup/{conn-lUvFLo_q.d.cts → conn-BiazosE_.d.cts} +111 -102
- package/dist/tsup/driver-helpers/mod.cjs +5 -5
- package/dist/tsup/driver-helpers/mod.d.cts +1 -1
- package/dist/tsup/driver-helpers/mod.d.ts +1 -1
- package/dist/tsup/driver-helpers/mod.js +4 -4
- package/dist/tsup/driver-test-suite/mod.cjs +71 -71
- package/dist/tsup/driver-test-suite/mod.d.cts +1 -1
- package/dist/tsup/driver-test-suite/mod.d.ts +1 -1
- package/dist/tsup/driver-test-suite/mod.js +11 -11
- package/dist/tsup/inspector/mod.cjs +6 -6
- package/dist/tsup/inspector/mod.d.cts +2 -2
- package/dist/tsup/inspector/mod.d.ts +2 -2
- package/dist/tsup/inspector/mod.js +5 -5
- package/dist/tsup/mod.cjs +10 -10
- package/dist/tsup/mod.d.cts +3 -3
- package/dist/tsup/mod.d.ts +3 -3
- package/dist/tsup/mod.js +9 -9
- package/dist/tsup/test/mod.cjs +11 -11
- package/dist/tsup/test/mod.d.cts +1 -1
- package/dist/tsup/test/mod.d.ts +1 -1
- package/dist/tsup/test/mod.js +10 -10
- package/dist/tsup/utils.cjs +8 -2
- package/dist/tsup/utils.cjs.map +1 -1
- package/dist/tsup/utils.d.cts +8 -1
- package/dist/tsup/utils.d.ts +8 -1
- package/dist/tsup/utils.js +7 -1
- package/package.json +5 -4
- package/src/actor/config.ts +10 -0
- package/src/actor/conn-drivers.ts +43 -1
- package/src/actor/conn-socket.ts +1 -1
- package/src/actor/conn.ts +22 -2
- package/src/actor/context.ts +1 -1
- package/src/actor/driver.ts +13 -2
- package/src/actor/instance.ts +248 -57
- package/src/actor/persisted.ts +7 -0
- package/src/actor/router-endpoints.ts +67 -45
- package/src/actor/router.ts +25 -17
- package/src/client/actor-conn.ts +9 -5
- package/src/common/cors.ts +57 -0
- package/src/common/log.ts +26 -5
- package/src/common/utils.ts +5 -9
- package/src/common/websocket-interface.ts +10 -0
- package/src/driver-helpers/utils.ts +1 -0
- package/src/drivers/engine/actor-driver.ts +261 -14
- package/src/drivers/engine/config.ts +2 -4
- package/src/drivers/file-system/actor.ts +3 -2
- package/src/drivers/file-system/global-state.ts +1 -1
- package/src/drivers/file-system/manager.ts +3 -0
- package/src/engine-process/mod.ts +22 -4
- package/src/inspector/config.ts +0 -45
- package/src/manager/gateway.ts +45 -32
- package/src/manager/hono-websocket-adapter.ts +31 -3
- package/src/manager/router.ts +11 -17
- package/src/registry/run-config.ts +2 -8
- package/src/remote-manager-driver/actor-http-client.ts +5 -8
- package/src/remote-manager-driver/actor-websocket-client.ts +2 -14
- package/src/remote-manager-driver/mod.ts +0 -1
- package/src/schemas/actor-persist/mod.ts +1 -1
- package/src/schemas/actor-persist/versioned.ts +22 -10
- package/src/utils.ts +26 -0
- package/dist/tsup/chunk-4B25D5OW.js.map +0 -1
- package/dist/tsup/chunk-6Z3YA6QR.cjs.map +0 -1
- package/dist/tsup/chunk-BIOPK7IB.cjs.map +0 -1
- package/dist/tsup/chunk-FETQGZN4.js.map +0 -1
- package/dist/tsup/chunk-GJPOIJHZ.js.map +0 -1
- package/dist/tsup/chunk-I7EJWHYV.js.map +0 -1
- package/dist/tsup/chunk-JKNDUKFI.js.map +0 -1
- package/dist/tsup/chunk-OAB7ECAB.cjs.map +0 -1
- package/dist/tsup/chunk-R6XOZKMU.cjs.map +0 -1
- package/dist/tsup/chunk-VJLGVVGP.cjs.map +0 -1
- /package/dist/tsup/{chunk-JN6GPVFY.js.map → chunk-2K2LR56Q.js.map} +0 -0
- /package/dist/tsup/{chunk-EEXX243L.js.map → chunk-2WVCZCJL.js.map} +0 -0
- /package/dist/tsup/{chunk-NDLOG2JH.js.map → chunk-6YQKMAMV.js.map} +0 -0
- /package/dist/tsup/{chunk-UBCUW7HD.js.map → chunk-D7AA2DK5.js.map} +0 -0
- /package/dist/tsup/{chunk-ZVEDMBFT.js.map → chunk-PBFLG45S.js.map} +0 -0
- /package/dist/tsup/{chunk-RPI45FGS.js.map → chunk-TQ4OAC2G.js.map} +0 -0
package/src/actor/instance.ts
CHANGED
|
@@ -15,7 +15,9 @@ import { PERSISTED_ACTOR_VERSIONED } from "@/schemas/actor-persist/versioned";
|
|
|
15
15
|
import type * as protocol from "@/schemas/client-protocol/mod";
|
|
16
16
|
import { TO_CLIENT_VERSIONED } from "@/schemas/client-protocol/versioned";
|
|
17
17
|
import {
|
|
18
|
+
arrayBuffersEqual,
|
|
18
19
|
bufferToArrayBuffer,
|
|
20
|
+
EXTRA_ERROR_LOG,
|
|
19
21
|
getEnvUniversal,
|
|
20
22
|
promiseWithResolvers,
|
|
21
23
|
SinglePromiseQueue,
|
|
@@ -26,7 +28,7 @@ import {
|
|
|
26
28
|
Conn,
|
|
27
29
|
type ConnId,
|
|
28
30
|
generateConnId,
|
|
29
|
-
|
|
31
|
+
generateConnRequestId,
|
|
30
32
|
generateConnToken,
|
|
31
33
|
} from "./conn";
|
|
32
34
|
import {
|
|
@@ -45,6 +47,7 @@ import { serializeActorKey } from "./keys";
|
|
|
45
47
|
import type {
|
|
46
48
|
PersistedActor,
|
|
47
49
|
PersistedConn,
|
|
50
|
+
PersistedHibernatableWebSocket,
|
|
48
51
|
PersistedScheduleEvent,
|
|
49
52
|
} from "./persisted";
|
|
50
53
|
import { processMessage } from "./protocol/old";
|
|
@@ -52,6 +55,8 @@ import { CachedSerializer } from "./protocol/serde";
|
|
|
52
55
|
import { Schedule } from "./schedule";
|
|
53
56
|
import { DeadlineError, deadline } from "./utils";
|
|
54
57
|
|
|
58
|
+
export const PERSIST_SYMBOL = Symbol("persist");
|
|
59
|
+
|
|
55
60
|
/**
|
|
56
61
|
* Options for the `_saveState` method.
|
|
57
62
|
*/
|
|
@@ -131,6 +136,14 @@ export type ExtractActorConnState<A extends AnyActorInstance> =
|
|
|
131
136
|
? ConnState
|
|
132
137
|
: never;
|
|
133
138
|
|
|
139
|
+
enum CanSleep {
|
|
140
|
+
Yes,
|
|
141
|
+
NotReady,
|
|
142
|
+
ActiveConns,
|
|
143
|
+
ActiveHonoHttpRequests,
|
|
144
|
+
ActiveRawWebSockets,
|
|
145
|
+
}
|
|
146
|
+
|
|
134
147
|
export class ActorInstance<S, CP, CS, V, I, DB extends AnyDatabaseProvider> {
|
|
135
148
|
// Shared actor context for this instance
|
|
136
149
|
actorContext: ActorContext<S, CP, CS, V, I, DB>;
|
|
@@ -145,7 +158,7 @@ export class ActorInstance<S, CP, CS, V, I, DB extends AnyDatabaseProvider> {
|
|
|
145
158
|
#stopCalled = false;
|
|
146
159
|
|
|
147
160
|
get isStopping() {
|
|
148
|
-
return this.#stopCalled
|
|
161
|
+
return this.#stopCalled;
|
|
149
162
|
}
|
|
150
163
|
|
|
151
164
|
#persistChanged = false;
|
|
@@ -158,6 +171,10 @@ export class ActorInstance<S, CP, CS, V, I, DB extends AnyDatabaseProvider> {
|
|
|
158
171
|
*/
|
|
159
172
|
#persist!: PersistedActor<S, CP, CS, I>;
|
|
160
173
|
|
|
174
|
+
get [PERSIST_SYMBOL](): PersistedActor<S, CP, CS, I> {
|
|
175
|
+
return this.#persist;
|
|
176
|
+
}
|
|
177
|
+
|
|
161
178
|
/** Raw state without the proxy wrapper */
|
|
162
179
|
#persistRaw!: PersistedActor<S, CP, CS, I>;
|
|
163
180
|
|
|
@@ -186,8 +203,11 @@ export class ActorInstance<S, CP, CS, V, I, DB extends AnyDatabaseProvider> {
|
|
|
186
203
|
|
|
187
204
|
#sleepTimeout?: NodeJS.Timeout;
|
|
188
205
|
|
|
189
|
-
|
|
190
|
-
|
|
206
|
+
/**
|
|
207
|
+
* Track active HTTP requests through Hono router so sleep logic can
|
|
208
|
+
* account for them. Does not include WebSockets.
|
|
209
|
+
**/
|
|
210
|
+
#activeHonoHttpRequests = 0;
|
|
191
211
|
#activeRawWebSockets = new Set<UniversalWebSocket>();
|
|
192
212
|
|
|
193
213
|
#schedule!: Schedule;
|
|
@@ -217,9 +237,17 @@ export class ActorInstance<S, CP, CS, V, I, DB extends AnyDatabaseProvider> {
|
|
|
217
237
|
return Array.from(this.#connections.entries()).map(
|
|
218
238
|
([id, conn]) => ({
|
|
219
239
|
id,
|
|
220
|
-
stateEnabled: conn.__stateEnabled,
|
|
221
240
|
params: conn.params as any,
|
|
222
241
|
state: conn.__stateEnabled ? conn.state : undefined,
|
|
242
|
+
status: conn.status,
|
|
243
|
+
subscriptions: conn.subscriptions.size,
|
|
244
|
+
lastSeen: conn.lastSeen,
|
|
245
|
+
stateEnabled: conn.__stateEnabled,
|
|
246
|
+
isHibernatable: conn.isHibernatable,
|
|
247
|
+
requestId: conn.__socket?.requestId,
|
|
248
|
+
driver: conn.__driverState
|
|
249
|
+
? getConnDriverKindFromState(conn.__driverState)
|
|
250
|
+
: undefined,
|
|
223
251
|
}),
|
|
224
252
|
);
|
|
225
253
|
},
|
|
@@ -235,10 +263,10 @@ export class ActorInstance<S, CP, CS, V, I, DB extends AnyDatabaseProvider> {
|
|
|
235
263
|
await this.saveState({ immediate: true });
|
|
236
264
|
},
|
|
237
265
|
executeAction: async (name, params) => {
|
|
238
|
-
const
|
|
266
|
+
const requestId = generateConnRequestId();
|
|
239
267
|
const conn = await this.createConn(
|
|
240
268
|
{
|
|
241
|
-
|
|
269
|
+
requestId: requestId,
|
|
242
270
|
driverState: { [ConnDriverKind.HTTP]: {} },
|
|
243
271
|
},
|
|
244
272
|
undefined,
|
|
@@ -252,7 +280,7 @@ export class ActorInstance<S, CP, CS, V, I, DB extends AnyDatabaseProvider> {
|
|
|
252
280
|
params || [],
|
|
253
281
|
);
|
|
254
282
|
} finally {
|
|
255
|
-
this.__connDisconnected(conn, true,
|
|
283
|
+
this.__connDisconnected(conn, true, requestId);
|
|
256
284
|
}
|
|
257
285
|
},
|
|
258
286
|
};
|
|
@@ -271,7 +299,7 @@ export class ActorInstance<S, CP, CS, V, I, DB extends AnyDatabaseProvider> {
|
|
|
271
299
|
}
|
|
272
300
|
|
|
273
301
|
get #sleepingSupported(): boolean {
|
|
274
|
-
return this.#actorDriver.
|
|
302
|
+
return this.#actorDriver.startSleep !== undefined;
|
|
275
303
|
}
|
|
276
304
|
|
|
277
305
|
/**
|
|
@@ -300,6 +328,9 @@ export class ActorInstance<S, CP, CS, V, I, DB extends AnyDatabaseProvider> {
|
|
|
300
328
|
actorId,
|
|
301
329
|
};
|
|
302
330
|
|
|
331
|
+
const extraLogParams = actorDriver.getExtraActorLogParams?.();
|
|
332
|
+
if (extraLogParams) Object.assign(logParams, extraLogParams);
|
|
333
|
+
|
|
303
334
|
this.#log = getBaseLogger().child(
|
|
304
335
|
Object.assign(
|
|
305
336
|
getIncludeTarget() ? { target: "actor" } : {},
|
|
@@ -861,16 +892,20 @@ export class ActorInstance<S, CP, CS, V, I, DB extends AnyDatabaseProvider> {
|
|
|
861
892
|
__connDisconnected(
|
|
862
893
|
conn: Conn<S, CP, CS, V, I, DB>,
|
|
863
894
|
wasClean: boolean,
|
|
864
|
-
|
|
895
|
+
requestId: string,
|
|
865
896
|
) {
|
|
866
897
|
// If socket ID is provided, check if it matches the current socket ID
|
|
867
898
|
// If it doesn't match, this is a stale disconnect event from an old socket
|
|
868
|
-
if (
|
|
899
|
+
if (
|
|
900
|
+
requestId &&
|
|
901
|
+
conn.__socket &&
|
|
902
|
+
requestId !== conn.__socket.requestId
|
|
903
|
+
) {
|
|
869
904
|
this.#rLog.debug({
|
|
870
905
|
msg: "ignoring stale disconnect event",
|
|
871
906
|
connId: conn.id,
|
|
872
|
-
|
|
873
|
-
|
|
907
|
+
eventRequestId: requestId,
|
|
908
|
+
currentRequestId: conn.__socket.requestId,
|
|
874
909
|
});
|
|
875
910
|
return;
|
|
876
911
|
}
|
|
@@ -1270,8 +1305,6 @@ export class ActorInstance<S, CP, CS, V, I, DB extends AnyDatabaseProvider> {
|
|
|
1270
1305
|
|
|
1271
1306
|
#assertReady(allowStoppingState: boolean = false) {
|
|
1272
1307
|
if (!this.#ready) throw new errors.InternalError("Actor not ready");
|
|
1273
|
-
if (!allowStoppingState && this.#sleepCalled)
|
|
1274
|
-
throw new errors.InternalError("Actor is going to sleep");
|
|
1275
1308
|
if (!allowStoppingState && this.#stopCalled)
|
|
1276
1309
|
throw new errors.InternalError("Actor is stopping");
|
|
1277
1310
|
}
|
|
@@ -1283,13 +1316,19 @@ export class ActorInstance<S, CP, CS, V, I, DB extends AnyDatabaseProvider> {
|
|
|
1283
1316
|
#checkConnectionsLiveness() {
|
|
1284
1317
|
this.#rLog.debug({ msg: "checking connections liveness" });
|
|
1285
1318
|
|
|
1319
|
+
let connected = 0;
|
|
1320
|
+
let reconnecting = 0;
|
|
1321
|
+
let removed = 0;
|
|
1286
1322
|
for (const conn of this.#connections.values()) {
|
|
1287
1323
|
if (conn.__status === "connected") {
|
|
1324
|
+
connected += 1;
|
|
1288
1325
|
this.#rLog.debug({
|
|
1289
1326
|
msg: "connection is alive",
|
|
1290
1327
|
connId: conn.id,
|
|
1291
1328
|
});
|
|
1292
1329
|
} else {
|
|
1330
|
+
reconnecting += 1;
|
|
1331
|
+
|
|
1293
1332
|
const lastSeen = conn.__persist.lastSeen;
|
|
1294
1333
|
const sinceLastSeen = Date.now() - lastSeen;
|
|
1295
1334
|
if (
|
|
@@ -1313,9 +1352,18 @@ export class ActorInstance<S, CP, CS, V, I, DB extends AnyDatabaseProvider> {
|
|
|
1313
1352
|
});
|
|
1314
1353
|
|
|
1315
1354
|
// Assume that the connection is dead here, no need to disconnect anything
|
|
1355
|
+
removed += 1;
|
|
1316
1356
|
this.#removeConn(conn);
|
|
1317
1357
|
}
|
|
1318
1358
|
}
|
|
1359
|
+
|
|
1360
|
+
this.#rLog.debug({
|
|
1361
|
+
msg: "checked connection liveness",
|
|
1362
|
+
total: connected + reconnecting,
|
|
1363
|
+
connected,
|
|
1364
|
+
reconnecting,
|
|
1365
|
+
removed,
|
|
1366
|
+
});
|
|
1319
1367
|
}
|
|
1320
1368
|
|
|
1321
1369
|
/**
|
|
@@ -1482,10 +1530,6 @@ export class ActorInstance<S, CP, CS, V, I, DB extends AnyDatabaseProvider> {
|
|
|
1482
1530
|
throw new errors.FetchHandlerNotDefined();
|
|
1483
1531
|
}
|
|
1484
1532
|
|
|
1485
|
-
// Track active raw fetch while handler runs
|
|
1486
|
-
this.#activeRawFetchCount++;
|
|
1487
|
-
this.#resetSleepTimer();
|
|
1488
|
-
|
|
1489
1533
|
try {
|
|
1490
1534
|
const response = await this.#config.onFetch(
|
|
1491
1535
|
this.actorContext,
|
|
@@ -1503,12 +1547,6 @@ export class ActorInstance<S, CP, CS, V, I, DB extends AnyDatabaseProvider> {
|
|
|
1503
1547
|
});
|
|
1504
1548
|
throw error;
|
|
1505
1549
|
} finally {
|
|
1506
|
-
// Decrement active raw fetch counter and re-evaluate sleep
|
|
1507
|
-
this.#activeRawFetchCount = Math.max(
|
|
1508
|
-
0,
|
|
1509
|
-
this.#activeRawFetchCount - 1,
|
|
1510
|
-
);
|
|
1511
|
-
this.#resetSleepTimer();
|
|
1512
1550
|
this.#savePersistThrottled();
|
|
1513
1551
|
}
|
|
1514
1552
|
}
|
|
@@ -1534,17 +1572,116 @@ export class ActorInstance<S, CP, CS, V, I, DB extends AnyDatabaseProvider> {
|
|
|
1534
1572
|
this.#activeRawWebSockets.add(websocket);
|
|
1535
1573
|
this.#resetSleepTimer();
|
|
1536
1574
|
|
|
1537
|
-
// Track
|
|
1538
|
-
|
|
1575
|
+
// Track hibernatable WebSockets
|
|
1576
|
+
let rivetRequestId: ArrayBuffer | undefined;
|
|
1577
|
+
let persistedHibernatableWebSocket:
|
|
1578
|
+
| PersistedHibernatableWebSocket
|
|
1579
|
+
| undefined;
|
|
1580
|
+
|
|
1581
|
+
const onSocketOpened = (event: any) => {
|
|
1582
|
+
rivetRequestId = event?.rivetRequestId;
|
|
1583
|
+
|
|
1584
|
+
// Find hibernatable WS
|
|
1585
|
+
if (rivetRequestId) {
|
|
1586
|
+
const rivetRequestIdLocal = rivetRequestId;
|
|
1587
|
+
persistedHibernatableWebSocket =
|
|
1588
|
+
this.#persist.hibernatableWebSocket.find((ws) =>
|
|
1589
|
+
arrayBuffersEqual(
|
|
1590
|
+
ws.requestId,
|
|
1591
|
+
rivetRequestIdLocal,
|
|
1592
|
+
),
|
|
1593
|
+
);
|
|
1594
|
+
|
|
1595
|
+
if (persistedHibernatableWebSocket) {
|
|
1596
|
+
persistedHibernatableWebSocket.lastSeenTimestamp =
|
|
1597
|
+
BigInt(Date.now());
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
this.#rLog.debug({
|
|
1602
|
+
msg: "actor instance onSocketOpened",
|
|
1603
|
+
rivetRequestId,
|
|
1604
|
+
isHibernatable: !!persistedHibernatableWebSocket,
|
|
1605
|
+
hibernationMsgIndex:
|
|
1606
|
+
persistedHibernatableWebSocket?.msgIndex,
|
|
1607
|
+
});
|
|
1608
|
+
};
|
|
1609
|
+
|
|
1610
|
+
const onSocketMessage = (event: any) => {
|
|
1611
|
+
// Update state of hibernatable WS
|
|
1612
|
+
if (persistedHibernatableWebSocket) {
|
|
1613
|
+
persistedHibernatableWebSocket.lastSeenTimestamp = BigInt(
|
|
1614
|
+
Date.now(),
|
|
1615
|
+
);
|
|
1616
|
+
persistedHibernatableWebSocket.msgIndex = BigInt(
|
|
1617
|
+
event.rivetMessageIndex,
|
|
1618
|
+
);
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
this.#rLog.debug({
|
|
1622
|
+
msg: "actor instance onSocketMessage",
|
|
1623
|
+
rivetRequestId,
|
|
1624
|
+
isHibernatable: !!persistedHibernatableWebSocket,
|
|
1625
|
+
hibernationMsgIndex:
|
|
1626
|
+
persistedHibernatableWebSocket?.msgIndex,
|
|
1627
|
+
});
|
|
1628
|
+
};
|
|
1629
|
+
|
|
1630
|
+
const onSocketClosed = (_event: any) => {
|
|
1631
|
+
// Remove hibernatable WS
|
|
1632
|
+
if (rivetRequestId) {
|
|
1633
|
+
const rivetRequestIdLocal = rivetRequestId;
|
|
1634
|
+
const wsIndex =
|
|
1635
|
+
this.#persist.hibernatableWebSocket.findIndex((ws) =>
|
|
1636
|
+
arrayBuffersEqual(
|
|
1637
|
+
ws.requestId,
|
|
1638
|
+
rivetRequestIdLocal,
|
|
1639
|
+
),
|
|
1640
|
+
);
|
|
1641
|
+
|
|
1642
|
+
const removed = this.#persist.hibernatableWebSocket.splice(
|
|
1643
|
+
wsIndex,
|
|
1644
|
+
1,
|
|
1645
|
+
);
|
|
1646
|
+
if (removed.length > 0) {
|
|
1647
|
+
this.#rLog.debug({
|
|
1648
|
+
msg: "removed hibernatable websocket",
|
|
1649
|
+
rivetRequestId,
|
|
1650
|
+
hibernationMsgIndex:
|
|
1651
|
+
persistedHibernatableWebSocket?.msgIndex,
|
|
1652
|
+
});
|
|
1653
|
+
} else {
|
|
1654
|
+
this.#rLog.warn({
|
|
1655
|
+
msg: "could not find hibernatable websocket to remove",
|
|
1656
|
+
rivetRequestId,
|
|
1657
|
+
hibernationMsgIndex:
|
|
1658
|
+
persistedHibernatableWebSocket?.msgIndex,
|
|
1659
|
+
});
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
this.#rLog.debug({
|
|
1664
|
+
msg: "actor instance onSocketMessage",
|
|
1665
|
+
rivetRequestId,
|
|
1666
|
+
isHibernatable: !!persistedHibernatableWebSocket,
|
|
1667
|
+
hibernatableWebSocketCount:
|
|
1668
|
+
this.#persist.hibernatableWebSocket.length,
|
|
1669
|
+
});
|
|
1670
|
+
|
|
1539
1671
|
// Remove listener and socket from tracking
|
|
1540
1672
|
try {
|
|
1673
|
+
websocket.removeEventListener("open", onSocketOpened);
|
|
1674
|
+
websocket.removeEventListener("message", onSocketMessage);
|
|
1541
1675
|
websocket.removeEventListener("close", onSocketClosed);
|
|
1542
1676
|
websocket.removeEventListener("error", onSocketClosed);
|
|
1543
1677
|
} catch {}
|
|
1544
1678
|
this.#activeRawWebSockets.delete(websocket);
|
|
1545
1679
|
this.#resetSleepTimer();
|
|
1546
1680
|
};
|
|
1681
|
+
|
|
1547
1682
|
try {
|
|
1683
|
+
websocket.addEventListener("open", onSocketOpened);
|
|
1684
|
+
websocket.addEventListener("message", onSocketMessage);
|
|
1548
1685
|
websocket.addEventListener("close", onSocketClosed);
|
|
1549
1686
|
websocket.addEventListener("error", onSocketClosed);
|
|
1550
1687
|
} catch {}
|
|
@@ -1746,6 +1883,29 @@ export class ActorInstance<S, CP, CS, V, I, DB extends AnyDatabaseProvider> {
|
|
|
1746
1883
|
}
|
|
1747
1884
|
}
|
|
1748
1885
|
|
|
1886
|
+
/**
|
|
1887
|
+
* Called by router middleware when an HTTP request begins.
|
|
1888
|
+
*/
|
|
1889
|
+
__beginHonoHttpRequest() {
|
|
1890
|
+
this.#activeHonoHttpRequests++;
|
|
1891
|
+
this.#resetSleepTimer();
|
|
1892
|
+
}
|
|
1893
|
+
|
|
1894
|
+
/**
|
|
1895
|
+
* Called by router middleware when an HTTP request ends.
|
|
1896
|
+
*/
|
|
1897
|
+
__endHonoHttpRequest() {
|
|
1898
|
+
this.#activeHonoHttpRequests--;
|
|
1899
|
+
if (this.#activeHonoHttpRequests < 0) {
|
|
1900
|
+
this.#activeHonoHttpRequests = 0;
|
|
1901
|
+
this.#rLog.warn({
|
|
1902
|
+
msg: "active hono requests went below 0, this is a RivetKit bug",
|
|
1903
|
+
...EXTRA_ERROR_LOG,
|
|
1904
|
+
});
|
|
1905
|
+
}
|
|
1906
|
+
this.#resetSleepTimer();
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1749
1909
|
// MARK: Sleep
|
|
1750
1910
|
/**
|
|
1751
1911
|
* Reset timer from the last actor interaction that allows it to be put to sleep.
|
|
@@ -1764,8 +1924,9 @@ export class ActorInstance<S, CP, CS, V, I, DB extends AnyDatabaseProvider> {
|
|
|
1764
1924
|
|
|
1765
1925
|
this.#rLog.debug({
|
|
1766
1926
|
msg: "resetting sleep timer",
|
|
1767
|
-
canSleep,
|
|
1927
|
+
canSleep: CanSleep[canSleep],
|
|
1768
1928
|
existingTimeout: !!this.#sleepTimeout,
|
|
1929
|
+
timeout: this.#config.options.sleepTimeout,
|
|
1769
1930
|
});
|
|
1770
1931
|
|
|
1771
1932
|
if (this.#sleepTimeout) {
|
|
@@ -1776,64 +1937,84 @@ export class ActorInstance<S, CP, CS, V, I, DB extends AnyDatabaseProvider> {
|
|
|
1776
1937
|
// Don't set a new timer if already sleeping
|
|
1777
1938
|
if (this.#sleepCalled) return;
|
|
1778
1939
|
|
|
1779
|
-
if (canSleep) {
|
|
1940
|
+
if (canSleep === CanSleep.Yes) {
|
|
1780
1941
|
this.#sleepTimeout = setTimeout(() => {
|
|
1781
|
-
this.
|
|
1782
|
-
this.#rLog.error({
|
|
1783
|
-
msg: "error during sleep",
|
|
1784
|
-
error: stringifyError(error),
|
|
1785
|
-
});
|
|
1786
|
-
});
|
|
1942
|
+
this._startSleep();
|
|
1787
1943
|
}, this.#config.options.sleepTimeout);
|
|
1788
1944
|
}
|
|
1789
1945
|
}
|
|
1790
1946
|
|
|
1791
1947
|
/** If this actor can be put in a sleeping state. */
|
|
1792
|
-
#canSleep():
|
|
1793
|
-
if (!this.#ready) return
|
|
1948
|
+
#canSleep(): CanSleep {
|
|
1949
|
+
if (!this.#ready) return CanSleep.NotReady;
|
|
1950
|
+
|
|
1951
|
+
// Do not sleep if Hono HTTP requests are in-flight
|
|
1952
|
+
if (this.#activeHonoHttpRequests > 0)
|
|
1953
|
+
return CanSleep.ActiveHonoHttpRequests;
|
|
1954
|
+
|
|
1955
|
+
// TODO: When WS hibernation is ready, update this to only count non-hibernatable websockets
|
|
1956
|
+
// Do not sleep if there are raw websockets open
|
|
1957
|
+
if (this.#activeRawWebSockets.size > 0)
|
|
1958
|
+
return CanSleep.ActiveRawWebSockets;
|
|
1794
1959
|
|
|
1795
1960
|
// Check for active conns. This will also cover active actions, since all actions have a connection.
|
|
1796
1961
|
for (const conn of this.#connections.values()) {
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
// Do not sleep if raw fetches are in-flight
|
|
1801
|
-
if (this.#activeRawFetchCount > 0) return false;
|
|
1962
|
+
// TODO: Enable this when hibernation is implemented. We're waiting on support for Guard to not auto-wake the actor if it sleeps.
|
|
1963
|
+
// if (conn.status === "connected" && !conn.isHibernatable)
|
|
1964
|
+
// return false;
|
|
1802
1965
|
|
|
1803
|
-
|
|
1804
|
-
|
|
1966
|
+
if (conn.status === "connected") return CanSleep.ActiveConns;
|
|
1967
|
+
}
|
|
1805
1968
|
|
|
1806
|
-
return
|
|
1969
|
+
return CanSleep.Yes;
|
|
1807
1970
|
}
|
|
1808
1971
|
|
|
1809
|
-
/**
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1972
|
+
/**
|
|
1973
|
+
* Puts an actor to sleep. This should just start the sleep sequence, most shutdown logic should be in _stop (which is called by the ActorDriver when sleeping).
|
|
1974
|
+
*
|
|
1975
|
+
* For the engine, this will:
|
|
1976
|
+
* 1. Publish EventActorIntent with ActorIntentSleep (via driver.startSleep)
|
|
1977
|
+
* 2. Engine runner will wait for CommandStopActor
|
|
1978
|
+
* 3. Engine runner will call _onStop and wait for it to finish
|
|
1979
|
+
* 4. Engine runner will publish EventActorStateUpdate with ActorStateSTop
|
|
1980
|
+
**/
|
|
1981
|
+
_startSleep() {
|
|
1982
|
+
// IMPORTANT: #sleepCalled should have no effect on the actor's
|
|
1983
|
+
// behavior aside from preventing calling _startSleep twice. Wait for
|
|
1984
|
+
// `_onStop` before putting in a stopping state.
|
|
1818
1985
|
if (this.#sleepCalled) {
|
|
1819
1986
|
this.#rLog.warn({ msg: "already sleeping actor" });
|
|
1820
1987
|
return;
|
|
1821
1988
|
}
|
|
1822
1989
|
this.#sleepCalled = true;
|
|
1823
1990
|
|
|
1991
|
+
// NOTE: Publishes ActorIntentSleep
|
|
1992
|
+
const sleep = this.#actorDriver.startSleep?.bind(
|
|
1993
|
+
this.#actorDriver,
|
|
1994
|
+
this.#actorId,
|
|
1995
|
+
);
|
|
1996
|
+
invariant(this.#sleepingSupported, "sleeping not supported");
|
|
1997
|
+
invariant(sleep, "no sleep on driver");
|
|
1998
|
+
|
|
1824
1999
|
this.#rLog.info({ msg: "actor sleeping" });
|
|
1825
2000
|
|
|
1826
2001
|
// Schedule sleep to happen on the next tick. This allows for any action that calls _sleep to complete.
|
|
1827
|
-
setImmediate(
|
|
2002
|
+
setImmediate(() => {
|
|
1828
2003
|
// The actor driver should call stop when ready to stop
|
|
1829
2004
|
//
|
|
1830
2005
|
// This will call _stop once Pegboard responds with the new status
|
|
1831
|
-
|
|
2006
|
+
sleep();
|
|
1832
2007
|
});
|
|
1833
2008
|
}
|
|
1834
2009
|
|
|
1835
2010
|
// MARK: Stop
|
|
1836
|
-
|
|
2011
|
+
/**
|
|
2012
|
+
* For the engine:
|
|
2013
|
+
* 1. Engine runner receives CommandStopActor
|
|
2014
|
+
* 2. Engine runner calls _onStop and waits for it to finish
|
|
2015
|
+
* 3. Engine runner publishes EventActorStateUpdate with ActorStateSTop
|
|
2016
|
+
*/
|
|
2017
|
+
async _onStop() {
|
|
1837
2018
|
if (this.#stopCalled) {
|
|
1838
2019
|
this.#rLog.warn({ msg: "already stopping actor" });
|
|
1839
2020
|
return;
|
|
@@ -1980,6 +2161,11 @@ export class ActorInstance<S, CP, CS, V, I, DB extends AnyDatabaseProvider> {
|
|
|
1980
2161
|
},
|
|
1981
2162
|
},
|
|
1982
2163
|
})),
|
|
2164
|
+
hibernatableWebSocket: persist.hibernatableWebSocket.map((ws) => ({
|
|
2165
|
+
requestId: ws.requestId,
|
|
2166
|
+
lastSeenTimestamp: ws.lastSeenTimestamp,
|
|
2167
|
+
msgIndex: ws.msgIndex,
|
|
2168
|
+
})),
|
|
1983
2169
|
};
|
|
1984
2170
|
}
|
|
1985
2171
|
|
|
@@ -2012,6 +2198,11 @@ export class ActorInstance<S, CP, CS, V, I, DB extends AnyDatabaseProvider> {
|
|
|
2012
2198
|
},
|
|
2013
2199
|
},
|
|
2014
2200
|
})),
|
|
2201
|
+
hibernatableWebSocket: bareData.hibernatableWebSocket.map((ws) => ({
|
|
2202
|
+
requestId: ws.requestId,
|
|
2203
|
+
lastSeenTimestamp: ws.lastSeenTimestamp,
|
|
2204
|
+
msgIndex: ws.msgIndex,
|
|
2205
|
+
})),
|
|
2015
2206
|
};
|
|
2016
2207
|
}
|
|
2017
2208
|
}
|
package/src/actor/persisted.ts
CHANGED
|
@@ -5,6 +5,7 @@ export interface PersistedActor<S, CP, CS, I> {
|
|
|
5
5
|
state: S;
|
|
6
6
|
connections: PersistedConn<CP, CS>[];
|
|
7
7
|
scheduledEvents: PersistedScheduleEvent[];
|
|
8
|
+
hibernatableWebSocket: PersistedHibernatableWebSocket[];
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
/** Object representing connection that gets persisted to storage. */
|
|
@@ -37,3 +38,9 @@ export interface PersistedScheduleEvent {
|
|
|
37
38
|
timestamp: number;
|
|
38
39
|
kind: PersistedScheduleEventKind;
|
|
39
40
|
}
|
|
41
|
+
|
|
42
|
+
export interface PersistedHibernatableWebSocket {
|
|
43
|
+
requestId: ArrayBuffer;
|
|
44
|
+
lastSeenTimestamp: bigint;
|
|
45
|
+
msgIndex: bigint;
|
|
46
|
+
}
|