@elaraai/e3-core 0.0.1-alpha.2
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/LICENSE.md +50 -0
- package/README.md +103 -0
- package/dist/src/dataflow.d.ts +136 -0
- package/dist/src/dataflow.d.ts.map +1 -0
- package/dist/src/dataflow.js +562 -0
- package/dist/src/dataflow.js.map +1 -0
- package/dist/src/errors.d.ts +125 -0
- package/dist/src/errors.d.ts.map +1 -0
- package/dist/src/errors.js +211 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/executions.d.ts +176 -0
- package/dist/src/executions.d.ts.map +1 -0
- package/dist/src/executions.js +585 -0
- package/dist/src/executions.js.map +1 -0
- package/dist/src/formats.d.ts +38 -0
- package/dist/src/formats.d.ts.map +1 -0
- package/dist/src/formats.js +115 -0
- package/dist/src/formats.js.map +1 -0
- package/dist/src/gc.d.ts +54 -0
- package/dist/src/gc.d.ts.map +1 -0
- package/dist/src/gc.js +233 -0
- package/dist/src/gc.js.map +1 -0
- package/dist/src/index.d.ts +25 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +72 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/objects.d.ts +62 -0
- package/dist/src/objects.d.ts.map +1 -0
- package/dist/src/objects.js +245 -0
- package/dist/src/objects.js.map +1 -0
- package/dist/src/packages.d.ts +93 -0
- package/dist/src/packages.d.ts.map +1 -0
- package/dist/src/packages.js +370 -0
- package/dist/src/packages.js.map +1 -0
- package/dist/src/repository.d.ts +38 -0
- package/dist/src/repository.d.ts.map +1 -0
- package/dist/src/repository.js +103 -0
- package/dist/src/repository.js.map +1 -0
- package/dist/src/tasks.d.ts +63 -0
- package/dist/src/tasks.d.ts.map +1 -0
- package/dist/src/tasks.js +145 -0
- package/dist/src/tasks.js.map +1 -0
- package/dist/src/test-helpers.d.ts +44 -0
- package/dist/src/test-helpers.d.ts.map +1 -0
- package/dist/src/test-helpers.js +141 -0
- package/dist/src/test-helpers.js.map +1 -0
- package/dist/src/trees.d.ts +178 -0
- package/dist/src/trees.d.ts.map +1 -0
- package/dist/src/trees.js +636 -0
- package/dist/src/trees.js.map +1 -0
- package/dist/src/workspaceLock.d.ts +67 -0
- package/dist/src/workspaceLock.d.ts.map +1 -0
- package/dist/src/workspaceLock.js +217 -0
- package/dist/src/workspaceLock.js.map +1 -0
- package/dist/src/workspaceStatus.d.ts +126 -0
- package/dist/src/workspaceStatus.d.ts.map +1 -0
- package/dist/src/workspaceStatus.js +352 -0
- package/dist/src/workspaceStatus.js.map +1 -0
- package/dist/src/workspaces.d.ts +150 -0
- package/dist/src/workspaces.d.ts.map +1 -0
- package/dist/src/workspaces.js +390 -0
- package/dist/src/workspaces.js.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1,562 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Elara AI Pty Ltd
|
|
3
|
+
* Licensed under BSL 1.1. See LICENSE for details.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Dataflow execution for e3 workspaces.
|
|
7
|
+
*
|
|
8
|
+
* Executes tasks in a workspace based on their dependency graph. Tasks are
|
|
9
|
+
* executed in parallel where possible, respecting a concurrency limit.
|
|
10
|
+
*
|
|
11
|
+
* The execution model is event-driven with a work queue:
|
|
12
|
+
* 1. Build dependency graph from tasks (input paths -> task -> output path)
|
|
13
|
+
* 2. Compute reverse dependencies (which tasks depend on each output)
|
|
14
|
+
* 3. Initialize ready queue with tasks whose inputs are all assigned
|
|
15
|
+
* 4. Execute tasks from ready queue, respecting concurrency limit
|
|
16
|
+
* 5. On task completion, queue workspace update then check dependents for readiness
|
|
17
|
+
* 6. On failure, stop launching new tasks but wait for running ones
|
|
18
|
+
*
|
|
19
|
+
* IMPORTANT: Workspace state updates are serialized through an async queue to
|
|
20
|
+
* prevent race conditions when multiple tasks complete concurrently. Each task's
|
|
21
|
+
* output is written to the workspace and dependents are notified only after the
|
|
22
|
+
* write completes, ensuring downstream tasks see consistent state.
|
|
23
|
+
*/
|
|
24
|
+
import { decodeBeast2For } from '@elaraai/east';
|
|
25
|
+
import { PackageObjectType, TaskObjectType, WorkspaceStateType, pathToString, } from '@elaraai/e3-types';
|
|
26
|
+
import { objectRead } from './objects.js';
|
|
27
|
+
import { taskExecute, executionGetOutput, inputsHash, } from './executions.js';
|
|
28
|
+
import { workspaceGetDatasetHash, workspaceSetDatasetByHash, } from './trees.js';
|
|
29
|
+
import { E3Error, WorkspaceNotFoundError, WorkspaceNotDeployedError, TaskNotFoundError, DataflowError, DataflowAbortedError, isNotFoundError, } from './errors.js';
|
|
30
|
+
import { acquireWorkspaceLock, } from './workspaceLock.js';
|
|
31
|
+
import * as fs from 'fs/promises';
|
|
32
|
+
import * as path from 'path';
|
|
33
|
+
// =============================================================================
|
|
34
|
+
// Async Mutex for Workspace Updates
|
|
35
|
+
// =============================================================================
|
|
36
|
+
/**
|
|
37
|
+
* Simple async mutex to serialize workspace state updates.
|
|
38
|
+
*
|
|
39
|
+
* When multiple tasks complete concurrently, their workspace writes must be
|
|
40
|
+
* serialized to prevent race conditions (read-modify-write on the workspace
|
|
41
|
+
* root hash). This mutex ensures only one update runs at a time.
|
|
42
|
+
*/
|
|
43
|
+
class AsyncMutex {
|
|
44
|
+
queue = [];
|
|
45
|
+
locked = false;
|
|
46
|
+
/**
|
|
47
|
+
* Acquire the mutex, execute the callback, then release.
|
|
48
|
+
* If the mutex is already held, waits until it's available.
|
|
49
|
+
*/
|
|
50
|
+
async runExclusive(fn) {
|
|
51
|
+
await this.acquire();
|
|
52
|
+
try {
|
|
53
|
+
return await fn();
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
this.release();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
acquire() {
|
|
60
|
+
return new Promise((resolve) => {
|
|
61
|
+
if (!this.locked) {
|
|
62
|
+
this.locked = true;
|
|
63
|
+
resolve();
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
this.queue.push(resolve);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
release() {
|
|
71
|
+
const next = this.queue.shift();
|
|
72
|
+
if (next) {
|
|
73
|
+
next();
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
this.locked = false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// =============================================================================
|
|
81
|
+
// Workspace State Reader
|
|
82
|
+
// =============================================================================
|
|
83
|
+
/**
|
|
84
|
+
* Read workspace state from file.
|
|
85
|
+
* @throws {WorkspaceNotFoundError} If workspace doesn't exist
|
|
86
|
+
* @throws {WorkspaceNotDeployedError} If workspace has no package deployed
|
|
87
|
+
*/
|
|
88
|
+
async function readWorkspaceState(repoPath, ws) {
|
|
89
|
+
const stateFile = path.join(repoPath, 'workspaces', `${ws}.beast2`);
|
|
90
|
+
let data;
|
|
91
|
+
try {
|
|
92
|
+
data = await fs.readFile(stateFile);
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
if (isNotFoundError(err)) {
|
|
96
|
+
throw new WorkspaceNotFoundError(ws);
|
|
97
|
+
}
|
|
98
|
+
throw err;
|
|
99
|
+
}
|
|
100
|
+
if (data.length === 0) {
|
|
101
|
+
throw new WorkspaceNotDeployedError(ws);
|
|
102
|
+
}
|
|
103
|
+
const decoder = decodeBeast2For(WorkspaceStateType);
|
|
104
|
+
return decoder(data);
|
|
105
|
+
}
|
|
106
|
+
// =============================================================================
|
|
107
|
+
// Dependency Graph Building
|
|
108
|
+
// =============================================================================
|
|
109
|
+
/**
|
|
110
|
+
* Build the dependency graph for a workspace.
|
|
111
|
+
*
|
|
112
|
+
* Returns:
|
|
113
|
+
* - taskNodes: Map of task name -> TaskNode
|
|
114
|
+
* - outputToTask: Map of output path string -> task name
|
|
115
|
+
* - taskDependents: Map of task name -> set of dependent task names
|
|
116
|
+
*/
|
|
117
|
+
async function buildDependencyGraph(repoPath, ws) {
|
|
118
|
+
// Read workspace state to get package hash
|
|
119
|
+
const state = await readWorkspaceState(repoPath, ws);
|
|
120
|
+
// Read package object to get tasks map
|
|
121
|
+
const pkgData = await objectRead(repoPath, state.packageHash);
|
|
122
|
+
const pkgDecoder = decodeBeast2For(PackageObjectType);
|
|
123
|
+
const pkgObject = pkgDecoder(Buffer.from(pkgData));
|
|
124
|
+
const taskNodes = new Map();
|
|
125
|
+
const outputToTask = new Map(); // output path -> task name
|
|
126
|
+
// First pass: load all tasks and build output->task map
|
|
127
|
+
const taskDecoder = decodeBeast2For(TaskObjectType);
|
|
128
|
+
for (const [taskName, taskHash] of pkgObject.tasks) {
|
|
129
|
+
const taskData = await objectRead(repoPath, taskHash);
|
|
130
|
+
const task = taskDecoder(Buffer.from(taskData));
|
|
131
|
+
const outputPathStr = pathToString(task.output);
|
|
132
|
+
outputToTask.set(outputPathStr, taskName);
|
|
133
|
+
taskNodes.set(taskName, {
|
|
134
|
+
name: taskName,
|
|
135
|
+
hash: taskHash,
|
|
136
|
+
task,
|
|
137
|
+
inputPaths: task.inputs,
|
|
138
|
+
outputPath: task.output,
|
|
139
|
+
unresolvedCount: 0, // Will be computed below
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
// Build reverse dependency map: task -> tasks that depend on it
|
|
143
|
+
const taskDependents = new Map();
|
|
144
|
+
for (const taskName of taskNodes.keys()) {
|
|
145
|
+
taskDependents.set(taskName, new Set());
|
|
146
|
+
}
|
|
147
|
+
// Second pass: compute dependencies and unresolved counts
|
|
148
|
+
for (const [taskName, node] of taskNodes) {
|
|
149
|
+
for (const inputPath of node.inputPaths) {
|
|
150
|
+
const inputPathStr = pathToString(inputPath);
|
|
151
|
+
const producerTask = outputToTask.get(inputPathStr);
|
|
152
|
+
if (producerTask) {
|
|
153
|
+
// This input comes from another task's output.
|
|
154
|
+
// The task cannot run until the producer task completes,
|
|
155
|
+
// regardless of whether the output is currently assigned
|
|
156
|
+
// (it might be stale from a previous run).
|
|
157
|
+
taskDependents.get(producerTask).add(taskName);
|
|
158
|
+
node.unresolvedCount++;
|
|
159
|
+
}
|
|
160
|
+
// If not produced by a task, it's an external input - check if assigned
|
|
161
|
+
else {
|
|
162
|
+
const { refType } = await workspaceGetDatasetHash(repoPath, ws, inputPath);
|
|
163
|
+
if (refType === 'unassigned') {
|
|
164
|
+
// External input that is unassigned - this task can never run
|
|
165
|
+
node.unresolvedCount++;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return { taskNodes, outputToTask, taskDependents };
|
|
171
|
+
}
|
|
172
|
+
// =============================================================================
|
|
173
|
+
// Dataflow Execution
|
|
174
|
+
// =============================================================================
|
|
175
|
+
/**
|
|
176
|
+
* Execute all tasks in a workspace according to the dependency graph.
|
|
177
|
+
*
|
|
178
|
+
* Tasks are executed in parallel where dependencies allow, respecting
|
|
179
|
+
* the concurrency limit. On failure, no new tasks are launched but
|
|
180
|
+
* running tasks are allowed to complete.
|
|
181
|
+
*
|
|
182
|
+
* Acquires an exclusive lock on the workspace for the duration of execution
|
|
183
|
+
* to prevent concurrent modifications. If options.lock is provided, uses that
|
|
184
|
+
* lock instead (caller is responsible for releasing it).
|
|
185
|
+
*
|
|
186
|
+
* @param repoPath - Path to .e3 repository
|
|
187
|
+
* @param ws - Workspace name
|
|
188
|
+
* @param options - Execution options
|
|
189
|
+
* @returns Result of the dataflow execution
|
|
190
|
+
* @throws {WorkspaceLockError} If workspace is locked by another process
|
|
191
|
+
* @throws {WorkspaceNotFoundError} If workspace doesn't exist
|
|
192
|
+
* @throws {WorkspaceNotDeployedError} If workspace has no package deployed
|
|
193
|
+
* @throws {TaskNotFoundError} If filter specifies a task that doesn't exist
|
|
194
|
+
* @throws {DataflowError} If execution fails for other reasons
|
|
195
|
+
*/
|
|
196
|
+
export async function dataflowExecute(repoPath, ws, options = {}) {
|
|
197
|
+
// Acquire lock if not provided externally
|
|
198
|
+
const externalLock = options.lock;
|
|
199
|
+
const lock = externalLock ?? await acquireWorkspaceLock(repoPath, ws);
|
|
200
|
+
try {
|
|
201
|
+
return await dataflowExecuteWithLock(repoPath, ws, options);
|
|
202
|
+
}
|
|
203
|
+
finally {
|
|
204
|
+
// Only release the lock if we acquired it internally
|
|
205
|
+
if (!externalLock) {
|
|
206
|
+
await lock.release();
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Start dataflow execution in the background (non-blocking).
|
|
212
|
+
*
|
|
213
|
+
* Returns a promise immediately without awaiting execution. The lock is
|
|
214
|
+
* released automatically when execution completes.
|
|
215
|
+
*
|
|
216
|
+
* @param repoPath - Path to .e3 repository
|
|
217
|
+
* @param ws - Workspace name
|
|
218
|
+
* @param options - Execution options (lock must be provided)
|
|
219
|
+
* @returns Promise that resolves when execution completes
|
|
220
|
+
* @throws {WorkspaceNotFoundError} If workspace doesn't exist
|
|
221
|
+
* @throws {WorkspaceNotDeployedError} If workspace has no package deployed
|
|
222
|
+
* @throws {TaskNotFoundError} If filter specifies a task that doesn't exist
|
|
223
|
+
* @throws {DataflowError} If execution fails for other reasons
|
|
224
|
+
*/
|
|
225
|
+
export function dataflowStart(repoPath, ws, options) {
|
|
226
|
+
return dataflowExecuteWithLock(repoPath, ws, options)
|
|
227
|
+
.finally(() => options.lock.release());
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Internal: Execute dataflow with lock already held.
|
|
231
|
+
*/
|
|
232
|
+
async function dataflowExecuteWithLock(repoPath, ws, options) {
|
|
233
|
+
const startTime = Date.now();
|
|
234
|
+
const concurrency = options.concurrency ?? 4;
|
|
235
|
+
let taskNodes;
|
|
236
|
+
let taskDependents;
|
|
237
|
+
try {
|
|
238
|
+
// Build dependency graph
|
|
239
|
+
const graph = await buildDependencyGraph(repoPath, ws);
|
|
240
|
+
taskNodes = graph.taskNodes;
|
|
241
|
+
taskDependents = graph.taskDependents;
|
|
242
|
+
}
|
|
243
|
+
catch (err) {
|
|
244
|
+
// Re-throw E3Errors as-is
|
|
245
|
+
if (err instanceof E3Error)
|
|
246
|
+
throw err;
|
|
247
|
+
// Wrap unexpected errors
|
|
248
|
+
throw new DataflowError(`Failed to build dependency graph: ${err instanceof Error ? err.message : err}`);
|
|
249
|
+
}
|
|
250
|
+
// Apply filter if specified
|
|
251
|
+
const filteredTaskNames = options.filter
|
|
252
|
+
? new Set([options.filter])
|
|
253
|
+
: null;
|
|
254
|
+
// Validate filter
|
|
255
|
+
if (filteredTaskNames && options.filter && !taskNodes.has(options.filter)) {
|
|
256
|
+
throw new TaskNotFoundError(options.filter);
|
|
257
|
+
}
|
|
258
|
+
// Track execution state
|
|
259
|
+
const results = [];
|
|
260
|
+
let executed = 0;
|
|
261
|
+
let cached = 0;
|
|
262
|
+
let failed = 0;
|
|
263
|
+
let skipped = 0;
|
|
264
|
+
let hasFailure = false;
|
|
265
|
+
let aborted = false;
|
|
266
|
+
// Check for abort signal
|
|
267
|
+
const checkAborted = () => {
|
|
268
|
+
if (options.signal?.aborted && !aborted) {
|
|
269
|
+
aborted = true;
|
|
270
|
+
}
|
|
271
|
+
return aborted;
|
|
272
|
+
};
|
|
273
|
+
// Mutex to serialize workspace state updates.
|
|
274
|
+
// When multiple tasks complete concurrently, their writes to the workspace
|
|
275
|
+
// must be serialized to prevent lost updates (read-modify-write race).
|
|
276
|
+
const workspaceUpdateMutex = new AsyncMutex();
|
|
277
|
+
// Ready queue: tasks with all dependencies resolved
|
|
278
|
+
const readyQueue = [];
|
|
279
|
+
const completed = new Set();
|
|
280
|
+
const inProgress = new Set();
|
|
281
|
+
// Initialize ready queue with tasks that have no unresolved dependencies
|
|
282
|
+
// and pass the filter (if any)
|
|
283
|
+
for (const [taskName, node] of taskNodes) {
|
|
284
|
+
if (node.unresolvedCount === 0) {
|
|
285
|
+
if (!filteredTaskNames || filteredTaskNames.has(taskName)) {
|
|
286
|
+
readyQueue.push(taskName);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
// Check if the task has a valid cached execution for current inputs
|
|
291
|
+
// Returns the output hash if cached, null if re-execution is needed
|
|
292
|
+
async function getCachedOutput(taskName) {
|
|
293
|
+
const node = taskNodes.get(taskName);
|
|
294
|
+
// Gather current input hashes
|
|
295
|
+
const currentInputHashes = [];
|
|
296
|
+
for (const inputPath of node.inputPaths) {
|
|
297
|
+
const { refType, hash } = await workspaceGetDatasetHash(repoPath, ws, inputPath);
|
|
298
|
+
if (refType !== 'value' || hash === null) {
|
|
299
|
+
// Input not assigned, can't be cached
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
currentInputHashes.push(hash);
|
|
303
|
+
}
|
|
304
|
+
// Check if there's a cached execution for these inputs
|
|
305
|
+
const inHash = inputsHash(currentInputHashes);
|
|
306
|
+
const cachedOutputHash = await executionGetOutput(repoPath, node.hash, inHash);
|
|
307
|
+
if (cachedOutputHash === null) {
|
|
308
|
+
// No cached execution for current inputs
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
// Also verify the workspace output matches the cached output
|
|
312
|
+
// (in case the workspace was modified outside of execution)
|
|
313
|
+
const { refType, hash: wsOutputHash } = await workspaceGetDatasetHash(repoPath, ws, node.outputPath);
|
|
314
|
+
if (refType !== 'value' || wsOutputHash !== cachedOutputHash) {
|
|
315
|
+
// Workspace output doesn't match cached output, need to re-execute
|
|
316
|
+
// (or update workspace with cached value)
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
return cachedOutputHash;
|
|
320
|
+
}
|
|
321
|
+
// Execute a single task (does NOT write to workspace - caller must do that)
|
|
322
|
+
async function executeTask(taskName) {
|
|
323
|
+
const node = taskNodes.get(taskName);
|
|
324
|
+
const taskStartTime = Date.now();
|
|
325
|
+
options.onTaskStart?.(taskName);
|
|
326
|
+
// Gather input hashes
|
|
327
|
+
const inputHashes = [];
|
|
328
|
+
for (const inputPath of node.inputPaths) {
|
|
329
|
+
const { refType, hash } = await workspaceGetDatasetHash(repoPath, ws, inputPath);
|
|
330
|
+
if (refType !== 'value' || hash === null) {
|
|
331
|
+
// Input not available - should not happen if dependency tracking is correct
|
|
332
|
+
return {
|
|
333
|
+
name: taskName,
|
|
334
|
+
cached: false,
|
|
335
|
+
state: 'error',
|
|
336
|
+
error: `Input at ${pathToString(inputPath)} is not assigned (refType: ${refType})`,
|
|
337
|
+
duration: Date.now() - taskStartTime,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
inputHashes.push(hash);
|
|
341
|
+
}
|
|
342
|
+
// Execute the task
|
|
343
|
+
const execOptions = {
|
|
344
|
+
force: options.force,
|
|
345
|
+
signal: options.signal,
|
|
346
|
+
onStdout: options.onStdout ? (data) => options.onStdout(taskName, data) : undefined,
|
|
347
|
+
onStderr: options.onStderr ? (data) => options.onStderr(taskName, data) : undefined,
|
|
348
|
+
};
|
|
349
|
+
const result = await taskExecute(repoPath, node.hash, inputHashes, execOptions);
|
|
350
|
+
// Build task result (NOTE: workspace update happens later, in mutex-protected section)
|
|
351
|
+
const taskResult = {
|
|
352
|
+
name: taskName,
|
|
353
|
+
cached: result.cached,
|
|
354
|
+
state: result.state,
|
|
355
|
+
duration: Date.now() - taskStartTime,
|
|
356
|
+
};
|
|
357
|
+
if (result.state === 'error') {
|
|
358
|
+
taskResult.error = result.error ?? undefined;
|
|
359
|
+
}
|
|
360
|
+
else if (result.state === 'failed') {
|
|
361
|
+
taskResult.exitCode = result.exitCode ?? undefined;
|
|
362
|
+
taskResult.error = result.error ?? undefined;
|
|
363
|
+
}
|
|
364
|
+
// Pass output hash to caller for workspace update (if successful)
|
|
365
|
+
if (result.state === 'success' && result.outputHash) {
|
|
366
|
+
taskResult.outputHash = result.outputHash;
|
|
367
|
+
}
|
|
368
|
+
return taskResult;
|
|
369
|
+
}
|
|
370
|
+
// Process dependents when a task completes
|
|
371
|
+
function notifyDependents(taskName) {
|
|
372
|
+
const dependents = taskDependents.get(taskName) ?? new Set();
|
|
373
|
+
for (const depName of dependents) {
|
|
374
|
+
if (completed.has(depName) || inProgress.has(depName))
|
|
375
|
+
continue;
|
|
376
|
+
// Skip dependents not in the filter
|
|
377
|
+
if (filteredTaskNames && !filteredTaskNames.has(depName))
|
|
378
|
+
continue;
|
|
379
|
+
const depNode = taskNodes.get(depName);
|
|
380
|
+
depNode.unresolvedCount--;
|
|
381
|
+
if (depNode.unresolvedCount === 0 && !readyQueue.includes(depName)) {
|
|
382
|
+
readyQueue.push(depName);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
// Mark dependents as skipped when a task fails
|
|
387
|
+
function skipDependents(taskName) {
|
|
388
|
+
const dependents = taskDependents.get(taskName) ?? new Set();
|
|
389
|
+
for (const depName of dependents) {
|
|
390
|
+
if (completed.has(depName) || inProgress.has(depName))
|
|
391
|
+
continue;
|
|
392
|
+
// Skip dependents not in the filter
|
|
393
|
+
if (filteredTaskNames && !filteredTaskNames.has(depName))
|
|
394
|
+
continue;
|
|
395
|
+
// Recursively skip
|
|
396
|
+
completed.add(depName);
|
|
397
|
+
skipped++;
|
|
398
|
+
results.push({
|
|
399
|
+
name: depName,
|
|
400
|
+
cached: false,
|
|
401
|
+
state: 'skipped',
|
|
402
|
+
duration: 0,
|
|
403
|
+
});
|
|
404
|
+
options.onTaskComplete?.({
|
|
405
|
+
name: depName,
|
|
406
|
+
cached: false,
|
|
407
|
+
state: 'skipped',
|
|
408
|
+
duration: 0,
|
|
409
|
+
});
|
|
410
|
+
skipDependents(depName);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
// Main execution loop using a work-stealing approach
|
|
414
|
+
const runningPromises = new Map();
|
|
415
|
+
async function processQueue() {
|
|
416
|
+
while (true) {
|
|
417
|
+
// Check if we're done
|
|
418
|
+
if (readyQueue.length === 0 && runningPromises.size === 0) {
|
|
419
|
+
break;
|
|
420
|
+
}
|
|
421
|
+
// Launch tasks up to concurrency limit if no failure and not aborted
|
|
422
|
+
while (!hasFailure && !checkAborted() && readyQueue.length > 0 && runningPromises.size < concurrency) {
|
|
423
|
+
const taskName = readyQueue.shift();
|
|
424
|
+
if (completed.has(taskName) || inProgress.has(taskName))
|
|
425
|
+
continue;
|
|
426
|
+
// Check if there's a valid cached execution for current inputs
|
|
427
|
+
const cachedOutputHash = await getCachedOutput(taskName);
|
|
428
|
+
if (cachedOutputHash !== null && !options.force) {
|
|
429
|
+
// Valid cached execution exists for current inputs.
|
|
430
|
+
// No workspace write needed (output already matches), but we still
|
|
431
|
+
// need mutex protection for state updates to prevent races with
|
|
432
|
+
// concurrent task completions.
|
|
433
|
+
await workspaceUpdateMutex.runExclusive(() => {
|
|
434
|
+
completed.add(taskName);
|
|
435
|
+
cached++;
|
|
436
|
+
const result = {
|
|
437
|
+
name: taskName,
|
|
438
|
+
cached: true,
|
|
439
|
+
state: 'success',
|
|
440
|
+
duration: 0,
|
|
441
|
+
};
|
|
442
|
+
results.push(result);
|
|
443
|
+
options.onTaskComplete?.(result);
|
|
444
|
+
notifyDependents(taskName);
|
|
445
|
+
});
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
inProgress.add(taskName);
|
|
449
|
+
const promise = (async () => {
|
|
450
|
+
try {
|
|
451
|
+
const result = await executeTask(taskName);
|
|
452
|
+
// Use mutex to serialize workspace updates and dependent notifications.
|
|
453
|
+
// This prevents race conditions where two tasks complete simultaneously,
|
|
454
|
+
// both read the same workspace state, and one overwrites the other's changes.
|
|
455
|
+
await workspaceUpdateMutex.runExclusive(async () => {
|
|
456
|
+
// Write output to workspace BEFORE notifying dependents
|
|
457
|
+
if (result.state === 'success' && result.outputHash) {
|
|
458
|
+
const node = taskNodes.get(taskName);
|
|
459
|
+
await workspaceSetDatasetByHash(repoPath, ws, node.outputPath, result.outputHash);
|
|
460
|
+
}
|
|
461
|
+
// Now safe to update execution state and notify dependents
|
|
462
|
+
inProgress.delete(taskName);
|
|
463
|
+
completed.add(taskName);
|
|
464
|
+
results.push(result);
|
|
465
|
+
options.onTaskComplete?.(result);
|
|
466
|
+
if (result.state === 'success') {
|
|
467
|
+
if (result.cached) {
|
|
468
|
+
cached++;
|
|
469
|
+
}
|
|
470
|
+
else {
|
|
471
|
+
executed++;
|
|
472
|
+
}
|
|
473
|
+
notifyDependents(taskName);
|
|
474
|
+
}
|
|
475
|
+
else {
|
|
476
|
+
failed++;
|
|
477
|
+
hasFailure = true;
|
|
478
|
+
skipDependents(taskName);
|
|
479
|
+
}
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
finally {
|
|
483
|
+
runningPromises.delete(taskName);
|
|
484
|
+
}
|
|
485
|
+
})();
|
|
486
|
+
runningPromises.set(taskName, promise);
|
|
487
|
+
}
|
|
488
|
+
// Wait for at least one task to complete if we can't launch more
|
|
489
|
+
if (runningPromises.size > 0) {
|
|
490
|
+
await Promise.race(runningPromises.values());
|
|
491
|
+
}
|
|
492
|
+
else if (readyQueue.length === 0) {
|
|
493
|
+
// No running tasks and no ready tasks - we might have unresolvable dependencies
|
|
494
|
+
break;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
await processQueue();
|
|
499
|
+
// Wait for any remaining tasks
|
|
500
|
+
if (runningPromises.size > 0) {
|
|
501
|
+
await Promise.all(runningPromises.values());
|
|
502
|
+
}
|
|
503
|
+
// Check for abort one final time
|
|
504
|
+
checkAborted();
|
|
505
|
+
// If aborted, throw with partial results
|
|
506
|
+
if (aborted) {
|
|
507
|
+
throw new DataflowAbortedError(results);
|
|
508
|
+
}
|
|
509
|
+
return {
|
|
510
|
+
success: !hasFailure,
|
|
511
|
+
executed,
|
|
512
|
+
cached,
|
|
513
|
+
failed,
|
|
514
|
+
skipped,
|
|
515
|
+
tasks: results,
|
|
516
|
+
duration: Date.now() - startTime,
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* Get the dependency graph for a workspace (for visualization/debugging).
|
|
521
|
+
*
|
|
522
|
+
* @param repoPath - Path to .e3 repository
|
|
523
|
+
* @param ws - Workspace name
|
|
524
|
+
* @returns Graph information
|
|
525
|
+
* @throws {WorkspaceNotFoundError} If workspace doesn't exist
|
|
526
|
+
* @throws {WorkspaceNotDeployedError} If workspace has no package deployed
|
|
527
|
+
* @throws {DataflowError} If graph building fails for other reasons
|
|
528
|
+
*/
|
|
529
|
+
export async function dataflowGetGraph(repoPath, ws) {
|
|
530
|
+
let taskNodes;
|
|
531
|
+
let outputToTask;
|
|
532
|
+
try {
|
|
533
|
+
const graph = await buildDependencyGraph(repoPath, ws);
|
|
534
|
+
taskNodes = graph.taskNodes;
|
|
535
|
+
outputToTask = graph.outputToTask;
|
|
536
|
+
}
|
|
537
|
+
catch (err) {
|
|
538
|
+
if (err instanceof E3Error)
|
|
539
|
+
throw err;
|
|
540
|
+
throw new DataflowError(`Failed to build dependency graph: ${err instanceof Error ? err.message : err}`);
|
|
541
|
+
}
|
|
542
|
+
const tasks = [];
|
|
543
|
+
for (const [taskName, node] of taskNodes) {
|
|
544
|
+
const dependsOn = [];
|
|
545
|
+
for (const inputPath of node.inputPaths) {
|
|
546
|
+
const inputPathStr = pathToString(inputPath);
|
|
547
|
+
const producerTask = outputToTask.get(inputPathStr);
|
|
548
|
+
if (producerTask) {
|
|
549
|
+
dependsOn.push(producerTask);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
tasks.push({
|
|
553
|
+
name: taskName,
|
|
554
|
+
hash: node.hash,
|
|
555
|
+
inputs: node.inputPaths.map(pathToString),
|
|
556
|
+
output: pathToString(node.outputPath),
|
|
557
|
+
dependsOn,
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
return { tasks };
|
|
561
|
+
}
|
|
562
|
+
//# sourceMappingURL=dataflow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dataflow.js","sourceRoot":"","sources":["../../src/dataflow.ts"],"names":[],"mappings":"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"}
|