rivetkit 2.3.24-rc.1 → 2.3.24
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/browser/client.d.ts +3 -2
- package/dist/browser/client.js +36 -17
- package/dist/browser/client.js.map +1 -1
- package/dist/browser/inspector/client.js +1 -1
- package/dist/browser/inspector/client.js.map +1 -1
- package/dist/tsup/agent-os/index.cjs +1 -1
- package/dist/tsup/agent-os/index.cjs.map +1 -1
- package/dist/tsup/agent-os/index.d.cts +0 -2
- package/dist/tsup/agent-os/index.d.ts +0 -2
- package/dist/tsup/agent-os/index.js +1 -1
- package/dist/tsup/agent-os/index.js.map +1 -1
- package/dist/tsup/{chunk-6HW3TU7Q.js → chunk-45WBFRBN.js} +2 -2
- package/dist/tsup/{chunk-64CRWLOX.js → chunk-7OEOLF5T.js} +3 -3
- package/dist/tsup/{chunk-LTGBHN5Z.js → chunk-CSOHAAZK.js} +1 -1
- package/dist/tsup/chunk-CSOHAAZK.js.map +1 -0
- package/dist/tsup/{chunk-CE6NBGOL.cjs → chunk-JOJ42ZLR.cjs} +3 -3
- package/dist/tsup/{chunk-CE6NBGOL.cjs.map → chunk-JOJ42ZLR.cjs.map} +1 -1
- package/dist/tsup/{chunk-ERBNWARB.cjs → chunk-L4AZXL2Q.cjs} +4 -4
- package/dist/tsup/{chunk-ERBNWARB.cjs.map → chunk-L4AZXL2Q.cjs.map} +1 -1
- package/dist/tsup/{chunk-YVVFOKEP.cjs → chunk-NNM2X3ER.cjs} +2 -2
- package/dist/tsup/chunk-NNM2X3ER.cjs.map +1 -0
- package/dist/tsup/{chunk-OB5JLDBM.js → chunk-NSNBWLCG.js} +2 -2
- package/dist/tsup/{chunk-NGJWQMUU.cjs → chunk-NWCAIWHL.cjs} +1 -1
- package/dist/tsup/chunk-NWCAIWHL.cjs.map +1 -0
- package/dist/tsup/{chunk-M5J3PXNU.js → chunk-QMR2J7PT.js} +2 -2
- package/dist/tsup/{chunk-QQ454DVR.js → chunk-QZQE75U2.js} +2 -2
- package/dist/tsup/{chunk-B6WTPGOR.js → chunk-SW22GCYF.js} +30 -11
- package/dist/tsup/chunk-SW22GCYF.js.map +1 -0
- package/dist/tsup/{chunk-PFHGTLF2.cjs → chunk-TMMZAGZL.cjs} +10 -10
- package/dist/tsup/{chunk-PFHGTLF2.cjs.map → chunk-TMMZAGZL.cjs.map} +1 -1
- package/dist/tsup/{chunk-R3UI7MO3.cjs → chunk-TQNVNKDB.cjs} +165 -146
- package/dist/tsup/chunk-TQNVNKDB.cjs.map +1 -0
- package/dist/tsup/{chunk-JKDAOE4T.js → chunk-UIPQ7RI4.js} +4 -4
- package/dist/tsup/{chunk-SWQCSORE.cjs → chunk-WCSFWDKH.cjs} +3 -3
- package/dist/tsup/{chunk-SWQCSORE.cjs.map → chunk-WCSFWDKH.cjs.map} +1 -1
- package/dist/tsup/{chunk-DAC73W53.cjs → chunk-WTZGJFD4.cjs} +7 -7
- package/dist/tsup/{chunk-DAC73W53.cjs.map → chunk-WTZGJFD4.cjs.map} +1 -1
- package/dist/tsup/{chunk-JJCUSUWO.js → chunk-YH2CAWLD.js} +2 -2
- package/dist/tsup/chunk-YH2CAWLD.js.map +1 -0
- package/dist/tsup/{chunk-JMO6AVUC.cjs → chunk-ZCHJJ2FZ.cjs} +3 -3
- package/dist/tsup/{chunk-JMO6AVUC.cjs.map → chunk-ZCHJJ2FZ.cjs.map} +1 -1
- package/dist/tsup/client/mod.cjs +5 -5
- package/dist/tsup/client/mod.d.cts +5 -2
- package/dist/tsup/client/mod.d.ts +5 -2
- package/dist/tsup/client/mod.js +4 -4
- package/dist/tsup/common/log.cjs +2 -2
- package/dist/tsup/common/log.js +1 -1
- package/dist/tsup/common/websocket.cjs +3 -3
- package/dist/tsup/common/websocket.js +2 -2
- package/dist/tsup/{config-RV6gIa2L.d.ts → config-Cv6pjCt-.d.ts} +0 -4
- package/dist/tsup/{config-CuKzfH58.d.cts → config-osdJqQwX.d.cts} +0 -4
- package/dist/tsup/{context-DoMKZBQA.d.ts → context-CoVbKBuk.d.ts} +1 -1
- package/dist/tsup/{context-DPCxyjax.d.cts → context-DpjrM7n7.d.cts} +1 -1
- package/dist/tsup/db/drizzle.cjs +2 -2
- package/dist/tsup/db/drizzle.js +1 -1
- package/dist/tsup/db/mod.cjs +3 -3
- package/dist/tsup/db/mod.js +2 -2
- package/dist/tsup/dynamic/mod.cjs +2 -2
- package/dist/tsup/dynamic/mod.d.cts +1 -1
- package/dist/tsup/dynamic/mod.d.ts +1 -1
- package/dist/tsup/dynamic/mod.js +1 -1
- package/dist/tsup/inspector/mod.cjs +5 -5
- package/dist/tsup/inspector/mod.js +4 -4
- package/dist/tsup/inspector/workflow.cjs +4 -4
- package/dist/tsup/inspector/workflow.js +3 -3
- package/dist/tsup/inspector-tab/mod.cjs +2 -2
- package/dist/tsup/inspector-tab/mod.d.cts +2 -2
- package/dist/tsup/inspector-tab/mod.d.ts +2 -2
- package/dist/tsup/inspector-tab/mod.js +1 -1
- package/dist/tsup/mod.cjs +188 -416
- package/dist/tsup/mod.cjs.map +1 -1
- package/dist/tsup/mod.d.cts +3 -3
- package/dist/tsup/mod.d.ts +3 -3
- package/dist/tsup/mod.js +90 -318
- package/dist/tsup/mod.js.map +1 -1
- package/dist/tsup/test/mod.cjs +6 -6
- package/dist/tsup/test/mod.d.cts +1 -1
- package/dist/tsup/test/mod.d.ts +1 -1
- package/dist/tsup/test/mod.js +4 -4
- package/dist/tsup/utils.cjs +2 -2
- package/dist/tsup/utils.js +1 -1
- package/dist/tsup/workflow/mod.cjs +21 -69
- package/dist/tsup/workflow/mod.cjs.map +1 -1
- package/dist/tsup/workflow/mod.d.cts +3 -3
- package/dist/tsup/workflow/mod.d.ts +3 -3
- package/dist/tsup/workflow/mod.js +16 -64
- package/dist/tsup/workflow/mod.js.map +1 -1
- package/package.json +9 -9
- package/src/actor/config.ts +0 -2
- package/src/client/config.ts +3 -0
- package/src/client/raw-utils.ts +7 -2
- package/src/common/fetch-like.ts +14 -0
- package/src/engine-client/actor-http-client.ts +16 -6
- package/src/registry/napi-runtime.ts +0 -8
- package/src/registry/native.ts +65 -332
- package/src/registry/run-handler-coordinator.test.ts +0 -102
- package/src/registry/run-handler-coordinator.ts +17 -84
- package/src/registry/runtime.ts +0 -2
- package/src/registry/wasm-runtime.ts +0 -8
- package/src/workflow/context.ts +0 -20
- package/src/workflow/driver.ts +1 -25
- package/src/workflow/mod.ts +12 -46
- package/dist/tsup/chunk-B6WTPGOR.js.map +0 -1
- package/dist/tsup/chunk-JJCUSUWO.js.map +0 -1
- package/dist/tsup/chunk-LTGBHN5Z.js.map +0 -1
- package/dist/tsup/chunk-NGJWQMUU.cjs.map +0 -1
- package/dist/tsup/chunk-R3UI7MO3.cjs.map +0 -1
- package/dist/tsup/chunk-YVVFOKEP.cjs.map +0 -1
- package/src/registry/lost-callbacks.test.ts +0 -64
- /package/dist/tsup/{chunk-6HW3TU7Q.js.map → chunk-45WBFRBN.js.map} +0 -0
- /package/dist/tsup/{chunk-64CRWLOX.js.map → chunk-7OEOLF5T.js.map} +0 -0
- /package/dist/tsup/{chunk-OB5JLDBM.js.map → chunk-NSNBWLCG.js.map} +0 -0
- /package/dist/tsup/{chunk-M5J3PXNU.js.map → chunk-QMR2J7PT.js.map} +0 -0
- /package/dist/tsup/{chunk-QQ454DVR.js.map → chunk-QZQE75U2.js.map} +0 -0
- /package/dist/tsup/{chunk-JKDAOE4T.js.map → chunk-UIPQ7RI4.js.map} +0 -0
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { describe, expect, test, vi } from "vitest";
|
|
2
2
|
import { defineRunHandler, type RunControl } from "@/actor/config";
|
|
3
|
-
import { workflow } from "@/workflow/mod";
|
|
4
3
|
import { RunHandlerCoordinator } from "./run-handler-coordinator";
|
|
5
4
|
|
|
6
5
|
class Deferred<T = void> {
|
|
@@ -52,107 +51,6 @@ function createCoordinator() {
|
|
|
52
51
|
}
|
|
53
52
|
|
|
54
53
|
describe("RunHandlerCoordinator", () => {
|
|
55
|
-
test("workflow inspector registrations stay per generation", () => {
|
|
56
|
-
const coordinator = new RunHandlerCoordinator(workflow(async () => {}));
|
|
57
|
-
const restart = vi.fn();
|
|
58
|
-
|
|
59
|
-
const oldInspector = coordinator.getInspector(
|
|
60
|
-
"actor-1",
|
|
61
|
-
restart,
|
|
62
|
-
1,
|
|
63
|
-
)?.workflow;
|
|
64
|
-
const newInspector = coordinator.getInspector(
|
|
65
|
-
"actor-1",
|
|
66
|
-
restart,
|
|
67
|
-
2,
|
|
68
|
-
)?.workflow;
|
|
69
|
-
expect(oldInspector).toBeDefined();
|
|
70
|
-
expect(newInspector).toBeDefined();
|
|
71
|
-
expect(newInspector).not.toBe(oldInspector);
|
|
72
|
-
|
|
73
|
-
// A superseded generation cannot re-initialize or rebind the current generation's
|
|
74
|
-
// workflow registration.
|
|
75
|
-
expect(coordinator.getInspector("actor-1", restart, 1)).toBeUndefined();
|
|
76
|
-
expect(coordinator.getInspector("actor-1", restart, 2)?.workflow).toBe(
|
|
77
|
-
newInspector,
|
|
78
|
-
);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
test("a superseded generation's run is rejected before it touches the inspector", async () => {
|
|
82
|
-
const coordinator = new RunHandlerCoordinator(workflow(async () => {}));
|
|
83
|
-
const restart = vi.fn();
|
|
84
|
-
const newInspector = coordinator.getInspector(
|
|
85
|
-
"actor-1",
|
|
86
|
-
restart,
|
|
87
|
-
2,
|
|
88
|
-
)?.workflow;
|
|
89
|
-
|
|
90
|
-
await expect(
|
|
91
|
-
coordinator.run("actor-1", restart, async () => {}, 1),
|
|
92
|
-
).resolves.toBe("closed");
|
|
93
|
-
expect(coordinator.getInspector("actor-1", restart, 2)?.workflow).toBe(
|
|
94
|
-
newInspector,
|
|
95
|
-
);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
test("a newer generation does not wait on an older generation's stuck run", async () => {
|
|
99
|
-
const coordinator = new RunHandlerCoordinator(
|
|
100
|
-
defineRunHandler(async () => {}, {
|
|
101
|
-
inspectorKind: "workflow",
|
|
102
|
-
createInspector: () => ({
|
|
103
|
-
inspector: {
|
|
104
|
-
workflow: {
|
|
105
|
-
getHistory: () => null,
|
|
106
|
-
getState: async () => null,
|
|
107
|
-
onHistoryUpdated: () => () => {},
|
|
108
|
-
replayFromStep: async () => null,
|
|
109
|
-
},
|
|
110
|
-
},
|
|
111
|
-
}),
|
|
112
|
-
}),
|
|
113
|
-
);
|
|
114
|
-
const oldRun = new Deferred();
|
|
115
|
-
const oldOutcome = coordinator.run(
|
|
116
|
-
"actor-1",
|
|
117
|
-
() => {},
|
|
118
|
-
() => oldRun.promise,
|
|
119
|
-
1,
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
// The lost generation's JS callback cannot be cancelled, so it stays active.
|
|
123
|
-
await expect(
|
|
124
|
-
coordinator.run(
|
|
125
|
-
"actor-1",
|
|
126
|
-
() => {},
|
|
127
|
-
async () => {},
|
|
128
|
-
2,
|
|
129
|
-
),
|
|
130
|
-
).resolves.toBe("ran");
|
|
131
|
-
|
|
132
|
-
// The superseded generation cannot start new runs, and its late destroy leaves the
|
|
133
|
-
// newer generation's state in place.
|
|
134
|
-
await expect(
|
|
135
|
-
coordinator.run(
|
|
136
|
-
"actor-1",
|
|
137
|
-
() => {},
|
|
138
|
-
async () => {},
|
|
139
|
-
1,
|
|
140
|
-
),
|
|
141
|
-
).resolves.toBe("closed");
|
|
142
|
-
coordinator.destroy("actor-1", 1);
|
|
143
|
-
await expect(
|
|
144
|
-
coordinator.run(
|
|
145
|
-
"actor-1",
|
|
146
|
-
() => {},
|
|
147
|
-
async () => {},
|
|
148
|
-
2,
|
|
149
|
-
),
|
|
150
|
-
).resolves.toBe("ran");
|
|
151
|
-
|
|
152
|
-
oldRun.resolve();
|
|
153
|
-
await expect(oldOutcome).resolves.toBe("ran");
|
|
154
|
-
});
|
|
155
|
-
|
|
156
54
|
test("fails explicitly when a JavaScript factory omits required workflow controls", async () => {
|
|
157
55
|
const coordinator = new RunHandlerCoordinator(
|
|
158
56
|
defineRunHandler(async () => {}, {
|
|
@@ -11,8 +11,6 @@ import { RivetError } from "@/actor/errors";
|
|
|
11
11
|
type RunHandler = ((...args: any[]) => any) | { run: (...args: any[]) => any };
|
|
12
12
|
|
|
13
13
|
interface ActorRunState {
|
|
14
|
-
/** Actor generation that owns this state, when the runtime reports one. */
|
|
15
|
-
actorGeneration: number | undefined;
|
|
16
14
|
active: boolean;
|
|
17
15
|
closed: boolean;
|
|
18
16
|
exclusive: boolean;
|
|
@@ -48,32 +46,6 @@ function notifyStateChanged(state: ActorRunState): void {
|
|
|
48
46
|
}
|
|
49
47
|
}
|
|
50
48
|
|
|
51
|
-
function createRunState(actorGeneration: number | undefined): ActorRunState {
|
|
52
|
-
return {
|
|
53
|
-
actorGeneration,
|
|
54
|
-
active: false,
|
|
55
|
-
closed: false,
|
|
56
|
-
exclusive: false,
|
|
57
|
-
exclusiveGeneration: 0,
|
|
58
|
-
inspectorInitialized: false,
|
|
59
|
-
queued: 0,
|
|
60
|
-
waiters: new Set(),
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Closes run state. The inspector disposer is user code, so it is skipped for a generation
|
|
66
|
-
* that was declared lost.
|
|
67
|
-
*/
|
|
68
|
-
function closeRunState(state: ActorRunState, disposeInspector = true): void {
|
|
69
|
-
state.closed = true;
|
|
70
|
-
if (disposeInspector) {
|
|
71
|
-
state.inspector?.dispose?.();
|
|
72
|
-
}
|
|
73
|
-
state.inspector = undefined;
|
|
74
|
-
notifyStateChanged(state);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
49
|
function waitForStateChange(state: ActorRunState): Promise<void> {
|
|
78
50
|
return new Promise((resolve) => {
|
|
79
51
|
state.waiters.add(resolve);
|
|
@@ -97,14 +69,8 @@ export class RunHandlerCoordinator {
|
|
|
97
69
|
actorId: string,
|
|
98
70
|
restart: () => void | Promise<void>,
|
|
99
71
|
callback: () => void | Promise<void>,
|
|
100
|
-
actorGeneration?: number,
|
|
101
72
|
): Promise<RunHandlerOutcome> {
|
|
102
|
-
const state = this.#getOrCreate(actorId
|
|
103
|
-
// Closed state belongs to a superseded generation. It must not initialize an inspector,
|
|
104
|
-
// which would rebind the workflow registration of the current generation.
|
|
105
|
-
if (state.closed) {
|
|
106
|
-
return "closed";
|
|
107
|
-
}
|
|
73
|
+
const state = this.#getOrCreate(actorId);
|
|
108
74
|
state.restart = restart;
|
|
109
75
|
this.#initializeInspector(actorId, state);
|
|
110
76
|
state.queued += 1;
|
|
@@ -144,37 +110,21 @@ export class RunHandlerCoordinator {
|
|
|
144
110
|
getInspector(
|
|
145
111
|
actorId: string,
|
|
146
112
|
restart: () => void | Promise<void>,
|
|
147
|
-
actorGeneration?: number,
|
|
148
113
|
): RunInspectorConfig | undefined {
|
|
149
|
-
const state = this.#getOrCreate(actorId
|
|
150
|
-
if (state.closed) {
|
|
151
|
-
return undefined;
|
|
152
|
-
}
|
|
114
|
+
const state = this.#getOrCreate(actorId);
|
|
153
115
|
state.restart = restart;
|
|
154
116
|
this.#initializeInspector(actorId, state);
|
|
155
117
|
return state.inspector?.inspector;
|
|
156
118
|
}
|
|
157
119
|
|
|
158
|
-
|
|
159
|
-
* Closes the run state for `actorId`. With `actorGeneration`, only state owned by that
|
|
160
|
-
* generation is closed, so an older generation's late cleanup cannot close a newer one.
|
|
161
|
-
*/
|
|
162
|
-
destroy(
|
|
163
|
-
actorId: string,
|
|
164
|
-
actorGeneration?: number,
|
|
165
|
-
options: { disposeInspector?: boolean } = {},
|
|
166
|
-
): void {
|
|
120
|
+
destroy(actorId: string): void {
|
|
167
121
|
const state = this.#states.get(actorId);
|
|
168
122
|
if (!state) return;
|
|
169
|
-
if (
|
|
170
|
-
actorGeneration !== undefined &&
|
|
171
|
-
state.actorGeneration !== undefined &&
|
|
172
|
-
state.actorGeneration !== actorGeneration
|
|
173
|
-
) {
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
123
|
|
|
177
|
-
|
|
124
|
+
state.closed = true;
|
|
125
|
+
state.inspector?.dispose?.();
|
|
126
|
+
state.inspector = undefined;
|
|
127
|
+
notifyStateChanged(state);
|
|
178
128
|
this.#states.delete(actorId);
|
|
179
129
|
}
|
|
180
130
|
|
|
@@ -223,34 +173,18 @@ export class RunHandlerCoordinator {
|
|
|
223
173
|
};
|
|
224
174
|
}
|
|
225
175
|
|
|
226
|
-
#getOrCreate(
|
|
227
|
-
actorId: string,
|
|
228
|
-
actorGeneration: number | undefined,
|
|
229
|
-
): ActorRunState {
|
|
176
|
+
#getOrCreate(actorId: string): ActorRunState {
|
|
230
177
|
let state = this.#states.get(actorId);
|
|
231
|
-
if (
|
|
232
|
-
state &&
|
|
233
|
-
actorGeneration !== undefined &&
|
|
234
|
-
state.actorGeneration !== undefined &&
|
|
235
|
-
state.actorGeneration !== actorGeneration
|
|
236
|
-
) {
|
|
237
|
-
if (actorGeneration < state.actorGeneration) {
|
|
238
|
-
// A superseded generation gets closed, detached state so it cannot run or
|
|
239
|
-
// block the current generation.
|
|
240
|
-
const detached = createRunState(actorGeneration);
|
|
241
|
-
detached.closed = true;
|
|
242
|
-
return detached;
|
|
243
|
-
}
|
|
244
|
-
// A newer generation replaces state left by an older one, which may still be
|
|
245
|
-
// marked active because its JS callback cannot be cancelled. That generation did not
|
|
246
|
-
// clean up after itself, which only happens when it was lost, so its user inspector
|
|
247
|
-
// disposer is not called.
|
|
248
|
-
closeRunState(state, false);
|
|
249
|
-
this.#states.delete(actorId);
|
|
250
|
-
state = undefined;
|
|
251
|
-
}
|
|
252
178
|
if (!state) {
|
|
253
|
-
state =
|
|
179
|
+
state = {
|
|
180
|
+
active: false,
|
|
181
|
+
closed: false,
|
|
182
|
+
exclusive: false,
|
|
183
|
+
exclusiveGeneration: 0,
|
|
184
|
+
inspectorInitialized: false,
|
|
185
|
+
queued: 0,
|
|
186
|
+
waiters: new Set(),
|
|
187
|
+
};
|
|
254
188
|
this.#states.set(actorId, state);
|
|
255
189
|
}
|
|
256
190
|
return state;
|
|
@@ -260,7 +194,6 @@ export class RunHandlerCoordinator {
|
|
|
260
194
|
if (state.inspectorInitialized) return;
|
|
261
195
|
const inspector = createRunInspector(this.#run, {
|
|
262
196
|
actorId,
|
|
263
|
-
actorGeneration: state.actorGeneration,
|
|
264
197
|
control: this.#control(actorId, state),
|
|
265
198
|
});
|
|
266
199
|
if (this.inspectorKind === "workflow") {
|
package/src/registry/runtime.ts
CHANGED
|
@@ -583,8 +583,6 @@ export interface CoreRuntime {
|
|
|
583
583
|
writes: RuntimeWorkflowKvWrite[],
|
|
584
584
|
): Promise<void>;
|
|
585
585
|
actorId(ctx: ActorContextHandle): string;
|
|
586
|
-
actorGeneration(ctx: ActorContextHandle): number | undefined;
|
|
587
|
-
actorIsLost(ctx: ActorContextHandle): boolean;
|
|
588
586
|
/**
|
|
589
587
|
* Runs one actor callback with `ctx` as the current invocation: operations
|
|
590
588
|
* on retained handles for the same actor resolve to it, and its Core span
|
|
@@ -538,14 +538,6 @@ export class WasmCoreRuntime implements CoreRuntime {
|
|
|
538
538
|
return callHandle(asWasmActorContext(ctx), "actorId");
|
|
539
539
|
}
|
|
540
540
|
|
|
541
|
-
actorGeneration(ctx: ActorContextHandle): number | undefined {
|
|
542
|
-
return callHandle(asWasmActorContext(ctx), "generation") ?? undefined;
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
actorIsLost(ctx: ActorContextHandle): boolean {
|
|
546
|
-
return callHandle(asWasmActorContext(ctx), "isLost");
|
|
547
|
-
}
|
|
548
|
-
|
|
549
541
|
runWithActorInvocationContext<T>(
|
|
550
542
|
_ctx: ActorContextHandle,
|
|
551
543
|
run: () => T,
|
package/src/workflow/context.ts
CHANGED
|
@@ -12,7 +12,6 @@ import type {
|
|
|
12
12
|
WorkflowContextInterface,
|
|
13
13
|
WorkflowQueueMessage,
|
|
14
14
|
} from "@rivetkit/workflow-engine";
|
|
15
|
-
import { EvictedError } from "@rivetkit/workflow-engine";
|
|
16
15
|
import type {
|
|
17
16
|
QueueFilterName,
|
|
18
17
|
QueueNextBatchOptions,
|
|
@@ -20,7 +19,6 @@ import type {
|
|
|
20
19
|
QueueResultMessageForName,
|
|
21
20
|
RunContext,
|
|
22
21
|
} from "@/actor/config";
|
|
23
|
-
import { ACTOR_CONTEXT_INTERNAL_SYMBOL } from "@/actor/config";
|
|
24
22
|
import type {
|
|
25
23
|
AnyActorDefinition,
|
|
26
24
|
BaseActorDefinition,
|
|
@@ -284,20 +282,6 @@ export type WorkflowBranchConfig<
|
|
|
284
282
|
// never appears on the public surface.
|
|
285
283
|
const DEACTIVATE_STEP = Symbol("workflow.step.deactivate");
|
|
286
284
|
|
|
287
|
-
/**
|
|
288
|
-
* Stops the workflow before it starts more user code once its actor generation was declared
|
|
289
|
-
* lost. The engine treats the eviction like a sleep and replays from history in the next
|
|
290
|
-
* generation.
|
|
291
|
-
*/
|
|
292
|
-
export function throwIfGenerationLost(runCtx: unknown): void {
|
|
293
|
-
const actor = (
|
|
294
|
-
runCtx as { [ACTOR_CONTEXT_INTERNAL_SYMBOL]?: { isLost?: boolean } }
|
|
295
|
-
)[ACTOR_CONTEXT_INTERNAL_SYMBOL];
|
|
296
|
-
if (actor?.isLost === true) {
|
|
297
|
-
throw new EvictedError();
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
285
|
/**
|
|
302
286
|
* The context handed to a workflow step (`step` / `tryStep` callbacks). This is
|
|
303
287
|
* the only scope where actor data (state, vars, db, client) and side effects
|
|
@@ -1010,7 +994,6 @@ export class WorkflowContext<
|
|
|
1010
994
|
TQueues
|
|
1011
995
|
>(this.#runCtx, () => this.#markGuardTriggered());
|
|
1012
996
|
|
|
1013
|
-
throwIfGenerationLost(this.#runCtx);
|
|
1014
997
|
try {
|
|
1015
998
|
return await run(stepCtx);
|
|
1016
999
|
} finally {
|
|
@@ -1061,7 +1044,6 @@ export class WorkflowContext<
|
|
|
1061
1044
|
TEvents,
|
|
1062
1045
|
TQueues
|
|
1063
1046
|
>(this.#runCtx, () => this.#markGuardTriggered());
|
|
1064
|
-
throwIfGenerationLost(this.#runCtx);
|
|
1065
1047
|
try {
|
|
1066
1048
|
await rollback(stepCtx, output);
|
|
1067
1049
|
} finally {
|
|
@@ -1115,8 +1097,6 @@ export class WorkflowContext<
|
|
|
1115
1097
|
TEvents,
|
|
1116
1098
|
TQueues
|
|
1117
1099
|
> {
|
|
1118
|
-
// Every try, loop, join, and race body runs through a child context.
|
|
1119
|
-
throwIfGenerationLost(this.#runCtx);
|
|
1120
1100
|
return new WorkflowContext(ctx, this.#runCtx);
|
|
1121
1101
|
}
|
|
1122
1102
|
}
|
package/src/workflow/driver.ts
CHANGED
|
@@ -10,7 +10,6 @@ import type { RunContext } from "@/actor/config";
|
|
|
10
10
|
import type { AnyStaticActorInstance } from "@/actor/definition";
|
|
11
11
|
import { makeWorkflowKey, workflowStoragePrefix } from "@/actor/keys";
|
|
12
12
|
import type { RawAccess } from "@/common/database/config";
|
|
13
|
-
import { throwIfGenerationLost } from "./context";
|
|
14
13
|
|
|
15
14
|
const WORKFLOW_STORAGE_PREFIX = workflowStoragePrefix();
|
|
16
15
|
// Keep workflow flushes below depot's 320-dirty-page commit ceiling. The
|
|
@@ -70,26 +69,6 @@ function normalizeSqlBlob(value: unknown): Uint8Array {
|
|
|
70
69
|
throw new Error("workflow sqlite value was not a blob");
|
|
71
70
|
}
|
|
72
71
|
|
|
73
|
-
function guardRawAccessAgainstLost(
|
|
74
|
-
access: RawAccess,
|
|
75
|
-
runCtx: RunContext<any, any, any, any, any, any, any, any> | undefined,
|
|
76
|
-
): RawAccess {
|
|
77
|
-
return {
|
|
78
|
-
...access,
|
|
79
|
-
execute: ((...args: Parameters<RawAccess["execute"]>) => {
|
|
80
|
-
throwIfGenerationLost(runCtx);
|
|
81
|
-
return access.execute(...args);
|
|
82
|
-
}) as RawAccess["execute"],
|
|
83
|
-
transaction: (callback, options) => {
|
|
84
|
-
throwIfGenerationLost(runCtx);
|
|
85
|
-
return access.transaction(
|
|
86
|
-
(tx) => callback(guardRawAccessAgainstLost(tx, runCtx)),
|
|
87
|
-
options,
|
|
88
|
-
);
|
|
89
|
-
},
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
|
|
93
72
|
function runtimeDbFromContext(
|
|
94
73
|
runCtx?: RunContext<any, any, any, any, any, any, any, any>,
|
|
95
74
|
): RawAccess | undefined {
|
|
@@ -100,10 +79,7 @@ function runtimeDbFromContext(
|
|
|
100
79
|
"execute" in db &&
|
|
101
80
|
"transaction" in db
|
|
102
81
|
) {
|
|
103
|
-
|
|
104
|
-
// awaits, including inside its own transactions, so every call checks that the
|
|
105
|
-
// generation was not lost first.
|
|
106
|
-
return guardRawAccessAgainstLost(db as RawAccess, runCtx);
|
|
82
|
+
return db as RawAccess;
|
|
107
83
|
}
|
|
108
84
|
return undefined;
|
|
109
85
|
}
|
package/src/workflow/mod.ts
CHANGED
|
@@ -24,7 +24,7 @@ import { isActorAbortedError, RivetError } from "@/actor/errors";
|
|
|
24
24
|
import type { EventSchemaConfig, QueueSchemaConfig } from "@/actor/schema";
|
|
25
25
|
import type { AnyDatabaseProvider } from "@/common/database/config";
|
|
26
26
|
import { stringifyError } from "@/utils";
|
|
27
|
-
import {
|
|
27
|
+
import { WorkflowContext } from "./context";
|
|
28
28
|
import { ActorWorkflowControlDriver, ActorWorkflowDriver } from "./driver";
|
|
29
29
|
import { createWorkflowInspectorAdapter } from "./inspector";
|
|
30
30
|
|
|
@@ -178,28 +178,13 @@ export function workflow<
|
|
|
178
178
|
type WorkflowInspectorRegistration = ReturnType<
|
|
179
179
|
typeof createWorkflowInspectorAdapter
|
|
180
180
|
> & { control?: RunControl };
|
|
181
|
-
// Keyed by actor id and generation so a lost generation that is still running cannot rebind
|
|
182
|
-
// the control or state of the generation that replaced it.
|
|
183
181
|
const workflowInspectors = new Map<string, WorkflowInspectorRegistration>();
|
|
184
182
|
|
|
185
|
-
function
|
|
186
|
-
actorId
|
|
187
|
-
actorGeneration: number | undefined,
|
|
188
|
-
): string {
|
|
189
|
-
return actorGeneration === undefined
|
|
190
|
-
? actorId
|
|
191
|
-
: `${actorId}#${actorGeneration}`;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
function getWorkflowInspector(
|
|
195
|
-
actorId: string,
|
|
196
|
-
actorGeneration: number | undefined,
|
|
197
|
-
) {
|
|
198
|
-
const key = workflowInspectorKey(actorId, actorGeneration);
|
|
199
|
-
let workflowInspector = workflowInspectors.get(key);
|
|
183
|
+
function getWorkflowInspector(actorId: string) {
|
|
184
|
+
let workflowInspector = workflowInspectors.get(actorId);
|
|
200
185
|
if (!workflowInspector) {
|
|
201
186
|
workflowInspector = createWorkflowInspectorAdapter();
|
|
202
|
-
workflowInspectors.set(
|
|
187
|
+
workflowInspectors.set(actorId, workflowInspector);
|
|
203
188
|
}
|
|
204
189
|
return workflowInspector;
|
|
205
190
|
}
|
|
@@ -222,11 +207,7 @@ export function workflow<
|
|
|
222
207
|
}
|
|
223
208
|
)[ACTOR_CONTEXT_INTERNAL_SYMBOL];
|
|
224
209
|
invariant(actor, "workflow() requires an actor instance");
|
|
225
|
-
const
|
|
226
|
-
const workflowInspector = getWorkflowInspector(
|
|
227
|
-
actor.id,
|
|
228
|
-
actorGeneration,
|
|
229
|
-
);
|
|
210
|
+
const workflowInspector = getWorkflowInspector(actor.id);
|
|
230
211
|
|
|
231
212
|
const driver = new ActorWorkflowDriver(actor, runCtx);
|
|
232
213
|
const controlDriver = new ActorWorkflowControlDriver(actor, runCtx);
|
|
@@ -265,10 +246,7 @@ export function workflow<
|
|
|
265
246
|
|
|
266
247
|
const handle = runWorkflow(
|
|
267
248
|
actor.id,
|
|
268
|
-
async (ctx) =>
|
|
269
|
-
throwIfGenerationLost(runCtx);
|
|
270
|
-
return await fn(new WorkflowContext(ctx, runCtx));
|
|
271
|
-
},
|
|
249
|
+
async (ctx) => await fn(new WorkflowContext(ctx, runCtx)),
|
|
272
250
|
undefined,
|
|
273
251
|
driver,
|
|
274
252
|
{
|
|
@@ -278,15 +256,7 @@ export function workflow<
|
|
|
278
256
|
logger: runCtx.log as RunWorkflowOptions["logger"],
|
|
279
257
|
onHistoryUpdated: workflowInspector.update,
|
|
280
258
|
onError: onError
|
|
281
|
-
? async (event) =>
|
|
282
|
-
// A lost generation starts no new user hook.
|
|
283
|
-
if (
|
|
284
|
-
(actor as { isLost?: boolean }).isLost === true
|
|
285
|
-
) {
|
|
286
|
-
return;
|
|
287
|
-
}
|
|
288
|
-
await onError(runCtx, event);
|
|
289
|
-
}
|
|
259
|
+
? async (event) => await onError(runCtx, event)
|
|
290
260
|
: undefined,
|
|
291
261
|
},
|
|
292
262
|
);
|
|
@@ -332,20 +302,16 @@ export function workflow<
|
|
|
332
302
|
return defineRunHandler(run, {
|
|
333
303
|
icon: "diagram-project",
|
|
334
304
|
inspectorKind: "workflow",
|
|
335
|
-
createInspector: ({ actorId,
|
|
336
|
-
const
|
|
337
|
-
const workflowInspector = getWorkflowInspector(
|
|
338
|
-
actorId,
|
|
339
|
-
actorGeneration,
|
|
340
|
-
);
|
|
305
|
+
createInspector: ({ actorId, control }) => {
|
|
306
|
+
const workflowInspector = getWorkflowInspector(actorId);
|
|
341
307
|
workflowInspector.control = control;
|
|
342
308
|
return {
|
|
343
309
|
inspector: { workflow: workflowInspector.adapter },
|
|
344
310
|
dispose: () => {
|
|
345
311
|
// Do not let a stale disposer remove a newly-created adapter for
|
|
346
|
-
// the same actor
|
|
347
|
-
if (workflowInspectors.get(
|
|
348
|
-
workflowInspectors.delete(
|
|
312
|
+
// the same actor id.
|
|
313
|
+
if (workflowInspectors.get(actorId) === workflowInspector) {
|
|
314
|
+
workflowInspectors.delete(actorId);
|
|
349
315
|
}
|
|
350
316
|
},
|
|
351
317
|
};
|