@aigne/afs-scheduler 1.12.0-beta.5
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 +26 -0
- package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.cjs +11 -0
- package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs +10 -0
- package/dist/cron-parser.cjs +202 -0
- package/dist/cron-parser.d.cts +65 -0
- package/dist/cron-parser.d.cts.map +1 -0
- package/dist/cron-parser.d.mts +65 -0
- package/dist/cron-parser.d.mts.map +1 -0
- package/dist/cron-parser.mjs +200 -0
- package/dist/cron-parser.mjs.map +1 -0
- package/dist/do-executor.cjs +59 -0
- package/dist/do-executor.d.cts +30 -0
- package/dist/do-executor.d.cts.map +1 -0
- package/dist/do-executor.d.mts +30 -0
- package/dist/do-executor.d.mts.map +1 -0
- package/dist/do-executor.mjs +59 -0
- package/dist/do-executor.mjs.map +1 -0
- package/dist/do-job-executor.cjs +53 -0
- package/dist/do-job-executor.d.cts +28 -0
- package/dist/do-job-executor.d.cts.map +1 -0
- package/dist/do-job-executor.d.mts +28 -0
- package/dist/do-job-executor.d.mts.map +1 -0
- package/dist/do-job-executor.mjs +53 -0
- package/dist/do-job-executor.mjs.map +1 -0
- package/dist/do-types.d.cts +66 -0
- package/dist/do-types.d.cts.map +1 -0
- package/dist/do-types.d.mts +66 -0
- package/dist/do-types.d.mts.map +1 -0
- package/dist/index.cjs +34 -0
- package/dist/index.d.cts +11 -0
- package/dist/index.d.mts +11 -0
- package/dist/index.mjs +11 -0
- package/dist/job-coordinator.cjs +216 -0
- package/dist/job-coordinator.d.cts +8 -0
- package/dist/job-coordinator.d.cts.map +1 -0
- package/dist/job-coordinator.d.mts +8 -0
- package/dist/job-coordinator.d.mts.map +1 -0
- package/dist/job-coordinator.mjs +216 -0
- package/dist/job-coordinator.mjs.map +1 -0
- package/dist/local-executor.cjs +25 -0
- package/dist/local-executor.d.cts +12 -0
- package/dist/local-executor.d.cts.map +1 -0
- package/dist/local-executor.d.mts +12 -0
- package/dist/local-executor.d.mts.map +1 -0
- package/dist/local-executor.mjs +25 -0
- package/dist/local-executor.mjs.map +1 -0
- package/dist/scheduler-provider.cjs +1236 -0
- package/dist/scheduler-provider.d.cts +95 -0
- package/dist/scheduler-provider.d.cts.map +1 -0
- package/dist/scheduler-provider.d.mts +95 -0
- package/dist/scheduler-provider.d.mts.map +1 -0
- package/dist/scheduler-provider.mjs +1237 -0
- package/dist/scheduler-provider.mjs.map +1 -0
- package/dist/task-runner.cjs +70 -0
- package/dist/task-runner.d.cts +25 -0
- package/dist/task-runner.d.cts.map +1 -0
- package/dist/task-runner.d.mts +25 -0
- package/dist/task-runner.d.mts.map +1 -0
- package/dist/task-runner.mjs +70 -0
- package/dist/task-runner.mjs.map +1 -0
- package/dist/task-store.cjs +186 -0
- package/dist/task-store.d.cts +60 -0
- package/dist/task-store.d.cts.map +1 -0
- package/dist/task-store.d.mts +60 -0
- package/dist/task-store.d.mts.map +1 -0
- package/dist/task-store.mjs +187 -0
- package/dist/task-store.mjs.map +1 -0
- package/dist/types.cjs +185 -0
- package/dist/types.d.cts +384 -0
- package/dist/types.d.cts.map +1 -0
- package/dist/types.d.mts +384 -0
- package/dist/types.d.mts.map +1 -0
- package/dist/types.mjs +176 -0
- package/dist/types.mjs.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
let _aigne_uuid = require("@aigne/uuid");
|
|
2
|
+
|
|
3
|
+
//#region src/job-coordinator.ts
|
|
4
|
+
/**
|
|
5
|
+
* Job Coordinator DO — wave-based map-reduce orchestration.
|
|
6
|
+
*
|
|
7
|
+
* Runs inside a Durable Object. Each job gets its own Coordinator DO
|
|
8
|
+
* (idFromName(jobId)). State is persisted in DO storage.
|
|
9
|
+
*
|
|
10
|
+
* Dispatch model:
|
|
11
|
+
* POST /start → create job, set alarm
|
|
12
|
+
* alarm → dispatch one wave (concurrency tasks), await results, set next alarm
|
|
13
|
+
* GET /status → return job state
|
|
14
|
+
* POST /cancel → cancel job, delete alarm
|
|
15
|
+
*
|
|
16
|
+
* Usage in a CF Worker Durable Object:
|
|
17
|
+
* ```typescript
|
|
18
|
+
* export class JobCoordinatorDO implements DurableObject {
|
|
19
|
+
* constructor(private state: DurableObjectState, private env: Env) {}
|
|
20
|
+
* async fetch(request: Request) {
|
|
21
|
+
* return handleJobCoordinatorRequest(request, this.state, this.env);
|
|
22
|
+
* }
|
|
23
|
+
* async alarm() {
|
|
24
|
+
* return handleJobCoordinatorAlarm(this.state, this.env);
|
|
25
|
+
* }
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
const STORAGE_KEY = "job";
|
|
30
|
+
async function handleJobCoordinatorRequest(request, state, env) {
|
|
31
|
+
const url = new URL(request.url);
|
|
32
|
+
if (request.method === "POST" && url.pathname === "/start") return handleStart(request, state, env);
|
|
33
|
+
if (request.method === "POST" && url.pathname === "/cancel") return handleCancel(state);
|
|
34
|
+
if (request.method === "GET" && url.pathname === "/status") return handleStatus(state);
|
|
35
|
+
return new Response("Not found", { status: 404 });
|
|
36
|
+
}
|
|
37
|
+
async function handleJobCoordinatorAlarm(state, env) {
|
|
38
|
+
const job = await state.storage.get(STORAGE_KEY);
|
|
39
|
+
if (!job) return;
|
|
40
|
+
if (job.status === "cancelled" || job.status === "done" || job.status === "failed") return;
|
|
41
|
+
if (job.status === "reducing") {
|
|
42
|
+
await executeReduceWave(job, state, env);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
await executeMapWave(job, state, env);
|
|
46
|
+
}
|
|
47
|
+
async function handleStart(request, state, _env) {
|
|
48
|
+
let body;
|
|
49
|
+
try {
|
|
50
|
+
body = await request.json();
|
|
51
|
+
} catch {
|
|
52
|
+
return Response.json({
|
|
53
|
+
success: false,
|
|
54
|
+
error: "Invalid JSON"
|
|
55
|
+
}, { status: 400 });
|
|
56
|
+
}
|
|
57
|
+
if (!body.mapTask || !body.totalItems || body.totalItems <= 0) return Response.json({
|
|
58
|
+
success: false,
|
|
59
|
+
error: "Missing required fields: mapTask, totalItems"
|
|
60
|
+
}, { status: 400 });
|
|
61
|
+
const batchSize = body.batchSize ?? 100;
|
|
62
|
+
const concurrency = body.concurrency ?? 10;
|
|
63
|
+
const totalBatches = Math.ceil(body.totalItems / batchSize);
|
|
64
|
+
const job = {
|
|
65
|
+
id: body.jobId,
|
|
66
|
+
name: body.name,
|
|
67
|
+
status: "mapping",
|
|
68
|
+
mapTask: body.mapTask,
|
|
69
|
+
reduceTask: body.reduceTask,
|
|
70
|
+
args: body.args,
|
|
71
|
+
totalItems: body.totalItems,
|
|
72
|
+
batchSize,
|
|
73
|
+
concurrency,
|
|
74
|
+
timeout: body.timeout,
|
|
75
|
+
totalBatches,
|
|
76
|
+
progress: {
|
|
77
|
+
pending: totalBatches,
|
|
78
|
+
running: 0,
|
|
79
|
+
done: 0,
|
|
80
|
+
failed: 0
|
|
81
|
+
},
|
|
82
|
+
mapTaskIds: new Array(totalBatches).fill(""),
|
|
83
|
+
mapResults: new Array(totalBatches).fill(null),
|
|
84
|
+
created: Date.now(),
|
|
85
|
+
started: Date.now()
|
|
86
|
+
};
|
|
87
|
+
await state.storage.put(STORAGE_KEY, job);
|
|
88
|
+
await state.storage.setAlarm(Date.now());
|
|
89
|
+
return Response.json({
|
|
90
|
+
success: true,
|
|
91
|
+
jobId: job.id
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
async function handleCancel(state) {
|
|
95
|
+
const job = await state.storage.get(STORAGE_KEY);
|
|
96
|
+
if (!job) return Response.json({
|
|
97
|
+
success: false,
|
|
98
|
+
error: "No job found"
|
|
99
|
+
}, { status: 404 });
|
|
100
|
+
job.status = "cancelled";
|
|
101
|
+
job.ended = Date.now();
|
|
102
|
+
await state.storage.put(STORAGE_KEY, job);
|
|
103
|
+
await state.storage.deleteAlarm();
|
|
104
|
+
return Response.json({ success: true });
|
|
105
|
+
}
|
|
106
|
+
async function handleStatus(state) {
|
|
107
|
+
const job = await state.storage.get(STORAGE_KEY);
|
|
108
|
+
if (!job) return Response.json({ error: "No job found" }, { status: 404 });
|
|
109
|
+
return Response.json(job);
|
|
110
|
+
}
|
|
111
|
+
async function executeMapWave(job, state, env) {
|
|
112
|
+
const batchIndices = [];
|
|
113
|
+
for (let i = 0; i < job.totalBatches && batchIndices.length < job.concurrency; i++) if (job.mapTaskIds[i] === "") batchIndices.push(i);
|
|
114
|
+
if (batchIndices.length === 0) return;
|
|
115
|
+
const promises = batchIndices.map(async (batchIndex) => {
|
|
116
|
+
const taskId = (0, _aigne_uuid.v7)();
|
|
117
|
+
const batchStart = batchIndex * job.batchSize;
|
|
118
|
+
const batchEnd = Math.min(batchStart + job.batchSize, job.totalItems);
|
|
119
|
+
const taskArgs = {
|
|
120
|
+
...job.args ?? {},
|
|
121
|
+
jobId: job.id,
|
|
122
|
+
batchIndex,
|
|
123
|
+
batchSize: job.batchSize,
|
|
124
|
+
batchStart,
|
|
125
|
+
batchEnd,
|
|
126
|
+
totalItems: job.totalItems
|
|
127
|
+
};
|
|
128
|
+
job.mapTaskIds[batchIndex] = taskId;
|
|
129
|
+
try {
|
|
130
|
+
const doId = env.TASK_DO.idFromName(taskId);
|
|
131
|
+
return {
|
|
132
|
+
batchIndex,
|
|
133
|
+
taskId,
|
|
134
|
+
result: await (await env.TASK_DO.get(doId).fetch(new Request("https://task/run", {
|
|
135
|
+
method: "POST",
|
|
136
|
+
headers: { "Content-Type": "application/json" },
|
|
137
|
+
body: JSON.stringify({
|
|
138
|
+
taskId,
|
|
139
|
+
taskPath: job.mapTask,
|
|
140
|
+
args: taskArgs
|
|
141
|
+
})
|
|
142
|
+
}))).json()
|
|
143
|
+
};
|
|
144
|
+
} catch (err) {
|
|
145
|
+
return {
|
|
146
|
+
batchIndex,
|
|
147
|
+
taskId,
|
|
148
|
+
result: {
|
|
149
|
+
success: false,
|
|
150
|
+
error: String(err)
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
const results = await Promise.all(promises);
|
|
156
|
+
for (const { batchIndex, result } of results) {
|
|
157
|
+
job.progress.pending = Math.max(0, job.progress.pending - 1);
|
|
158
|
+
if (result.success) {
|
|
159
|
+
job.mapResults[batchIndex] = result.data;
|
|
160
|
+
job.progress.done++;
|
|
161
|
+
} else {
|
|
162
|
+
job.mapResults[batchIndex] = null;
|
|
163
|
+
job.progress.failed++;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (job.progress.done + job.progress.failed === job.totalBatches) if (job.reduceTask) {
|
|
167
|
+
job.status = "reducing";
|
|
168
|
+
await state.storage.setAlarm(Date.now());
|
|
169
|
+
} else {
|
|
170
|
+
job.status = "done";
|
|
171
|
+
job.result = job.mapResults;
|
|
172
|
+
job.ended = Date.now();
|
|
173
|
+
await state.storage.deleteAlarm();
|
|
174
|
+
}
|
|
175
|
+
else await state.storage.setAlarm(Date.now());
|
|
176
|
+
await state.storage.put(STORAGE_KEY, job);
|
|
177
|
+
}
|
|
178
|
+
async function executeReduceWave(job, state, env) {
|
|
179
|
+
const reduceTaskId = (0, _aigne_uuid.v7)();
|
|
180
|
+
job.reduceTaskId = reduceTaskId;
|
|
181
|
+
const reduceArgs = {
|
|
182
|
+
...job.args ?? {},
|
|
183
|
+
jobId: job.id,
|
|
184
|
+
mapResults: job.mapResults,
|
|
185
|
+
totalBatches: job.totalBatches
|
|
186
|
+
};
|
|
187
|
+
try {
|
|
188
|
+
const doId = env.TASK_DO.idFromName(reduceTaskId);
|
|
189
|
+
const result = await (await env.TASK_DO.get(doId).fetch(new Request("https://task/run", {
|
|
190
|
+
method: "POST",
|
|
191
|
+
headers: { "Content-Type": "application/json" },
|
|
192
|
+
body: JSON.stringify({
|
|
193
|
+
taskId: reduceTaskId,
|
|
194
|
+
taskPath: job.reduceTask,
|
|
195
|
+
args: reduceArgs
|
|
196
|
+
})
|
|
197
|
+
}))).json();
|
|
198
|
+
if (result.success) {
|
|
199
|
+
job.status = "done";
|
|
200
|
+
job.result = result.data;
|
|
201
|
+
} else {
|
|
202
|
+
job.status = "failed";
|
|
203
|
+
job.error = result.error ?? "Reduce task failed";
|
|
204
|
+
}
|
|
205
|
+
} catch (err) {
|
|
206
|
+
job.status = "failed";
|
|
207
|
+
job.error = `Reduce task error: ${String(err)}`;
|
|
208
|
+
}
|
|
209
|
+
job.ended = Date.now();
|
|
210
|
+
await state.storage.deleteAlarm();
|
|
211
|
+
await state.storage.put(STORAGE_KEY, job);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
//#endregion
|
|
215
|
+
exports.handleJobCoordinatorAlarm = handleJobCoordinatorAlarm;
|
|
216
|
+
exports.handleJobCoordinatorRequest = handleJobCoordinatorRequest;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { DOState, JobCoordinatorEnv } from "./do-types.cjs";
|
|
2
|
+
|
|
3
|
+
//#region src/job-coordinator.d.ts
|
|
4
|
+
declare function handleJobCoordinatorRequest(request: Request, state: DOState, env: JobCoordinatorEnv): Promise<Response>;
|
|
5
|
+
declare function handleJobCoordinatorAlarm(state: DOState, env: JobCoordinatorEnv): Promise<void>;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { handleJobCoordinatorAlarm, handleJobCoordinatorRequest };
|
|
8
|
+
//# sourceMappingURL=job-coordinator.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job-coordinator.d.cts","names":[],"sources":["../src/job-coordinator.ts"],"mappings":";;;iBAiCsB,2BAAA,CACpB,OAAA,EAAS,OAAA,EACT,KAAA,EAAO,OAAA,EACP,GAAA,EAAK,iBAAA,GACJ,OAAA,CAAQ,QAAA;AAAA,iBAoBW,yBAAA,CACpB,KAAA,EAAO,OAAA,EACP,GAAA,EAAK,iBAAA,GACJ,OAAA"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { DOState, JobCoordinatorEnv } from "./do-types.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/job-coordinator.d.ts
|
|
4
|
+
declare function handleJobCoordinatorRequest(request: Request, state: DOState, env: JobCoordinatorEnv): Promise<Response>;
|
|
5
|
+
declare function handleJobCoordinatorAlarm(state: DOState, env: JobCoordinatorEnv): Promise<void>;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { handleJobCoordinatorAlarm, handleJobCoordinatorRequest };
|
|
8
|
+
//# sourceMappingURL=job-coordinator.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job-coordinator.d.mts","names":[],"sources":["../src/job-coordinator.ts"],"mappings":";;;iBAiCsB,2BAAA,CACpB,OAAA,EAAS,OAAA,EACT,KAAA,EAAO,OAAA,EACP,GAAA,EAAK,iBAAA,GACJ,OAAA,CAAQ,QAAA;AAAA,iBAoBW,yBAAA,CACpB,KAAA,EAAO,OAAA,EACP,GAAA,EAAK,iBAAA,GACJ,OAAA"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { v7 } from "@aigne/uuid";
|
|
2
|
+
|
|
3
|
+
//#region src/job-coordinator.ts
|
|
4
|
+
/**
|
|
5
|
+
* Job Coordinator DO — wave-based map-reduce orchestration.
|
|
6
|
+
*
|
|
7
|
+
* Runs inside a Durable Object. Each job gets its own Coordinator DO
|
|
8
|
+
* (idFromName(jobId)). State is persisted in DO storage.
|
|
9
|
+
*
|
|
10
|
+
* Dispatch model:
|
|
11
|
+
* POST /start → create job, set alarm
|
|
12
|
+
* alarm → dispatch one wave (concurrency tasks), await results, set next alarm
|
|
13
|
+
* GET /status → return job state
|
|
14
|
+
* POST /cancel → cancel job, delete alarm
|
|
15
|
+
*
|
|
16
|
+
* Usage in a CF Worker Durable Object:
|
|
17
|
+
* ```typescript
|
|
18
|
+
* export class JobCoordinatorDO implements DurableObject {
|
|
19
|
+
* constructor(private state: DurableObjectState, private env: Env) {}
|
|
20
|
+
* async fetch(request: Request) {
|
|
21
|
+
* return handleJobCoordinatorRequest(request, this.state, this.env);
|
|
22
|
+
* }
|
|
23
|
+
* async alarm() {
|
|
24
|
+
* return handleJobCoordinatorAlarm(this.state, this.env);
|
|
25
|
+
* }
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
const STORAGE_KEY = "job";
|
|
30
|
+
async function handleJobCoordinatorRequest(request, state, env) {
|
|
31
|
+
const url = new URL(request.url);
|
|
32
|
+
if (request.method === "POST" && url.pathname === "/start") return handleStart(request, state, env);
|
|
33
|
+
if (request.method === "POST" && url.pathname === "/cancel") return handleCancel(state);
|
|
34
|
+
if (request.method === "GET" && url.pathname === "/status") return handleStatus(state);
|
|
35
|
+
return new Response("Not found", { status: 404 });
|
|
36
|
+
}
|
|
37
|
+
async function handleJobCoordinatorAlarm(state, env) {
|
|
38
|
+
const job = await state.storage.get(STORAGE_KEY);
|
|
39
|
+
if (!job) return;
|
|
40
|
+
if (job.status === "cancelled" || job.status === "done" || job.status === "failed") return;
|
|
41
|
+
if (job.status === "reducing") {
|
|
42
|
+
await executeReduceWave(job, state, env);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
await executeMapWave(job, state, env);
|
|
46
|
+
}
|
|
47
|
+
async function handleStart(request, state, _env) {
|
|
48
|
+
let body;
|
|
49
|
+
try {
|
|
50
|
+
body = await request.json();
|
|
51
|
+
} catch {
|
|
52
|
+
return Response.json({
|
|
53
|
+
success: false,
|
|
54
|
+
error: "Invalid JSON"
|
|
55
|
+
}, { status: 400 });
|
|
56
|
+
}
|
|
57
|
+
if (!body.mapTask || !body.totalItems || body.totalItems <= 0) return Response.json({
|
|
58
|
+
success: false,
|
|
59
|
+
error: "Missing required fields: mapTask, totalItems"
|
|
60
|
+
}, { status: 400 });
|
|
61
|
+
const batchSize = body.batchSize ?? 100;
|
|
62
|
+
const concurrency = body.concurrency ?? 10;
|
|
63
|
+
const totalBatches = Math.ceil(body.totalItems / batchSize);
|
|
64
|
+
const job = {
|
|
65
|
+
id: body.jobId,
|
|
66
|
+
name: body.name,
|
|
67
|
+
status: "mapping",
|
|
68
|
+
mapTask: body.mapTask,
|
|
69
|
+
reduceTask: body.reduceTask,
|
|
70
|
+
args: body.args,
|
|
71
|
+
totalItems: body.totalItems,
|
|
72
|
+
batchSize,
|
|
73
|
+
concurrency,
|
|
74
|
+
timeout: body.timeout,
|
|
75
|
+
totalBatches,
|
|
76
|
+
progress: {
|
|
77
|
+
pending: totalBatches,
|
|
78
|
+
running: 0,
|
|
79
|
+
done: 0,
|
|
80
|
+
failed: 0
|
|
81
|
+
},
|
|
82
|
+
mapTaskIds: new Array(totalBatches).fill(""),
|
|
83
|
+
mapResults: new Array(totalBatches).fill(null),
|
|
84
|
+
created: Date.now(),
|
|
85
|
+
started: Date.now()
|
|
86
|
+
};
|
|
87
|
+
await state.storage.put(STORAGE_KEY, job);
|
|
88
|
+
await state.storage.setAlarm(Date.now());
|
|
89
|
+
return Response.json({
|
|
90
|
+
success: true,
|
|
91
|
+
jobId: job.id
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
async function handleCancel(state) {
|
|
95
|
+
const job = await state.storage.get(STORAGE_KEY);
|
|
96
|
+
if (!job) return Response.json({
|
|
97
|
+
success: false,
|
|
98
|
+
error: "No job found"
|
|
99
|
+
}, { status: 404 });
|
|
100
|
+
job.status = "cancelled";
|
|
101
|
+
job.ended = Date.now();
|
|
102
|
+
await state.storage.put(STORAGE_KEY, job);
|
|
103
|
+
await state.storage.deleteAlarm();
|
|
104
|
+
return Response.json({ success: true });
|
|
105
|
+
}
|
|
106
|
+
async function handleStatus(state) {
|
|
107
|
+
const job = await state.storage.get(STORAGE_KEY);
|
|
108
|
+
if (!job) return Response.json({ error: "No job found" }, { status: 404 });
|
|
109
|
+
return Response.json(job);
|
|
110
|
+
}
|
|
111
|
+
async function executeMapWave(job, state, env) {
|
|
112
|
+
const batchIndices = [];
|
|
113
|
+
for (let i = 0; i < job.totalBatches && batchIndices.length < job.concurrency; i++) if (job.mapTaskIds[i] === "") batchIndices.push(i);
|
|
114
|
+
if (batchIndices.length === 0) return;
|
|
115
|
+
const promises = batchIndices.map(async (batchIndex) => {
|
|
116
|
+
const taskId = v7();
|
|
117
|
+
const batchStart = batchIndex * job.batchSize;
|
|
118
|
+
const batchEnd = Math.min(batchStart + job.batchSize, job.totalItems);
|
|
119
|
+
const taskArgs = {
|
|
120
|
+
...job.args ?? {},
|
|
121
|
+
jobId: job.id,
|
|
122
|
+
batchIndex,
|
|
123
|
+
batchSize: job.batchSize,
|
|
124
|
+
batchStart,
|
|
125
|
+
batchEnd,
|
|
126
|
+
totalItems: job.totalItems
|
|
127
|
+
};
|
|
128
|
+
job.mapTaskIds[batchIndex] = taskId;
|
|
129
|
+
try {
|
|
130
|
+
const doId = env.TASK_DO.idFromName(taskId);
|
|
131
|
+
return {
|
|
132
|
+
batchIndex,
|
|
133
|
+
taskId,
|
|
134
|
+
result: await (await env.TASK_DO.get(doId).fetch(new Request("https://task/run", {
|
|
135
|
+
method: "POST",
|
|
136
|
+
headers: { "Content-Type": "application/json" },
|
|
137
|
+
body: JSON.stringify({
|
|
138
|
+
taskId,
|
|
139
|
+
taskPath: job.mapTask,
|
|
140
|
+
args: taskArgs
|
|
141
|
+
})
|
|
142
|
+
}))).json()
|
|
143
|
+
};
|
|
144
|
+
} catch (err) {
|
|
145
|
+
return {
|
|
146
|
+
batchIndex,
|
|
147
|
+
taskId,
|
|
148
|
+
result: {
|
|
149
|
+
success: false,
|
|
150
|
+
error: String(err)
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
const results = await Promise.all(promises);
|
|
156
|
+
for (const { batchIndex, result } of results) {
|
|
157
|
+
job.progress.pending = Math.max(0, job.progress.pending - 1);
|
|
158
|
+
if (result.success) {
|
|
159
|
+
job.mapResults[batchIndex] = result.data;
|
|
160
|
+
job.progress.done++;
|
|
161
|
+
} else {
|
|
162
|
+
job.mapResults[batchIndex] = null;
|
|
163
|
+
job.progress.failed++;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (job.progress.done + job.progress.failed === job.totalBatches) if (job.reduceTask) {
|
|
167
|
+
job.status = "reducing";
|
|
168
|
+
await state.storage.setAlarm(Date.now());
|
|
169
|
+
} else {
|
|
170
|
+
job.status = "done";
|
|
171
|
+
job.result = job.mapResults;
|
|
172
|
+
job.ended = Date.now();
|
|
173
|
+
await state.storage.deleteAlarm();
|
|
174
|
+
}
|
|
175
|
+
else await state.storage.setAlarm(Date.now());
|
|
176
|
+
await state.storage.put(STORAGE_KEY, job);
|
|
177
|
+
}
|
|
178
|
+
async function executeReduceWave(job, state, env) {
|
|
179
|
+
const reduceTaskId = v7();
|
|
180
|
+
job.reduceTaskId = reduceTaskId;
|
|
181
|
+
const reduceArgs = {
|
|
182
|
+
...job.args ?? {},
|
|
183
|
+
jobId: job.id,
|
|
184
|
+
mapResults: job.mapResults,
|
|
185
|
+
totalBatches: job.totalBatches
|
|
186
|
+
};
|
|
187
|
+
try {
|
|
188
|
+
const doId = env.TASK_DO.idFromName(reduceTaskId);
|
|
189
|
+
const result = await (await env.TASK_DO.get(doId).fetch(new Request("https://task/run", {
|
|
190
|
+
method: "POST",
|
|
191
|
+
headers: { "Content-Type": "application/json" },
|
|
192
|
+
body: JSON.stringify({
|
|
193
|
+
taskId: reduceTaskId,
|
|
194
|
+
taskPath: job.reduceTask,
|
|
195
|
+
args: reduceArgs
|
|
196
|
+
})
|
|
197
|
+
}))).json();
|
|
198
|
+
if (result.success) {
|
|
199
|
+
job.status = "done";
|
|
200
|
+
job.result = result.data;
|
|
201
|
+
} else {
|
|
202
|
+
job.status = "failed";
|
|
203
|
+
job.error = result.error ?? "Reduce task failed";
|
|
204
|
+
}
|
|
205
|
+
} catch (err) {
|
|
206
|
+
job.status = "failed";
|
|
207
|
+
job.error = `Reduce task error: ${String(err)}`;
|
|
208
|
+
}
|
|
209
|
+
job.ended = Date.now();
|
|
210
|
+
await state.storage.deleteAlarm();
|
|
211
|
+
await state.storage.put(STORAGE_KEY, job);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
//#endregion
|
|
215
|
+
export { handleJobCoordinatorAlarm, handleJobCoordinatorRequest };
|
|
216
|
+
//# sourceMappingURL=job-coordinator.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job-coordinator.mjs","names":[],"sources":["../src/job-coordinator.ts"],"sourcesContent":["/**\n * Job Coordinator DO — wave-based map-reduce orchestration.\n *\n * Runs inside a Durable Object. Each job gets its own Coordinator DO\n * (idFromName(jobId)). State is persisted in DO storage.\n *\n * Dispatch model:\n * POST /start → create job, set alarm\n * alarm → dispatch one wave (concurrency tasks), await results, set next alarm\n * GET /status → return job state\n * POST /cancel → cancel job, delete alarm\n *\n * Usage in a CF Worker Durable Object:\n * ```typescript\n * export class JobCoordinatorDO implements DurableObject {\n * constructor(private state: DurableObjectState, private env: Env) {}\n * async fetch(request: Request) {\n * return handleJobCoordinatorRequest(request, this.state, this.env);\n * }\n * async alarm() {\n * return handleJobCoordinatorAlarm(this.state, this.env);\n * }\n * }\n * ```\n */\nimport { v7 } from \"@aigne/uuid\";\nimport type { DOState, JobCoordinatorEnv, JobStartRequest, TaskRunResponse } from \"./do-types.js\";\nimport type { JobEntry } from \"./types.js\";\n\nconst STORAGE_KEY = \"job\";\n\n// ─── HTTP request handler ───\n\nexport async function handleJobCoordinatorRequest(\n request: Request,\n state: DOState,\n env: JobCoordinatorEnv,\n): Promise<Response> {\n const url = new URL(request.url);\n\n if (request.method === \"POST\" && url.pathname === \"/start\") {\n return handleStart(request, state, env);\n }\n\n if (request.method === \"POST\" && url.pathname === \"/cancel\") {\n return handleCancel(state);\n }\n\n if (request.method === \"GET\" && url.pathname === \"/status\") {\n return handleStatus(state);\n }\n\n return new Response(\"Not found\", { status: 404 });\n}\n\n// ─── Alarm handler (wave dispatch) ───\n\nexport async function handleJobCoordinatorAlarm(\n state: DOState,\n env: JobCoordinatorEnv,\n): Promise<void> {\n const job = await state.storage.get<JobEntry>(STORAGE_KEY);\n if (!job) return;\n\n if (job.status === \"cancelled\" || job.status === \"done\" || job.status === \"failed\") {\n return;\n }\n\n if (job.status === \"reducing\") {\n await executeReduceWave(job, state, env);\n return;\n }\n\n // status === \"mapping\" — dispatch next wave of map tasks\n await executeMapWave(job, state, env);\n}\n\n// ─── Internal handlers ───\n\nasync function handleStart(\n request: Request,\n state: DOState,\n _env: JobCoordinatorEnv,\n): Promise<Response> {\n let body: JobStartRequest;\n try {\n body = await request.json();\n } catch {\n return Response.json({ success: false, error: \"Invalid JSON\" }, { status: 400 });\n }\n\n if (!body.mapTask || !body.totalItems || body.totalItems <= 0) {\n return Response.json(\n { success: false, error: \"Missing required fields: mapTask, totalItems\" },\n { status: 400 },\n );\n }\n\n const batchSize = body.batchSize ?? 100;\n const concurrency = body.concurrency ?? 10;\n const totalBatches = Math.ceil(body.totalItems / batchSize);\n\n const job: JobEntry = {\n id: body.jobId,\n name: body.name,\n status: \"mapping\",\n mapTask: body.mapTask,\n reduceTask: body.reduceTask,\n args: body.args,\n totalItems: body.totalItems,\n batchSize,\n concurrency,\n timeout: body.timeout,\n totalBatches,\n progress: { pending: totalBatches, running: 0, done: 0, failed: 0 },\n mapTaskIds: new Array<string>(totalBatches).fill(\"\"),\n mapResults: new Array<unknown>(totalBatches).fill(null),\n created: Date.now(),\n started: Date.now(),\n };\n\n await state.storage.put(STORAGE_KEY, job);\n\n // Set immediate alarm for first wave\n await state.storage.setAlarm(Date.now());\n\n return Response.json({ success: true, jobId: job.id });\n}\n\nasync function handleCancel(state: DOState): Promise<Response> {\n const job = await state.storage.get<JobEntry>(STORAGE_KEY);\n if (!job) {\n return Response.json({ success: false, error: \"No job found\" }, { status: 404 });\n }\n\n job.status = \"cancelled\";\n job.ended = Date.now();\n await state.storage.put(STORAGE_KEY, job);\n await state.storage.deleteAlarm();\n\n return Response.json({ success: true });\n}\n\nasync function handleStatus(state: DOState): Promise<Response> {\n const job = await state.storage.get<JobEntry>(STORAGE_KEY);\n if (!job) {\n return Response.json({ error: \"No job found\" }, { status: 404 });\n }\n return Response.json(job);\n}\n\n// ─── Wave execution ───\n\nasync function executeMapWave(\n job: JobEntry,\n state: DOState,\n env: JobCoordinatorEnv,\n): Promise<void> {\n // Find the next batch of undispatched tasks\n const batchIndices: number[] = [];\n for (let i = 0; i < job.totalBatches && batchIndices.length < job.concurrency; i++) {\n if (job.mapTaskIds[i] === \"\") {\n batchIndices.push(i);\n }\n }\n\n if (batchIndices.length === 0) {\n // All batches dispatched but not all completed? Shouldn't happen in wave model.\n return;\n }\n\n // Dispatch wave concurrently\n const promises = batchIndices.map(async (batchIndex) => {\n const taskId = v7();\n const batchStart = batchIndex * job.batchSize;\n const batchEnd = Math.min(batchStart + job.batchSize, job.totalItems);\n\n const taskArgs: Record<string, unknown> = {\n ...((job.args as Record<string, unknown>) ?? {}),\n jobId: job.id,\n batchIndex,\n batchSize: job.batchSize,\n batchStart,\n batchEnd,\n totalItems: job.totalItems,\n };\n\n job.mapTaskIds[batchIndex] = taskId;\n\n try {\n const doId = env.TASK_DO.idFromName(taskId);\n const stub = env.TASK_DO.get(doId);\n const response = await stub.fetch(\n new Request(\"https://task/run\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ taskId, taskPath: job.mapTask, args: taskArgs }),\n }),\n );\n\n const result: TaskRunResponse = await response.json();\n return { batchIndex, taskId, result };\n } catch (err) {\n return {\n batchIndex,\n taskId,\n result: { success: false, error: String(err) } as TaskRunResponse,\n };\n }\n });\n\n const results = await Promise.all(promises);\n\n // Record results\n for (const { batchIndex, result } of results) {\n job.progress.pending = Math.max(0, job.progress.pending - 1);\n if (result.success) {\n job.mapResults[batchIndex] = result.data;\n job.progress.done++;\n } else {\n job.mapResults[batchIndex] = null;\n job.progress.failed++;\n }\n }\n\n // Check if all map tasks complete\n const allDone = job.progress.done + job.progress.failed === job.totalBatches;\n\n if (allDone) {\n if (job.reduceTask) {\n job.status = \"reducing\";\n // Set alarm for reduce wave\n await state.storage.setAlarm(Date.now());\n } else {\n job.status = \"done\";\n job.result = job.mapResults;\n job.ended = Date.now();\n await state.storage.deleteAlarm();\n }\n } else {\n // More batches to dispatch — set next alarm\n await state.storage.setAlarm(Date.now());\n }\n\n await state.storage.put(STORAGE_KEY, job);\n}\n\nasync function executeReduceWave(\n job: JobEntry,\n state: DOState,\n env: JobCoordinatorEnv,\n): Promise<void> {\n const reduceTaskId = v7();\n job.reduceTaskId = reduceTaskId;\n\n const reduceArgs: Record<string, unknown> = {\n ...((job.args as Record<string, unknown>) ?? {}),\n jobId: job.id,\n mapResults: job.mapResults,\n totalBatches: job.totalBatches,\n };\n\n try {\n const doId = env.TASK_DO.idFromName(reduceTaskId);\n const stub = env.TASK_DO.get(doId);\n const response = await stub.fetch(\n new Request(\"https://task/run\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ taskId: reduceTaskId, taskPath: job.reduceTask, args: reduceArgs }),\n }),\n );\n\n const result: TaskRunResponse = await response.json();\n if (result.success) {\n job.status = \"done\";\n job.result = result.data;\n } else {\n job.status = \"failed\";\n job.error = result.error ?? \"Reduce task failed\";\n }\n } catch (err) {\n job.status = \"failed\";\n job.error = `Reduce task error: ${String(err)}`;\n }\n\n job.ended = Date.now();\n await state.storage.deleteAlarm();\n await state.storage.put(STORAGE_KEY, job);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/local-executor.ts
|
|
3
|
+
var LocalExecutor = class {
|
|
4
|
+
constructor(getAFS) {
|
|
5
|
+
this.getAFS = getAFS;
|
|
6
|
+
}
|
|
7
|
+
async run(_taskId, taskPath, args, context) {
|
|
8
|
+
const root = this.getAFS();
|
|
9
|
+
if (!root.exec) return {
|
|
10
|
+
success: false,
|
|
11
|
+
error: "AFS root does not support exec"
|
|
12
|
+
};
|
|
13
|
+
const execOptions = {};
|
|
14
|
+
if (context) execOptions.context = context;
|
|
15
|
+
const result = await root.exec(taskPath, args ?? {}, execOptions);
|
|
16
|
+
return {
|
|
17
|
+
success: result.success,
|
|
18
|
+
data: result.data,
|
|
19
|
+
error: result.error ? result.error.message : void 0
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
//#endregion
|
|
25
|
+
exports.LocalExecutor = LocalExecutor;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { TaskContext, TaskExecutor, TaskResult } from "./types.cjs";
|
|
2
|
+
import { AFSRoot } from "@aigne/afs";
|
|
3
|
+
|
|
4
|
+
//#region src/local-executor.d.ts
|
|
5
|
+
declare class LocalExecutor implements TaskExecutor {
|
|
6
|
+
private getAFS;
|
|
7
|
+
constructor(getAFS: () => AFSRoot);
|
|
8
|
+
run(_taskId: string, taskPath: string, args: unknown, context?: TaskContext): Promise<TaskResult>;
|
|
9
|
+
}
|
|
10
|
+
//#endregion
|
|
11
|
+
export { LocalExecutor };
|
|
12
|
+
//# sourceMappingURL=local-executor.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-executor.d.cts","names":[],"sources":["../src/local-executor.ts"],"mappings":";;;;cAGa,aAAA,YAAyB,YAAA;EAAA,QAChB,MAAA;cAAA,MAAA,QAAc,OAAA;EAE5B,GAAA,CACJ,OAAA,UACA,QAAA,UACA,IAAA,WACA,OAAA,GAAU,WAAA,GACT,OAAA,CAAQ,UAAA;AAAA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { TaskContext, TaskExecutor, TaskResult } from "./types.mjs";
|
|
2
|
+
import { AFSRoot } from "@aigne/afs";
|
|
3
|
+
|
|
4
|
+
//#region src/local-executor.d.ts
|
|
5
|
+
declare class LocalExecutor implements TaskExecutor {
|
|
6
|
+
private getAFS;
|
|
7
|
+
constructor(getAFS: () => AFSRoot);
|
|
8
|
+
run(_taskId: string, taskPath: string, args: unknown, context?: TaskContext): Promise<TaskResult>;
|
|
9
|
+
}
|
|
10
|
+
//#endregion
|
|
11
|
+
export { LocalExecutor };
|
|
12
|
+
//# sourceMappingURL=local-executor.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-executor.d.mts","names":[],"sources":["../src/local-executor.ts"],"mappings":";;;;cAGa,aAAA,YAAyB,YAAA;EAAA,QAChB,MAAA;cAAA,MAAA,QAAc,OAAA;EAE5B,GAAA,CACJ,OAAA,UACA,QAAA,UACA,IAAA,WACA,OAAA,GAAU,WAAA,GACT,OAAA,CAAQ,UAAA;AAAA"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/local-executor.ts
|
|
2
|
+
var LocalExecutor = class {
|
|
3
|
+
constructor(getAFS) {
|
|
4
|
+
this.getAFS = getAFS;
|
|
5
|
+
}
|
|
6
|
+
async run(_taskId, taskPath, args, context) {
|
|
7
|
+
const root = this.getAFS();
|
|
8
|
+
if (!root.exec) return {
|
|
9
|
+
success: false,
|
|
10
|
+
error: "AFS root does not support exec"
|
|
11
|
+
};
|
|
12
|
+
const execOptions = {};
|
|
13
|
+
if (context) execOptions.context = context;
|
|
14
|
+
const result = await root.exec(taskPath, args ?? {}, execOptions);
|
|
15
|
+
return {
|
|
16
|
+
success: result.success,
|
|
17
|
+
data: result.data,
|
|
18
|
+
error: result.error ? result.error.message : void 0
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
//#endregion
|
|
24
|
+
export { LocalExecutor };
|
|
25
|
+
//# sourceMappingURL=local-executor.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-executor.mjs","names":[],"sources":["../src/local-executor.ts"],"sourcesContent":["import type { AFSRoot } from \"@aigne/afs\";\nimport type { TaskContext, TaskExecutor, TaskResult } from \"./types.js\";\n\nexport class LocalExecutor implements TaskExecutor {\n constructor(private getAFS: () => AFSRoot) {}\n\n async run(\n _taskId: string,\n taskPath: string,\n args: unknown,\n context?: TaskContext,\n ): Promise<TaskResult> {\n const root = this.getAFS();\n if (!root.exec) {\n return { success: false, error: \"AFS root does not support exec\" };\n }\n const execOptions: Record<string, unknown> = {};\n if (context) {\n execOptions.context = context;\n }\n const result = await root.exec(taskPath, (args ?? {}) as Record<string, unknown>, execOptions);\n return {\n success: result.success,\n data: result.data,\n error: result.error ? result.error.message : undefined,\n };\n }\n}\n"],"mappings":";AAGA,IAAa,gBAAb,MAAmD;CACjD,YAAY,AAAQ,QAAuB;EAAvB;;CAEpB,MAAM,IACJ,SACA,UACA,MACA,SACqB;EACrB,MAAM,OAAO,KAAK,QAAQ;AAC1B,MAAI,CAAC,KAAK,KACR,QAAO;GAAE,SAAS;GAAO,OAAO;GAAkC;EAEpE,MAAM,cAAuC,EAAE;AAC/C,MAAI,QACF,aAAY,UAAU;EAExB,MAAM,SAAS,MAAM,KAAK,KAAK,UAAW,QAAQ,EAAE,EAA8B,YAAY;AAC9F,SAAO;GACL,SAAS,OAAO;GAChB,MAAM,OAAO;GACb,OAAO,OAAO,QAAQ,OAAO,MAAM,UAAU;GAC9C"}
|