@langchain/langgraph 1.4.21 → 1.5.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/dist/channels/base.cjs +123 -11
- package/dist/channels/base.cjs.map +1 -1
- package/dist/channels/base.d.cts +26 -4
- package/dist/channels/base.d.cts.map +1 -1
- package/dist/channels/base.d.ts +26 -4
- package/dist/channels/base.d.ts.map +1 -1
- package/dist/channels/base.js +120 -13
- package/dist/channels/base.js.map +1 -1
- package/dist/channels/binop.cjs +2 -1
- package/dist/channels/binop.cjs.map +1 -1
- package/dist/channels/binop.d.cts.map +1 -1
- package/dist/channels/binop.d.ts.map +1 -1
- package/dist/channels/binop.js +2 -1
- package/dist/channels/binop.js.map +1 -1
- package/dist/channels/index.cjs +2 -0
- package/dist/channels/index.d.cts +2 -2
- package/dist/channels/index.d.ts +2 -2
- package/dist/channels/index.js +2 -2
- package/dist/constants.cjs +14 -0
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts.map +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +13 -1
- package/dist/constants.js.map +1 -1
- package/dist/errors.cjs +22 -0
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.cts +10 -1
- package/dist/errors.d.cts.map +1 -1
- package/dist/errors.d.ts +10 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +21 -1
- package/dist/errors.js.map +1 -1
- package/dist/graph/index.cjs +1 -1
- package/dist/graph/index.js +1 -1
- package/dist/graph/messages_reducer.cjs +30 -6
- package/dist/graph/messages_reducer.cjs.map +1 -1
- package/dist/graph/messages_reducer.d.cts +4 -2
- package/dist/graph/messages_reducer.d.cts.map +1 -1
- package/dist/graph/messages_reducer.d.ts +4 -2
- package/dist/graph/messages_reducer.d.ts.map +1 -1
- package/dist/graph/messages_reducer.js +31 -8
- package/dist/graph/messages_reducer.js.map +1 -1
- package/dist/index.cjs +3 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -3
- package/dist/interrupt.cjs +9 -1
- package/dist/interrupt.cjs.map +1 -1
- package/dist/interrupt.d.cts.map +1 -1
- package/dist/interrupt.d.ts.map +1 -1
- package/dist/interrupt.js +10 -2
- package/dist/interrupt.js.map +1 -1
- package/dist/pregel/algo.cjs +46 -45
- package/dist/pregel/algo.cjs.map +1 -1
- package/dist/pregel/algo.d.cts.map +1 -1
- package/dist/pregel/algo.d.ts.map +1 -1
- package/dist/pregel/algo.js +47 -47
- package/dist/pregel/algo.js.map +1 -1
- package/dist/pregel/checkpoint.cjs +70 -0
- package/dist/pregel/checkpoint.cjs.map +1 -0
- package/dist/pregel/checkpoint.js +67 -0
- package/dist/pregel/checkpoint.js.map +1 -0
- package/dist/pregel/debug.cjs +1 -0
- package/dist/pregel/debug.cjs.map +1 -1
- package/dist/pregel/debug.js +1 -0
- package/dist/pregel/debug.js.map +1 -1
- package/dist/pregel/index.cjs +87 -34
- package/dist/pregel/index.cjs.map +1 -1
- package/dist/pregel/index.d.cts.map +1 -1
- package/dist/pregel/index.d.ts.map +1 -1
- package/dist/pregel/index.js +91 -38
- package/dist/pregel/index.js.map +1 -1
- package/dist/pregel/loop.cjs +336 -113
- package/dist/pregel/loop.cjs.map +1 -1
- package/dist/pregel/loop.js +341 -119
- package/dist/pregel/loop.js.map +1 -1
- package/dist/pregel/runner.cjs +1 -1
- package/dist/pregel/runner.cjs.map +1 -1
- package/dist/pregel/runner.js +2 -2
- package/dist/pregel/runner.js.map +1 -1
- package/dist/state/prebuilt/messages.cjs +1 -1
- package/dist/state/prebuilt/messages.js +1 -1
- package/dist/web.cjs +3 -1
- package/dist/web.d.cts +2 -2
- package/dist/web.d.ts +2 -2
- package/dist/web.js +3 -3
- package/package.json +5 -5
package/dist/pregel/loop.js
CHANGED
|
@@ -1,40 +1,116 @@
|
|
|
1
|
-
import { CONFIG_KEY_CHECKPOINT_ID, CONFIG_KEY_CHECKPOINT_MAP, CONFIG_KEY_CHECKPOINT_NS, CONFIG_KEY_READ, CONFIG_KEY_REPLAY_STATE, CONFIG_KEY_RESUME_MAP, CONFIG_KEY_RESUMING, CONFIG_KEY_SCRATCHPAD, CONFIG_KEY_STREAM, ERROR, INPUT, INTERRUPT, NULL_TASK_ID, PUSH, RESUME, _isOverwriteValue, _isSend, isCommand } from "../constants.js";
|
|
1
|
+
import { CONFIG_KEY_CHECKPOINT_ID, CONFIG_KEY_CHECKPOINT_MAP, CONFIG_KEY_CHECKPOINT_NS, CONFIG_KEY_READ, CONFIG_KEY_REPLAY_STATE, CONFIG_KEY_RESUME_MAP, CONFIG_KEY_RESUMING, CONFIG_KEY_SCRATCHPAD, CONFIG_KEY_STREAM, ERROR, INPUT, INTERRUPT, NULL_TASK_ID, Overwrite, PUSH, RESUME, _getOverwriteValue, _isOverwriteValue, _isSend, isCommand } from "../constants.js";
|
|
2
2
|
import { EmptyInputError, GraphInterrupt, isGraphInterrupt } from "../errors.js";
|
|
3
|
-
import { DELTA_WRITES_VERSIONED, channelsFromCheckpoint, createCheckpoint, deltaChannelsToSnapshot, exitDeltaTaskId, isDeltaChannel, isDeltaWritesVersioned } from "../channels/base.js";
|
|
3
|
+
import { DELTA_WRITES_VERSIONED, channelsFromCheckpoint, createCheckpoint, deltaChannelWrites, deltaChannelsToSnapshot, deltaChannelsWithPendingWrites, exitDeltaLateTaskId, exitDeltaTaskId, isDeltaChannel, isDeltaWritesVersioned } from "../channels/base.js";
|
|
4
4
|
import { gatherIterator, gatherIteratorSync, prefixGenerator } from "../utils.js";
|
|
5
|
+
import { advanceDeltaCounters, checkpointSuperseded } from "./checkpoint.js";
|
|
5
6
|
import { isXXH3 } from "../hash.js";
|
|
6
7
|
import { mapCommand, mapInput, mapOutputUpdates, mapOutputValues, readChannels } from "./io.js";
|
|
7
8
|
import { getNewChannelVersions, patchConfigurable } from "./utils/index.js";
|
|
8
|
-
import { _applyWrites, _prepareNextTasks, _prepareNodeErrorHandlerTask, _prepareSingleTask, increment, sanitizeUntrackedValuesInSend, shouldInterrupt } from "./algo.js";
|
|
9
|
+
import { _applyWrites, _prepareNextTasks, _prepareNodeErrorHandlerTask, _prepareSingleTask, increment, sanitizeUntrackedValuesInSend, shouldInterrupt, taskPathStr } from "./algo.js";
|
|
9
10
|
import { mapDebugCheckpoint, mapDebugTaskResults, mapDebugTasks, printStepTasks } from "./debug.js";
|
|
11
|
+
import { isMessagesDeltaReducer } from "../graph/messages_reducer.js";
|
|
10
12
|
import { ReplayState } from "./replay.js";
|
|
11
13
|
import { createDuplexStream } from "./stream.js";
|
|
12
|
-
import { AsyncBatchedStore, BaseCache, WRITES_IDX_MAP, copyCheckpoint, emptyCheckpoint } from "@langchain/langgraph-checkpoint";
|
|
14
|
+
import { AsyncBatchedStore, BaseCache, WRITES_IDX_MAP, copyCheckpoint, emptyCheckpoint, uuid5 } from "@langchain/langgraph-checkpoint";
|
|
13
15
|
import { v4 } from "@langchain/core/utils/uuid";
|
|
14
|
-
import { BaseMessage } from "@langchain/core/messages";
|
|
16
|
+
import { BaseMessage, coerceMessageLikeToMessage } from "@langchain/core/messages";
|
|
15
17
|
//#region src/pregel/loop.ts
|
|
16
18
|
const INPUT_DONE = Symbol.for("INPUT_DONE");
|
|
17
19
|
const INPUT_RESUMING = Symbol.for("INPUT_RESUMING");
|
|
18
20
|
const DEFAULT_LOOP_LIMIT = 25;
|
|
19
21
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
22
|
+
* Give the messages written to DeltaChannels stable ids, and return the writes
|
|
23
|
+
* in `writes` that go to a DeltaChannel.
|
|
24
|
+
*
|
|
25
|
+
* A DeltaChannel replays its saved writes on every read, and its reducer may
|
|
26
|
+
* not assign missing ids (`messagesDeltaReducer` doesn't), so a message saved
|
|
27
|
+
* without an id comes back without one, or with a new one on every read, and
|
|
28
|
+
* nothing can target it by id, for example `RemoveMessage`.
|
|
29
|
+
*
|
|
30
|
+
* In a channel whose reducer is {@link messagesDeltaReducer}, every
|
|
31
|
+
* message-like it accepts (a message, a plain object, a string, a
|
|
32
|
+
* `[role, content]` pair; alone, in an array or in an `Overwrite`) gets an id.
|
|
33
|
+
* A plain message object stays plain, with the id on a copy, except in an
|
|
34
|
+
* `Overwrite`, whose value the channel keeps as it is, so there it becomes a
|
|
35
|
+
* message. Other DeltaChannels may hold other data, so there only message
|
|
36
|
+
* objects get ids. Anything that can't become a message is left as it is, so
|
|
37
|
+
* it fails where it always has, when the reducer reads it.
|
|
38
|
+
*
|
|
39
|
+
* A write that changes is replaced in `writes` by a new `[channel, value]`
|
|
40
|
+
* pair: arrays and pairs the caller passed in are never changed, only the ids
|
|
41
|
+
* of message objects.
|
|
23
42
|
*/
|
|
24
|
-
function
|
|
25
|
-
|
|
43
|
+
function ensureDeltaMessageIds(channels, writes) {
|
|
44
|
+
const deltaWrites = [];
|
|
45
|
+
for (let i = 0; i < writes.length; i += 1) {
|
|
46
|
+
const [channelName, value] = writes[i];
|
|
47
|
+
const channel = channels[String(channelName)];
|
|
48
|
+
if (!isDeltaChannel(channel)) continue;
|
|
49
|
+
if (isMessagesDeltaReducer(channel.reducer)) {
|
|
50
|
+
const newValue = withMessageIds(value);
|
|
51
|
+
if (newValue !== value) writes[i] = [channelName, newValue];
|
|
52
|
+
} else stampMessageIds(value);
|
|
53
|
+
deltaWrites.push(writes[i]);
|
|
54
|
+
}
|
|
55
|
+
return deltaWrites;
|
|
56
|
+
}
|
|
57
|
+
function withMessageIds(value) {
|
|
58
|
+
const [isOverwrite, overwriteValue] = _getOverwriteValue(value);
|
|
59
|
+
if (!isOverwrite) return Array.isArray(value) ? mapItems(value, writtenMessageWithId) : writtenMessageWithId(value);
|
|
60
|
+
if (!Array.isArray(overwriteValue)) return value;
|
|
61
|
+
const items = mapItems(overwriteValue, messageWithId);
|
|
62
|
+
return items === overwriteValue ? value : new Overwrite(items);
|
|
63
|
+
}
|
|
64
|
+
function mapItems(items, fn) {
|
|
65
|
+
const mapped = items.map(fn);
|
|
66
|
+
return mapped.every((item, i) => item === items[i]) ? items : mapped;
|
|
67
|
+
}
|
|
68
|
+
function writtenMessageWithId(item) {
|
|
69
|
+
if (!isPlainObject(item)) return messageWithId(item);
|
|
70
|
+
try {
|
|
71
|
+
if (coerceMessageLikeToMessage(item).id) return item;
|
|
72
|
+
} catch {
|
|
73
|
+
return item;
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
...item,
|
|
77
|
+
id: v4()
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function messageWithId(item) {
|
|
81
|
+
if (BaseMessage.isInstance(item)) {
|
|
82
|
+
stampMessageIds(item);
|
|
83
|
+
return item;
|
|
84
|
+
}
|
|
85
|
+
let message;
|
|
86
|
+
try {
|
|
87
|
+
message = coerceMessageLikeToMessage(item);
|
|
88
|
+
} catch {
|
|
89
|
+
return item;
|
|
90
|
+
}
|
|
91
|
+
if (!message.id) setMessageId(message);
|
|
92
|
+
return message;
|
|
93
|
+
}
|
|
94
|
+
function isPlainObject(item) {
|
|
95
|
+
if (typeof item !== "object" || item === null) return false;
|
|
96
|
+
const proto = Object.getPrototypeOf(item);
|
|
97
|
+
return (proto === Object.prototype || proto === null) && !("lc" in item);
|
|
98
|
+
}
|
|
99
|
+
function stampMessageIds(value) {
|
|
26
100
|
if (BaseMessage.isInstance(value)) {
|
|
27
|
-
|
|
28
|
-
if (msg.id == null) {
|
|
29
|
-
msg.id = v4();
|
|
30
|
-
if (msg.lc_kwargs != null) msg.lc_kwargs.id = msg.id;
|
|
31
|
-
}
|
|
101
|
+
if (value.id == null) setMessageId(value);
|
|
32
102
|
return;
|
|
33
103
|
}
|
|
34
104
|
if (Array.isArray(value)) {
|
|
35
|
-
for (const item of value)
|
|
105
|
+
for (const item of value) stampMessageIds(item);
|
|
36
106
|
return;
|
|
37
107
|
}
|
|
108
|
+
const [isOverwrite, overwriteValue] = _getOverwriteValue(value);
|
|
109
|
+
if (isOverwrite) stampMessageIds(overwriteValue);
|
|
110
|
+
}
|
|
111
|
+
function setMessageId(message) {
|
|
112
|
+
message.id = v4();
|
|
113
|
+
if (message.lc_kwargs != null) message.lc_kwargs.id = message.id;
|
|
38
114
|
}
|
|
39
115
|
/**
|
|
40
116
|
* Split a serialized checkpoint namespace into its path segments.
|
|
@@ -111,20 +187,67 @@ var PregelLoop = class PregelLoop {
|
|
|
111
187
|
checkpointIdSaved;
|
|
112
188
|
/**
|
|
113
189
|
* Exit-mode accumulator of DeltaChannel writes across the whole run, as
|
|
114
|
-
* `[step, taskId, channel, value]
|
|
190
|
+
* `[step, taskId, taskPath, channel, value]` — see `_putExitDeltaWrites`
|
|
191
|
+
* for how they are ordered. `undefined` outside "exit" durability.
|
|
115
192
|
*/
|
|
116
193
|
_exitDeltaWrites;
|
|
117
194
|
/**
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
195
|
+
* The `(taskId, channel)` pairs whose delta writes are stored on the loaded
|
|
196
|
+
* checkpoint, which a resume addressed by `checkpoint_id` can rerun.
|
|
197
|
+
* Captured in the constructor, before this run's Command processing adds
|
|
198
|
+
* writes (mirrors Python's `_first`). Used by the exit-mode accumulator to
|
|
199
|
+
* skip the loaded writes on the first superstep — they are already stored
|
|
200
|
+
* on the checkpoint.
|
|
201
|
+
*/
|
|
202
|
+
_storedDeltaWrites = /* @__PURE__ */ new Set();
|
|
203
|
+
/**
|
|
204
|
+
* This run's `Command` delta writes (`NULL_TASK_ID`), kept apart from the
|
|
205
|
+
* `NULL_TASK_ID` writes loaded with the checkpoint, which the first
|
|
206
|
+
* superstep skips. Written by the input handler; replayed into the
|
|
207
|
+
* exit-mode accumulator at the first superstep.
|
|
208
|
+
*/
|
|
209
|
+
_exitCommandWrites = [];
|
|
210
|
+
/**
|
|
211
|
+
* The step of the first superstep this run completed (the loaded
|
|
212
|
+
* checkpoint's own), or `null` before it ticks. The exit-mode accumulator
|
|
213
|
+
* keeps that superstep's real task paths so its writes interleave with the
|
|
214
|
+
* loaded ones as live execution did; later supersteps get `~~`-prefixed
|
|
215
|
+
* synthetic paths (see `_putExitDeltaWrites`).
|
|
216
|
+
*/
|
|
217
|
+
_exitFirstStep = null;
|
|
218
|
+
/**
|
|
219
|
+
* DeltaChannels that must snapshot at the next checkpoint, whatever their
|
|
220
|
+
* cadence counters say:
|
|
221
|
+
* - an Overwrite arrived since the last checkpoint, so sparse replay has to
|
|
222
|
+
* start from the post-overwrite value;
|
|
223
|
+
* - the checkpoint this run starts from has pending writes to them; see
|
|
224
|
+
* {@link deltaChannelsWithPendingWrites}.
|
|
225
|
+
* Cleared once the channel snapshots.
|
|
226
|
+
*/
|
|
227
|
+
_deltaChannelsForcedSnapshot = /* @__PURE__ */ new Set();
|
|
228
|
+
/**
|
|
229
|
+
* Set by `_first` for a resume: the writes it loaded with the checkpoint,
|
|
230
|
+
* which the end of the superstep checks against the ones handed back to
|
|
231
|
+
* their tasks.
|
|
232
|
+
*/
|
|
233
|
+
_resumeLoadedWrites = [];
|
|
234
|
+
/** Tasks a resume handed loaded writes back to. */
|
|
235
|
+
_reappliedTaskIds = /* @__PURE__ */ new Set();
|
|
236
|
+
/** Tasks that wrote during this run, the only ones exit mode saves at exit. */
|
|
237
|
+
_writtenTaskIds = /* @__PURE__ */ new Set();
|
|
238
|
+
/**
|
|
239
|
+
* A checkpoint addressed by `checkpoint_id` that the thread has moved past,
|
|
240
|
+
* so it may already have children.
|
|
123
241
|
*/
|
|
124
|
-
|
|
242
|
+
addressedCheckpointSuperseded;
|
|
125
243
|
/** Whether a real checkpoint was loaded from the saver at initialization. */
|
|
126
244
|
_hasPersistedParent = false;
|
|
127
245
|
/**
|
|
246
|
+
* Whether initialization loaded the thread's latest checkpoint, not one a
|
|
247
|
+
* `checkpoint_id` addressed, so nothing has been built on it yet.
|
|
248
|
+
*/
|
|
249
|
+
_loadedLatest = false;
|
|
250
|
+
/**
|
|
128
251
|
* Whether this branch carries {@link DELTA_WRITES_VERSIONED}: a graph with
|
|
129
252
|
* a DeltaChannel on a new thread, or a loaded checkpoint that has it. Every
|
|
130
253
|
* checkpoint this loop writes inherits it.
|
|
@@ -143,7 +266,9 @@ var PregelLoop = class PregelLoop {
|
|
|
143
266
|
outputKeys;
|
|
144
267
|
streamKeys;
|
|
145
268
|
nodes;
|
|
146
|
-
|
|
269
|
+
/** Whether the run points this loop at a checkpoint. */
|
|
270
|
+
addressed;
|
|
271
|
+
skipDoneTasks = true;
|
|
147
272
|
prevCheckpointConfig;
|
|
148
273
|
updatedChannels;
|
|
149
274
|
status = "pending";
|
|
@@ -162,6 +287,14 @@ var PregelLoop = class PregelLoop {
|
|
|
162
287
|
resumeAtHead;
|
|
163
288
|
_checkpointerChainedPromise = Promise.resolve();
|
|
164
289
|
/**
|
|
290
|
+
* The DeltaChannel writes this loop sent to the saver since the last
|
|
291
|
+
* checkpoint save was scheduled. That save waits for them, and a rejection
|
|
292
|
+
* skips it and every later save: a DeltaChannel is rebuilt from its writes
|
|
293
|
+
* along the parent chain, so a checkpoint saved past a missing write or
|
|
294
|
+
* parent reads back short for good.
|
|
295
|
+
*/
|
|
296
|
+
_pendingDeltaWrites = [];
|
|
297
|
+
/**
|
|
165
298
|
* Track a checkpointer promise, removing it from the set on success.
|
|
166
299
|
* Failed promises are kept so that Promise.all() in the finally block
|
|
167
300
|
* of _streamIterator can surface the error.
|
|
@@ -175,6 +308,7 @@ var PregelLoop = class PregelLoop {
|
|
|
175
308
|
}, (error) => {
|
|
176
309
|
throw error;
|
|
177
310
|
});
|
|
311
|
+
tracked.catch(() => {});
|
|
178
312
|
this.checkpointerPromises.add(tracked);
|
|
179
313
|
}
|
|
180
314
|
/**
|
|
@@ -222,18 +356,20 @@ var PregelLoop = class PregelLoop {
|
|
|
222
356
|
this.checkpointPreviousVersions = params.checkpointPreviousVersions;
|
|
223
357
|
this.channels = params.channels;
|
|
224
358
|
this.checkpointPendingWrites = params.checkpointPendingWrites;
|
|
359
|
+
for (const [tid, ch] of this.checkpointPendingWrites) if (tid !== "00000000-0000-0000-0000-000000000000" && isDeltaChannel(this.channels[ch])) this._storedDeltaWrites.add(`${tid}\u0000${ch}`);
|
|
225
360
|
this.step = params.step;
|
|
226
361
|
this.stop = params.stop;
|
|
227
362
|
this.config = params.config;
|
|
228
363
|
this.checkpointConfig = params.checkpointConfig;
|
|
229
364
|
this.isNested = params.isNested;
|
|
230
365
|
this.resumeAtHead = params.resumeAtHead;
|
|
366
|
+
this.addressedCheckpointSuperseded = params.addressedCheckpointSuperseded;
|
|
231
367
|
this.manager = params.manager;
|
|
232
368
|
this.lifecycleEvents = params.hasGraphCallbacks ? [] : void 0;
|
|
233
369
|
this.outputKeys = params.outputKeys;
|
|
234
370
|
this.streamKeys = params.streamKeys;
|
|
235
371
|
this.nodes = params.nodes;
|
|
236
|
-
this.
|
|
372
|
+
this.addressed = params.addressed;
|
|
237
373
|
this.store = params.store;
|
|
238
374
|
this.cache = params.cache ? new AsyncBatchedCache(params.cache) : void 0;
|
|
239
375
|
this.stream = params.stream;
|
|
@@ -247,6 +383,7 @@ var PregelLoop = class PregelLoop {
|
|
|
247
383
|
this.control = this.config.control;
|
|
248
384
|
this._exitDeltaWrites = this.durability === "exit" && this.checkpointer != null ? [] : void 0;
|
|
249
385
|
this._hasPersistedParent = params.hasPersistedParent ?? false;
|
|
386
|
+
this._loadedLatest = params.loadedLatest ?? false;
|
|
250
387
|
this._deltaWritesVersioned = params.deltaWritesVersioned ?? false;
|
|
251
388
|
this._initialCheckpointConfig = params.checkpointConfig;
|
|
252
389
|
this.checkpointIdSaved = params.checkpoint.id;
|
|
@@ -254,13 +391,11 @@ var PregelLoop = class PregelLoop {
|
|
|
254
391
|
static async initialize(params) {
|
|
255
392
|
let { config, stream } = params;
|
|
256
393
|
if (stream !== void 0 && config.configurable?.["__pregel_stream"] !== void 0) stream = createDuplexStream(stream, config.configurable[CONFIG_KEY_STREAM]);
|
|
257
|
-
const skipDoneTasks = config.configurable ? !("checkpoint_id" in config.configurable) : true;
|
|
258
394
|
const scratchpad = config.configurable?.[CONFIG_KEY_SCRATCHPAD];
|
|
259
395
|
if (config.configurable && scratchpad) {
|
|
260
396
|
if (scratchpad.subgraphCounter > 0) config = patchConfigurable(config, { [CONFIG_KEY_CHECKPOINT_NS]: [config.configurable[CONFIG_KEY_CHECKPOINT_NS], scratchpad.subgraphCounter.toString()].join("|") });
|
|
261
397
|
scratchpad.subgraphCounter += 1;
|
|
262
398
|
}
|
|
263
|
-
const requestedCheckpointId = config.configurable?.checkpoint_id;
|
|
264
399
|
const isNested = CONFIG_KEY_READ in (config.configurable ?? {});
|
|
265
400
|
if (!isNested && config.configurable?.checkpoint_ns !== void 0 && config.configurable?.checkpoint_ns !== "") config = patchConfigurable(config, {
|
|
266
401
|
checkpoint_ns: "",
|
|
@@ -268,15 +403,21 @@ var PregelLoop = class PregelLoop {
|
|
|
268
403
|
});
|
|
269
404
|
let checkpointConfig = config;
|
|
270
405
|
if (config.configurable?.checkpoint_id === void 0 && config.configurable?.["checkpoint_map"] !== void 0 && config.configurable?.["checkpoint_map"]?.[config.configurable?.checkpoint_ns]) checkpointConfig = patchConfigurable(config, { checkpoint_id: config.configurable[CONFIG_KEY_CHECKPOINT_MAP][config.configurable?.checkpoint_ns] });
|
|
406
|
+
const addressed = Boolean(checkpointConfig.configurable?.["checkpoint_id"] || config.configurable?.["__pregel_replay_state"]);
|
|
271
407
|
const checkpointNamespace = checkpointNamespaceFromNs(config.configurable?.checkpoint_ns);
|
|
272
408
|
let saved;
|
|
409
|
+
let loadedLatest = false;
|
|
273
410
|
if (!params.checkpointer) saved = void 0;
|
|
274
411
|
else if (checkpointConfig.configurable?.["checkpoint_id"]) saved = await params.checkpointer.getTuple(checkpointConfig);
|
|
275
412
|
else if (config.configurable?.["__pregel_replay_state"]) {
|
|
276
413
|
saved = await config.configurable[CONFIG_KEY_REPLAY_STATE].getCheckpoint(config.configurable?.["checkpoint_ns"] ?? "", params.checkpointer, checkpointConfig);
|
|
277
414
|
if (config.configurable) delete config.configurable[CONFIG_KEY_RESUMING];
|
|
278
|
-
} else
|
|
415
|
+
} else {
|
|
416
|
+
saved = await params.checkpointer.getTuple(checkpointConfig);
|
|
417
|
+
loadedLatest = true;
|
|
418
|
+
}
|
|
279
419
|
const hasPersistedParent = saved !== void 0;
|
|
420
|
+
const addressedConfig = checkpointConfig;
|
|
280
421
|
if (!saved) saved = {
|
|
281
422
|
config,
|
|
282
423
|
checkpoint: emptyCheckpoint(),
|
|
@@ -304,12 +445,13 @@ var PregelLoop = class PregelLoop {
|
|
|
304
445
|
const checkpointMap = config.configurable?.[CONFIG_KEY_CHECKPOINT_MAP];
|
|
305
446
|
if (typeof currentCheckpointNamespace === "string" && currentCheckpointNamespace !== "" && typeof checkpointMap === "object" && checkpointMap !== null && currentCheckpointNamespace in checkpointMap && checkpointPendingWrites.length > 0) checkpointPendingWrites = checkpointPendingWrites.filter(([, channel]) => channel !== RESUME);
|
|
306
447
|
let resumeAtHead = false;
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
448
|
+
let addressedCheckpointSuperseded = false;
|
|
449
|
+
if (params.checkpointer && hasPersistedParent) {
|
|
450
|
+
const superseded = await checkpointSuperseded(params.checkpointer, addressedConfig, saved);
|
|
451
|
+
const isUpdateOrFork = checkpointMetadata.source === "update" || checkpointMetadata.source === "fork";
|
|
452
|
+
resumeAtHead = addressedConfig.configurable?.checkpoint_id !== void 0 && !superseded && !isUpdateOrFork;
|
|
453
|
+
addressedCheckpointSuperseded = superseded;
|
|
454
|
+
}
|
|
313
455
|
const deltaWritesVersioned = Object.values(params.channelSpecs).some(isDeltaChannel) && (!hasPersistedParent || isDeltaWritesVersioned(saved.metadata));
|
|
314
456
|
const channels = await channelsFromCheckpoint(params.channelSpecs, checkpoint, {
|
|
315
457
|
saver: params.checkpointer,
|
|
@@ -333,9 +475,10 @@ var PregelLoop = class PregelLoop {
|
|
|
333
475
|
channels,
|
|
334
476
|
isNested,
|
|
335
477
|
resumeAtHead,
|
|
478
|
+
addressedCheckpointSuperseded,
|
|
336
479
|
manager: params.manager,
|
|
337
480
|
hasGraphCallbacks: params.hasGraphCallbacks,
|
|
338
|
-
|
|
481
|
+
addressed,
|
|
339
482
|
step,
|
|
340
483
|
stop,
|
|
341
484
|
checkpointPreviousVersions,
|
|
@@ -352,11 +495,14 @@ var PregelLoop = class PregelLoop {
|
|
|
352
495
|
debug: params.debug,
|
|
353
496
|
triggerToNodes: params.triggerToNodes,
|
|
354
497
|
hasPersistedParent,
|
|
498
|
+
loadedLatest,
|
|
355
499
|
deltaWritesVersioned
|
|
356
500
|
});
|
|
357
501
|
}
|
|
358
502
|
_checkpointerPutAfterPrevious(input) {
|
|
359
|
-
|
|
503
|
+
const pendingDeltaWrites = this._pendingDeltaWrites;
|
|
504
|
+
this._pendingDeltaWrites = [];
|
|
505
|
+
this._checkpointerChainedPromise = this._checkpointerChainedPromise.then(() => Promise.all(pendingDeltaWrites)).then(() => {
|
|
360
506
|
return this.checkpointer?.put(input.config, input.checkpoint, input.metadata, input.newVersions);
|
|
361
507
|
});
|
|
362
508
|
this._trackCheckpointerPromise(this._checkpointerChainedPromise);
|
|
@@ -365,10 +511,14 @@ var PregelLoop = class PregelLoop {
|
|
|
365
511
|
* Put writes for a task, to be read by the next tick.
|
|
366
512
|
* @param taskId
|
|
367
513
|
* @param writes
|
|
514
|
+
* @param cached The writes were served from the node cache: stream them as
|
|
515
|
+
* cached and don't write them to the cache again.
|
|
368
516
|
*/
|
|
369
|
-
putWrites(taskId, writes) {
|
|
517
|
+
putWrites(taskId, writes, cached = false) {
|
|
370
518
|
let writesCopy = writes;
|
|
371
519
|
if (writesCopy.length === 0) return;
|
|
520
|
+
this._writtenTaskIds.add(taskId);
|
|
521
|
+
const deltaWrites = ensureDeltaMessageIds(this.channels, writes);
|
|
372
522
|
if (writesCopy.every(([key]) => key in WRITES_IDX_MAP)) writesCopy = Array.from(new Map(writesCopy.map((w) => [w[0], w])).values());
|
|
373
523
|
let hasUntrackedChannels = false;
|
|
374
524
|
for (const key in this.channels) if (Object.prototype.hasOwnProperty.call(this.channels, key)) {
|
|
@@ -391,17 +541,18 @@ var PregelLoop = class PregelLoop {
|
|
|
391
541
|
c,
|
|
392
542
|
v
|
|
393
543
|
]);
|
|
394
|
-
for (const [c, v] of writesToSave) {
|
|
395
|
-
const channel = this.channels[c];
|
|
396
|
-
if (channel != null && isDeltaChannel(channel)) ensureMessageIds(v);
|
|
397
|
-
}
|
|
398
544
|
const config = patchConfigurable(this.checkpointConfig, {
|
|
399
545
|
[CONFIG_KEY_CHECKPOINT_NS]: this.config.configurable?.checkpoint_ns ?? "",
|
|
400
546
|
[CONFIG_KEY_CHECKPOINT_ID]: this.checkpoint.id
|
|
401
547
|
});
|
|
402
|
-
if (this.durability !== "exit" && this.checkpointer != null)
|
|
403
|
-
|
|
404
|
-
|
|
548
|
+
if (this.durability !== "exit" && this.checkpointer != null) {
|
|
549
|
+
const task = this.tasks[taskId];
|
|
550
|
+
const saved = this.checkpointer.putWrites(config, writesToSave, taskId, task != null ? taskPathStr(task.path) : "");
|
|
551
|
+
this._trackCheckpointerPromise(saved);
|
|
552
|
+
if (deltaWrites.length > 0) this._pendingDeltaWrites.push(saved);
|
|
553
|
+
}
|
|
554
|
+
if (this.tasks) this._outputWrites(taskId, writesCopy, cached);
|
|
555
|
+
if (cached || !writes.length || !this.cache || !this.tasks) return;
|
|
405
556
|
const task = this.tasks[taskId];
|
|
406
557
|
if (task == null || task.cache_key == null) return;
|
|
407
558
|
if (writes[0][0] === "__error__" || writes[0][0] === "__interrupt__") return;
|
|
@@ -411,6 +562,10 @@ var PregelLoop = class PregelLoop {
|
|
|
411
562
|
ttl: task.cache_key.ttl
|
|
412
563
|
}]);
|
|
413
564
|
}
|
|
565
|
+
/** Streams the writes reapplied to tasks that finished in a previous run. */
|
|
566
|
+
_outputCachedWrites(tasks) {
|
|
567
|
+
for (const task of tasks) if (task.writes.length > 0) this._outputWrites(task.id, task.writes, true);
|
|
568
|
+
}
|
|
414
569
|
_outputWrites(taskId, writes, cached = false) {
|
|
415
570
|
const task = this.tasks[taskId];
|
|
416
571
|
if (task !== void 0) {
|
|
@@ -469,19 +624,37 @@ var PregelLoop = class PregelLoop {
|
|
|
469
624
|
const finishTaskList = Object.values(this.tasks);
|
|
470
625
|
const writes = finishTaskList.flatMap((t) => t.writes);
|
|
471
626
|
this.updatedChannels = _applyWrites(this.checkpoint, this.channels, finishTaskList, this.checkpointerGetNextVersion, this.triggerToNodes);
|
|
472
|
-
for (const [ch, v] of writes)
|
|
473
|
-
|
|
474
|
-
|
|
627
|
+
for (const [ch, v] of deltaChannelWrites(this.channels, writes)) if (_isOverwriteValue(v)) this._deltaChannelsForcedSnapshot.add(ch);
|
|
628
|
+
if (this._resumeLoadedWrites.length > 0) {
|
|
629
|
+
for (const ch of deltaChannelsWithPendingWrites(this.channels, this._resumeLoadedWrites.filter(([tid]) => !this._reappliedTaskIds.has(tid)))) this._deltaChannelsForcedSnapshot.add(ch);
|
|
630
|
+
this._resumeLoadedWrites = [];
|
|
475
631
|
}
|
|
476
632
|
const valuesOutput = await gatherIterator(prefixGenerator(mapOutputValues(this.outputKeys, writes, this.channels), "values"));
|
|
477
|
-
if (this._exitDeltaWrites !== void 0)
|
|
478
|
-
const
|
|
479
|
-
if (
|
|
480
|
-
this.step
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
633
|
+
if (this._exitDeltaWrites !== void 0) {
|
|
634
|
+
const first = this._exitFirstStep === null;
|
|
635
|
+
if (first) {
|
|
636
|
+
this._exitFirstStep = this.step;
|
|
637
|
+
for (const [ch, v] of this._exitCommandWrites) this._exitDeltaWrites.push([
|
|
638
|
+
this.step,
|
|
639
|
+
NULL_TASK_ID,
|
|
640
|
+
"",
|
|
641
|
+
ch,
|
|
642
|
+
v
|
|
643
|
+
]);
|
|
644
|
+
}
|
|
645
|
+
for (const [tid, ch, v] of this.checkpointPendingWrites) {
|
|
646
|
+
const channel = this.channels[ch];
|
|
647
|
+
if (channel == null || !isDeltaChannel(channel)) continue;
|
|
648
|
+
if (first && (tid === "00000000-0000-0000-0000-000000000000" || this._storedDeltaWrites.has(`${tid}\u0000${ch}`))) continue;
|
|
649
|
+
const task = this.tasks[tid];
|
|
650
|
+
this._exitDeltaWrites.push([
|
|
651
|
+
this.step,
|
|
652
|
+
tid,
|
|
653
|
+
task != null ? taskPathStr(task.path) : "",
|
|
654
|
+
ch,
|
|
655
|
+
v
|
|
656
|
+
]);
|
|
657
|
+
}
|
|
485
658
|
}
|
|
486
659
|
this.checkpointPendingWrites = [];
|
|
487
660
|
await this._putCheckpoint({ source: "loop" });
|
|
@@ -513,25 +686,24 @@ var PregelLoop = class PregelLoop {
|
|
|
513
686
|
this.status = "done";
|
|
514
687
|
return false;
|
|
515
688
|
}
|
|
516
|
-
if (this.control != null && this.control.drainRequested) {
|
|
517
|
-
this.status = "draining";
|
|
518
|
-
return false;
|
|
519
|
-
}
|
|
520
689
|
if (this.skipDoneTasks && this.checkpointPendingWrites.length > 0) {
|
|
521
|
-
|
|
522
|
-
if (k === "__error__" || k === "__error_source_node__" || k === "__interrupt__" || k === "__resume__") continue;
|
|
523
|
-
const task = taskList.find((t) => t.id === tid);
|
|
524
|
-
if (task) task.writes.push([k, v]);
|
|
525
|
-
}
|
|
690
|
+
this._reapplyWritesToSucceededNodes(this.tasks);
|
|
526
691
|
this._resumeErrorHandlersIfApplicable();
|
|
527
692
|
taskList = Object.values(this.tasks);
|
|
528
|
-
for (const task of taskList) if (task.writes.length > 0) this._outputWrites(task.id, task.writes, true);
|
|
529
693
|
}
|
|
530
|
-
if (taskList.every((task) => task.writes.length > 0))
|
|
694
|
+
if (taskList.every((task) => task.writes.length > 0)) {
|
|
695
|
+
this._outputCachedWrites(taskList);
|
|
696
|
+
return this.tick({ inputKeys });
|
|
697
|
+
}
|
|
531
698
|
if (shouldInterrupt(this.checkpoint, this.interruptBefore, taskList)) {
|
|
532
699
|
this.status = "interrupt_before";
|
|
533
700
|
throw new GraphInterrupt();
|
|
534
701
|
}
|
|
702
|
+
if (this.control != null && this.control.drainRequested) {
|
|
703
|
+
this.status = "draining";
|
|
704
|
+
return false;
|
|
705
|
+
}
|
|
706
|
+
this._outputCachedWrites(taskList);
|
|
535
707
|
if (this.stream.modes.has("tasks") || this.stream.modes.has("debug")) {
|
|
536
708
|
const debugOutput = await gatherIterator(prefixGenerator(mapDebugTasks(taskList), "tasks"));
|
|
537
709
|
this._emit(debugOutput);
|
|
@@ -583,7 +755,7 @@ var PregelLoop = class PregelLoop {
|
|
|
583
755
|
this.tasks[pushed.id] = pushed;
|
|
584
756
|
if (this.skipDoneTasks) this._matchWrites({ [pushed.id]: pushed });
|
|
585
757
|
const tasks = await this._matchCachedWrites();
|
|
586
|
-
for (const { task } of tasks) this.
|
|
758
|
+
for (const { task } of tasks) this.putWrites(task.id, task.writes, true);
|
|
587
759
|
return pushed;
|
|
588
760
|
}
|
|
589
761
|
/**
|
|
@@ -667,33 +839,54 @@ var PregelLoop = class PregelLoop {
|
|
|
667
839
|
}
|
|
668
840
|
async _first(inputKeys) {
|
|
669
841
|
const { configurable } = this.config;
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
842
|
+
const inputIsCommand = isCommand(this.input);
|
|
843
|
+
const isTimeTraveling = this.addressed && (this.isNested && configurable?.["checkpoint_ns"] !== void 0 && configurable?.["checkpoint_ns"] !== "" && configurable?.["checkpoint_map"] !== void 0 && configurable["checkpoint_ns"] in configurable["checkpoint_map"] || !(inputIsCommand && this.input.resume != null || configurable?.["__pregel_resuming"] === true || this.resumeAtHead));
|
|
844
|
+
this.skipDoneTasks = !(isTimeTraveling || this.addressedCheckpointSuperseded);
|
|
845
|
+
const isUpdateOrFork = this.checkpointMetadata.source === "update" || this.checkpointMetadata.source === "fork";
|
|
846
|
+
const forks = isTimeTraveling && (inputIsCommand || this.isResuming) && (!isUpdateOrFork || this.addressedCheckpointSuperseded);
|
|
847
|
+
const carried = [];
|
|
848
|
+
const reapplies = this.isResuming && this.skipDoneTasks;
|
|
849
|
+
this._resumeLoadedWrites = reapplies ? this.checkpointPendingWrites.filter(([tid]) => tid !== NULL_TASK_ID) : [];
|
|
850
|
+
this._reappliedTaskIds = /* @__PURE__ */ new Set();
|
|
851
|
+
this._deltaChannelsForcedSnapshot = reapplies ? /* @__PURE__ */ new Set() : deltaChannelsWithPendingWrites(this.channels, this.checkpointPendingWrites);
|
|
852
|
+
if (inputIsCommand) {
|
|
853
|
+
const command = this.input;
|
|
854
|
+
const hasResume = command.resume != null;
|
|
855
|
+
if (command.resume != null && typeof command.resume === "object" && Object.keys(command.resume).every(isXXH3)) {
|
|
673
856
|
this.config.configurable ??= {};
|
|
674
|
-
this.config.configurable[CONFIG_KEY_RESUME_MAP] =
|
|
857
|
+
this.config.configurable[CONFIG_KEY_RESUME_MAP] = command.resume;
|
|
675
858
|
}
|
|
676
859
|
if (hasResume && this.checkpointer == null) throw new Error("Cannot use Command(resume=...) without checkpointer");
|
|
677
860
|
const writes = {};
|
|
678
|
-
for (const [tid, key, value] of mapCommand(
|
|
861
|
+
for (const [tid, key, value] of mapCommand(command, this.checkpointPendingWrites)) {
|
|
679
862
|
writes[tid] ??= [];
|
|
680
863
|
writes[tid].push([key, value]);
|
|
681
864
|
}
|
|
682
865
|
if (Object.keys(writes).length === 0) throw new EmptyInputError("Received empty Command input");
|
|
683
|
-
for (const [tid, ws] of Object.entries(writes))
|
|
866
|
+
for (const [tid, ws] of Object.entries(writes)) if (forks) {
|
|
867
|
+
ensureDeltaMessageIds(this.channels, ws);
|
|
868
|
+
for (const [c, v] of ws) carried.push([
|
|
869
|
+
tid,
|
|
870
|
+
c,
|
|
871
|
+
v
|
|
872
|
+
]);
|
|
873
|
+
} else {
|
|
874
|
+
this.putWrites(tid, ws);
|
|
875
|
+
if (this._exitDeltaWrites !== void 0 && tid === "00000000-0000-0000-0000-000000000000") this._exitCommandWrites.push(...deltaChannelWrites(this.channels, ws));
|
|
876
|
+
}
|
|
877
|
+
for (const ch of deltaChannelsWithPendingWrites(this.channels, carried)) this._deltaChannelsForcedSnapshot.add(ch);
|
|
684
878
|
}
|
|
685
|
-
const nullWrites =
|
|
879
|
+
const nullWrites = [...this.checkpointPendingWrites ?? [], ...carried].filter((w) => w[0] === NULL_TASK_ID).map((w) => w.slice(1));
|
|
686
880
|
if (nullWrites.length > 0) _applyWrites(this.checkpoint, this.channels, [{
|
|
687
881
|
name: INPUT,
|
|
688
882
|
writes: nullWrites,
|
|
689
883
|
triggers: []
|
|
690
884
|
}], this.checkpointerGetNextVersion, this.triggerToNodes);
|
|
691
|
-
const inputIsCommand = isCommand(this.input);
|
|
692
885
|
const isCommandUpdateOrGoto = inputIsCommand && nullWrites.length > 0;
|
|
693
|
-
const isTimeTraveling = this.isReplaying && (this.isNested && configurable?.["checkpoint_ns"] !== void 0 && configurable?.["checkpoint_ns"] !== "" && configurable?.["checkpoint_map"] !== void 0 && configurable["checkpoint_ns"] in configurable["checkpoint_map"] || !(inputIsCommand && this.input.resume != null || configurable?.["__pregel_resuming"] === true || this.resumeAtHead));
|
|
694
886
|
if (isTimeTraveling) this.checkpointPendingWrites = this.checkpointPendingWrites.filter((w) => w[1] !== RESUME);
|
|
695
|
-
const
|
|
696
|
-
if (
|
|
887
|
+
const nullResume = (configurable?.[CONFIG_KEY_SCRATCHPAD])?.nullResume;
|
|
888
|
+
if (nullResume !== void 0 && !forks) this.putWrites(NULL_TASK_ID, [[RESUME, nullResume]]);
|
|
889
|
+
let replayBound;
|
|
697
890
|
const cachedIsResuming = this.isResuming;
|
|
698
891
|
if (cachedIsResuming || isCommandUpdateOrGoto) {
|
|
699
892
|
const interruptSeen = { ...this.checkpoint.versions_seen[INTERRUPT] };
|
|
@@ -702,9 +895,11 @@ var PregelLoop = class PregelLoop {
|
|
|
702
895
|
if (this.checkpoint.channel_versions[channelName] !== void 0) interruptSeen[channelName] = this.checkpoint.channel_versions[channelName];
|
|
703
896
|
}
|
|
704
897
|
this.checkpoint.versions_seen[INTERRUPT] = interruptSeen;
|
|
705
|
-
if (
|
|
898
|
+
if (forks) {
|
|
899
|
+
if (isUpdateOrFork && this.prevCheckpointConfig) replayBound = this.prevCheckpointConfig;
|
|
706
900
|
this.checkpointPendingWrites = this.checkpointPendingWrites.filter((w) => w[1] !== INTERRUPT);
|
|
707
901
|
await this._putCheckpoint({ source: "fork" });
|
|
902
|
+
if (nullResume !== void 0) this.putWrites(NULL_TASK_ID, [[RESUME, nullResume]]);
|
|
708
903
|
}
|
|
709
904
|
const valuesOutput = await gatherIterator(prefixGenerator(mapOutputValues(this.outputKeys, true, this.channels), "values"));
|
|
710
905
|
if (cachedIsResuming) this.input = INPUT_RESUMING;
|
|
@@ -716,25 +911,24 @@ var PregelLoop = class PregelLoop {
|
|
|
716
911
|
} else {
|
|
717
912
|
const inputWrites = await gatherIterator(mapInput(inputKeys, this.input));
|
|
718
913
|
if (inputWrites.length > 0) {
|
|
914
|
+
const deltaInput = ensureDeltaMessageIds(this.channels, inputWrites);
|
|
719
915
|
const discardTasks = _prepareNextTasks(this.checkpoint, this.checkpointPendingWrites, this.nodes, this.channels, this.config, true, { step: this.step });
|
|
720
916
|
this.updatedChannels = _applyWrites(this.checkpoint, this.channels, Object.values(discardTasks).concat([{
|
|
721
917
|
name: INPUT,
|
|
722
918
|
writes: inputWrites,
|
|
723
919
|
triggers: []
|
|
724
920
|
}]), this.checkpointerGetNextVersion, this.triggerToNodes);
|
|
725
|
-
const deltaInput
|
|
726
|
-
const channel = this.channels[c];
|
|
727
|
-
return channel != null && isDeltaChannel(channel);
|
|
728
|
-
});
|
|
729
|
-
for (const [c, v] of deltaInput) if (_isOverwriteValue(v)) this._deltaChannelsWithOverwrite.add(c);
|
|
921
|
+
for (const [c, v] of deltaInput) if (_isOverwriteValue(v)) this._deltaChannelsForcedSnapshot.add(c);
|
|
730
922
|
if (deltaInput.length > 0) {
|
|
731
923
|
if (this._exitDeltaWrites !== void 0) for (const [c, v] of deltaInput) this._exitDeltaWrites.push([
|
|
732
924
|
this.step,
|
|
733
925
|
NULL_TASK_ID,
|
|
926
|
+
"",
|
|
734
927
|
c,
|
|
735
928
|
v
|
|
736
929
|
]);
|
|
737
|
-
else if (this.checkpointer != null) this.putWrites(
|
|
930
|
+
else if (this.checkpointer != null) if (this._hasPersistedParent && this._loadedLatest) this.putWrites(uuid5(INPUT, this.checkpoint.id), deltaInput);
|
|
931
|
+
else for (const [c] of deltaInput) this._deltaChannelsForcedSnapshot.add(c);
|
|
738
932
|
}
|
|
739
933
|
await this._putCheckpoint({ source: "input" });
|
|
740
934
|
this.input = INPUT_DONE;
|
|
@@ -745,7 +939,8 @@ var PregelLoop = class PregelLoop {
|
|
|
745
939
|
let replayState;
|
|
746
940
|
if (isTimeTraveling) {
|
|
747
941
|
let replayCheckpointId = this.checkpoint.id;
|
|
748
|
-
if (
|
|
942
|
+
if (replayBound !== void 0) replayCheckpointId = replayBound.configurable?.["checkpoint_id"] ?? replayCheckpointId;
|
|
943
|
+
else if ((this.checkpointMetadata.source === "update" || this.checkpointMetadata.source === "fork") && this.prevCheckpointConfig) replayCheckpointId = this.prevCheckpointConfig.configurable?.["checkpoint_id"] ?? replayCheckpointId;
|
|
749
944
|
replayState = new ReplayState(replayCheckpointId);
|
|
750
945
|
}
|
|
751
946
|
this.config = patchConfigurable(this.config, {
|
|
@@ -855,15 +1050,7 @@ var PregelLoop = class PregelLoop {
|
|
|
855
1050
|
};
|
|
856
1051
|
let newCounters;
|
|
857
1052
|
if (!exiting) {
|
|
858
|
-
|
|
859
|
-
newCounters = {};
|
|
860
|
-
const updated = this.updatedChannels ?? /* @__PURE__ */ new Set();
|
|
861
|
-
for (const chName in this.channels) {
|
|
862
|
-
if (!Object.prototype.hasOwnProperty.call(this.channels, chName)) continue;
|
|
863
|
-
if (!isDeltaChannel(this.channels[chName])) continue;
|
|
864
|
-
const [u, s] = prevCounters[chName] ?? [0, 0];
|
|
865
|
-
newCounters[chName] = [updated.has(chName) ? u + 1 : u, s + 1];
|
|
866
|
-
}
|
|
1053
|
+
newCounters = advanceDeltaCounters(this.channels, this.updatedChannels ?? /* @__PURE__ */ new Set(), this.checkpointMetadata.counters_since_delta_snapshot);
|
|
867
1054
|
this.checkpointMetadata = {
|
|
868
1055
|
...inputMetadata,
|
|
869
1056
|
step: this.step,
|
|
@@ -872,16 +1059,17 @@ var PregelLoop = class PregelLoop {
|
|
|
872
1059
|
} else newCounters = { ...this.checkpointMetadata.counters_since_delta_snapshot ?? {} };
|
|
873
1060
|
if (this._deltaWritesVersioned) this.checkpointMetadata[DELTA_WRITES_VERSIONED] = true;
|
|
874
1061
|
const channelsToSnapshot = doCheckpoint ? deltaChannelsToSnapshot(this.channels, newCounters, this._deltaWritesVersioned ? this.checkpoint.channel_versions : void 0) : /* @__PURE__ */ new Set();
|
|
875
|
-
if (doCheckpoint) for (const ch of this.
|
|
1062
|
+
if (doCheckpoint) for (const ch of this._deltaChannelsForcedSnapshot) channelsToSnapshot.add(ch);
|
|
876
1063
|
this.checkpoint = createCheckpoint(this.checkpoint, doCheckpoint ? this.channels : void 0, this.step, {
|
|
877
1064
|
id: exiting ? this.checkpoint.id : void 0,
|
|
878
1065
|
channelsToSnapshot,
|
|
879
|
-
|
|
880
|
-
getNextVersion: doCheckpoint ? (current) => this.checkpointerGetNextVersion(current) : void 0
|
|
1066
|
+
storedVersions: this.checkpointPreviousVersions,
|
|
1067
|
+
getNextVersion: doCheckpoint ? (current) => this.checkpointerGetNextVersion(current) : void 0,
|
|
1068
|
+
triggerToNodes: this.triggerToNodes
|
|
881
1069
|
});
|
|
882
1070
|
for (const k of channelsToSnapshot) {
|
|
883
1071
|
newCounters[k] = [0, 0];
|
|
884
|
-
this.
|
|
1072
|
+
this._deltaChannelsForcedSnapshot.delete(k);
|
|
885
1073
|
}
|
|
886
1074
|
const nonZero = {};
|
|
887
1075
|
for (const k in newCounters) {
|
|
@@ -907,10 +1095,11 @@ var PregelLoop = class PregelLoop {
|
|
|
907
1095
|
*/
|
|
908
1096
|
async _putExitDeltaWrites() {
|
|
909
1097
|
if (this._exitDeltaWrites === void 0 || this._exitDeltaWrites.length === 0 || this.checkpointer == null || this._initialCheckpointConfig === void 0) return;
|
|
1098
|
+
if (this._hasPersistedParent && !this._loadedLatest) for (const [, , , ch] of this._exitDeltaWrites) this._deltaChannelsForcedSnapshot.add(ch);
|
|
910
1099
|
const counters = this.checkpointMetadata.counters_since_delta_snapshot ?? {};
|
|
911
1100
|
const channelsToSnapshot = deltaChannelsToSnapshot(this.channels, counters, this._deltaWritesVersioned ? this.checkpoint.channel_versions : void 0);
|
|
912
|
-
for (const ch of this.
|
|
913
|
-
const pending = this._exitDeltaWrites.filter(([, , ch]) => !channelsToSnapshot.has(ch));
|
|
1101
|
+
for (const ch of this._deltaChannelsForcedSnapshot) channelsToSnapshot.add(ch);
|
|
1102
|
+
const pending = this._exitDeltaWrites.filter(([, , , ch]) => !channelsToSnapshot.has(ch));
|
|
914
1103
|
if (pending.length === 0) return;
|
|
915
1104
|
let anchorConfig;
|
|
916
1105
|
if (this._hasPersistedParent) anchorConfig = this._initialCheckpointConfig;
|
|
@@ -920,11 +1109,16 @@ var PregelLoop = class PregelLoop {
|
|
|
920
1109
|
stubCp.ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
921
1110
|
const stubPutConfig = patchConfigurable(this._initialCheckpointConfig, { [CONFIG_KEY_CHECKPOINT_ID]: void 0 });
|
|
922
1111
|
anchorConfig = patchConfigurable(this._initialCheckpointConfig, { [CONFIG_KEY_CHECKPOINT_ID]: stubCp.id });
|
|
923
|
-
this.
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
1112
|
+
this._checkpointerPutAfterPrevious({
|
|
1113
|
+
config: stubPutConfig,
|
|
1114
|
+
checkpoint: stubCp,
|
|
1115
|
+
metadata: {
|
|
1116
|
+
source: "loop",
|
|
1117
|
+
step: -2,
|
|
1118
|
+
parents: {}
|
|
1119
|
+
},
|
|
1120
|
+
newVersions: {}
|
|
1121
|
+
});
|
|
928
1122
|
this.checkpointConfig = anchorConfig;
|
|
929
1123
|
}
|
|
930
1124
|
const anchorWriteConfig = patchConfigurable(anchorConfig, {
|
|
@@ -933,23 +1127,36 @@ var PregelLoop = class PregelLoop {
|
|
|
933
1127
|
});
|
|
934
1128
|
const grouped = /* @__PURE__ */ new Map();
|
|
935
1129
|
const order = [];
|
|
936
|
-
for (const [step, tid, ch, v] of pending) {
|
|
937
|
-
|
|
1130
|
+
for (const [step, tid, path, ch, v] of pending) {
|
|
1131
|
+
let synthTid;
|
|
1132
|
+
let synthPath;
|
|
1133
|
+
if (tid === "00000000-0000-0000-0000-000000000000") {
|
|
1134
|
+
synthTid = exitDeltaTaskId(step, tid);
|
|
1135
|
+
synthPath = "";
|
|
1136
|
+
} else if (step === this._exitFirstStep) {
|
|
1137
|
+
synthTid = exitDeltaTaskId(step, tid);
|
|
1138
|
+
synthPath = path;
|
|
1139
|
+
} else {
|
|
1140
|
+
synthTid = exitDeltaLateTaskId(step, tid);
|
|
1141
|
+
synthPath = `~~${taskPathStr(step)}${path}`;
|
|
1142
|
+
}
|
|
1143
|
+
const key = `${synthTid}\u0000${synthPath}`;
|
|
938
1144
|
let group = grouped.get(key);
|
|
939
1145
|
if (group === void 0) {
|
|
940
1146
|
group = [];
|
|
941
1147
|
grouped.set(key, group);
|
|
942
1148
|
order.push({
|
|
943
1149
|
key,
|
|
944
|
-
|
|
945
|
-
|
|
1150
|
+
tid: synthTid,
|
|
1151
|
+
path: synthPath
|
|
946
1152
|
});
|
|
947
1153
|
}
|
|
948
1154
|
group.push([ch, v]);
|
|
949
1155
|
}
|
|
950
|
-
for (const { key,
|
|
951
|
-
const
|
|
952
|
-
this._trackCheckpointerPromise(
|
|
1156
|
+
for (const { key, tid, path } of order) {
|
|
1157
|
+
const saved = this.checkpointer.putWrites(anchorWriteConfig, grouped.get(key), tid, path);
|
|
1158
|
+
this._trackCheckpointerPromise(saved);
|
|
1159
|
+
this._pendingDeltaWrites.push(saved);
|
|
953
1160
|
}
|
|
954
1161
|
}
|
|
955
1162
|
_flushPendingWrites() {
|
|
@@ -961,21 +1168,36 @@ var PregelLoop = class PregelLoop {
|
|
|
961
1168
|
});
|
|
962
1169
|
const byTask = {};
|
|
963
1170
|
for (const [tid, key, value] of this.checkpointPendingWrites) {
|
|
1171
|
+
if (!this._writtenTaskIds.has(tid)) continue;
|
|
964
1172
|
byTask[tid] ??= [];
|
|
965
1173
|
byTask[tid].push([key, value]);
|
|
966
1174
|
}
|
|
967
|
-
for (const [tid, ws] of Object.entries(byTask))
|
|
1175
|
+
for (const [tid, ws] of Object.entries(byTask)) {
|
|
1176
|
+
const task = this.tasks[tid];
|
|
1177
|
+
this._trackCheckpointerPromise(this.checkpointer.putWrites(config, ws, tid, task != null ? taskPathStr(task.path) : ""));
|
|
1178
|
+
}
|
|
968
1179
|
}
|
|
969
|
-
|
|
1180
|
+
/**
|
|
1181
|
+
* Hands the writes loaded with the checkpoint back to the finished tasks
|
|
1182
|
+
* among `tasks`. A failed or interrupted task keeps empty writes, so it
|
|
1183
|
+
* reruns or goes to its error handler.
|
|
1184
|
+
*/
|
|
1185
|
+
_reapplyWritesToSucceededNodes(tasks) {
|
|
970
1186
|
for (const [tid, k, v] of this.checkpointPendingWrites) {
|
|
971
|
-
if (k === "__error__" || k === "__interrupt__" || k === "__resume__") continue;
|
|
972
|
-
const task =
|
|
973
|
-
if (task)
|
|
1187
|
+
if (k === "__error__" || k === "__error_source_node__" || k === "__interrupt__" || k === "__resume__") continue;
|
|
1188
|
+
const task = tasks[tid];
|
|
1189
|
+
if (task) {
|
|
1190
|
+
task.writes.push([k, v]);
|
|
1191
|
+
this._reappliedTaskIds.add(tid);
|
|
1192
|
+
}
|
|
974
1193
|
}
|
|
1194
|
+
}
|
|
1195
|
+
_matchWrites(tasks) {
|
|
1196
|
+
this._reapplyWritesToSucceededNodes(tasks);
|
|
975
1197
|
for (const task of Object.values(tasks)) if (task.writes.length > 0) this._outputWrites(task.id, task.writes, true);
|
|
976
1198
|
}
|
|
977
1199
|
};
|
|
978
1200
|
//#endregion
|
|
979
|
-
export { PregelLoop };
|
|
1201
|
+
export { PregelLoop, ensureDeltaMessageIds };
|
|
980
1202
|
|
|
981
1203
|
//# sourceMappingURL=loop.js.map
|