@openfn/ws-worker 0.1.8 → 0.2.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/CHANGELOG.md +22 -0
- package/dist/index.d.ts +17 -2
- package/dist/index.js +94 -34
- package/dist/start.js +94 -34
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# ws-worker
|
|
2
2
|
|
|
3
|
+
## 0.2.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- ad8f6e9: Improve robustness of server connectivity
|
|
8
|
+
- Updated dependencies [704e7b6]
|
|
9
|
+
- @openfn/engine-multi@0.1.7
|
|
10
|
+
|
|
11
|
+
## 0.2.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- 0e8e20c: BREAKING: Updated exit reasons to `{ reason: "success", error_type, error_message }`
|
|
16
|
+
Add exit reasons to job and attempt complete
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- Updated dependencies [a540888]
|
|
21
|
+
- Updated dependencies [0e8e20c]
|
|
22
|
+
- @openfn/runtime@0.1.0
|
|
23
|
+
- @openfn/engine-multi@0.1.6
|
|
24
|
+
|
|
3
25
|
## 0.1.8
|
|
4
26
|
|
|
5
27
|
### Patch Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,22 @@
|
|
|
1
1
|
import Koa from 'koa';
|
|
2
2
|
import { SanitizePolicies, Logger } from '@openfn/logger';
|
|
3
|
-
import { RuntimeEngine } from '@openfn/engine-multi';
|
|
4
3
|
import { Channel as Channel$1 } from 'phoenix';
|
|
5
4
|
import { ExecutionPlan } from '@openfn/runtime';
|
|
5
|
+
import { RuntimeEngine } from '@openfn/engine-multi';
|
|
6
|
+
|
|
7
|
+
type ExitReasonStrings =
|
|
8
|
+
| 'success'
|
|
9
|
+
| 'fail'
|
|
10
|
+
| 'crash'
|
|
11
|
+
| 'kill'
|
|
12
|
+
| 'cancel'
|
|
13
|
+
| 'exception';
|
|
14
|
+
|
|
15
|
+
type ExitReason = {
|
|
16
|
+
reason: ExitReasonStrings;
|
|
17
|
+
error_message: string | null;
|
|
18
|
+
error_type: string | null;
|
|
19
|
+
};
|
|
6
20
|
|
|
7
21
|
type AttemptOptions = {
|
|
8
22
|
timeout?: number;
|
|
@@ -37,6 +51,7 @@ declare type AttemptState = {
|
|
|
37
51
|
plan: ExecutionPlan;
|
|
38
52
|
options: AttemptOptions;
|
|
39
53
|
dataclips: Record<string, any>;
|
|
54
|
+
reasons: Record<string, ExitReason>;
|
|
40
55
|
lastDataclipId?: string;
|
|
41
56
|
};
|
|
42
57
|
declare type Context = {
|
|
@@ -70,7 +85,7 @@ interface ServerApp extends Koa {
|
|
|
70
85
|
workflows: Record<string, true | Context>;
|
|
71
86
|
execute: ({ id, token }: CLAIM_ATTEMPT) => Promise<void>;
|
|
72
87
|
destroy: () => void;
|
|
73
|
-
killWorkloop
|
|
88
|
+
killWorkloop?: () => void;
|
|
74
89
|
}
|
|
75
90
|
declare function createServer(engine: RuntimeEngine, options?: ServerOptions): ServerApp;
|
|
76
91
|
|
package/dist/index.js
CHANGED
|
@@ -80,6 +80,11 @@ var claim = (app, logger = mockLogger, maxWorkers = 5) => {
|
|
|
80
80
|
app.execute(attempt);
|
|
81
81
|
resolve();
|
|
82
82
|
});
|
|
83
|
+
}).receive("error", () => {
|
|
84
|
+
logger.debug("pull err");
|
|
85
|
+
}).receive("timeout", () => {
|
|
86
|
+
logger.debug("pull timeout");
|
|
87
|
+
reject(new Error("timeout"));
|
|
83
88
|
});
|
|
84
89
|
});
|
|
85
90
|
};
|
|
@@ -210,6 +215,30 @@ var stringify_default = (obj) => stringify(obj, (_key, value) => {
|
|
|
210
215
|
return value;
|
|
211
216
|
});
|
|
212
217
|
|
|
218
|
+
// src/api/reasons.ts
|
|
219
|
+
var calculateJobExitReason = (jobId, state, error) => {
|
|
220
|
+
let reason = "success";
|
|
221
|
+
let error_type = null;
|
|
222
|
+
let error_message = null;
|
|
223
|
+
if (error) {
|
|
224
|
+
reason = error.severity ?? "crash";
|
|
225
|
+
error_message = error.message;
|
|
226
|
+
error_type = error.subtype || error.type || error.name;
|
|
227
|
+
} else if (state.errors?.[jobId]) {
|
|
228
|
+
reason = "fail";
|
|
229
|
+
({ message: error_message, type: error_type } = state.errors[jobId]);
|
|
230
|
+
}
|
|
231
|
+
return { reason, error_type, error_message };
|
|
232
|
+
};
|
|
233
|
+
var calculateAttemptExitReason = (state) => {
|
|
234
|
+
if (state.reasons) {
|
|
235
|
+
const fail = Object.values(state.reasons).find(
|
|
236
|
+
({ reason }) => reason === "fail"
|
|
237
|
+
);
|
|
238
|
+
return fail || { reason: "success", error_type: null, error_message: null };
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
213
242
|
// src/api/execute.ts
|
|
214
243
|
var enc = new TextDecoder("utf-8");
|
|
215
244
|
var eventMap = {
|
|
@@ -219,15 +248,17 @@ var eventMap = {
|
|
|
219
248
|
"workflow-log": ATTEMPT_LOG,
|
|
220
249
|
"workflow-complete": ATTEMPT_COMPLETE
|
|
221
250
|
};
|
|
251
|
+
var createAttemptState = (plan, options = {}) => ({
|
|
252
|
+
plan,
|
|
253
|
+
lastDataclipId: plan.initialState,
|
|
254
|
+
dataclips: {},
|
|
255
|
+
reasons: {},
|
|
256
|
+
options
|
|
257
|
+
});
|
|
222
258
|
function execute(channel, engine, logger, plan, options = {}, onComplete = (_result) => {
|
|
223
259
|
}) {
|
|
224
260
|
logger.info("execute...");
|
|
225
|
-
const state =
|
|
226
|
-
plan,
|
|
227
|
-
lastDataclipId: plan.initialState,
|
|
228
|
-
dataclips: {},
|
|
229
|
-
options
|
|
230
|
-
};
|
|
261
|
+
const state = createAttemptState(plan, options);
|
|
231
262
|
const context = { channel, state, logger, onComplete };
|
|
232
263
|
const addEvent = (eventName, handler) => {
|
|
233
264
|
const wrappedFn = async (event) => {
|
|
@@ -251,6 +282,7 @@ function execute(channel, engine, logger, plan, options = {}, onComplete = (_res
|
|
|
251
282
|
addEvent("workflow-start", onWorkflowStart),
|
|
252
283
|
addEvent("job-start", onJobStart),
|
|
253
284
|
addEvent("job-complete", onJobComplete),
|
|
285
|
+
addEvent("job-error", onJobError),
|
|
254
286
|
addEvent("workflow-log", onJobLog),
|
|
255
287
|
addEvent("workflow-complete", onWorkflowComplete),
|
|
256
288
|
addEvent("workflow-error", onWorkflowError)
|
|
@@ -288,7 +320,15 @@ function onJobStart({ channel, state }, event) {
|
|
|
288
320
|
input_dataclip_id: state.lastDataclipId
|
|
289
321
|
});
|
|
290
322
|
}
|
|
291
|
-
function
|
|
323
|
+
function onJobError(context, event) {
|
|
324
|
+
const { state, error, jobId } = event;
|
|
325
|
+
if (state.errors?.[jobId]?.message === error.message) {
|
|
326
|
+
onJobComplete(context, event);
|
|
327
|
+
} else {
|
|
328
|
+
onJobComplete(context, event, event.error);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
function onJobComplete({ channel, state }, event, error) {
|
|
292
332
|
const dataclipId = crypto2.randomUUID();
|
|
293
333
|
const run_id = state.activeRun;
|
|
294
334
|
const job_id = state.activeJob;
|
|
@@ -299,12 +339,20 @@ function onJobComplete({ channel, state }, event) {
|
|
|
299
339
|
state.lastDataclipId = dataclipId;
|
|
300
340
|
delete state.activeRun;
|
|
301
341
|
delete state.activeJob;
|
|
342
|
+
const { reason, error_message, error_type } = calculateJobExitReason(
|
|
343
|
+
job_id,
|
|
344
|
+
event.state,
|
|
345
|
+
error
|
|
346
|
+
);
|
|
347
|
+
state.reasons[job_id] = { reason, error_message, error_type };
|
|
302
348
|
return sendEvent(channel, RUN_COMPLETE, {
|
|
303
349
|
run_id,
|
|
304
350
|
job_id,
|
|
305
351
|
output_dataclip_id: dataclipId,
|
|
306
352
|
output_dataclip: stringify_default(event.state),
|
|
307
|
-
reason
|
|
353
|
+
reason,
|
|
354
|
+
error_message,
|
|
355
|
+
error_type
|
|
308
356
|
});
|
|
309
357
|
}
|
|
310
358
|
function onWorkflowStart({ channel }, _event) {
|
|
@@ -312,20 +360,20 @@ function onWorkflowStart({ channel }, _event) {
|
|
|
312
360
|
}
|
|
313
361
|
async function onWorkflowComplete({ state, channel, onComplete }, _event) {
|
|
314
362
|
const result = state.dataclips[state.lastDataclipId];
|
|
363
|
+
const reason = calculateAttemptExitReason(state);
|
|
315
364
|
await sendEvent(channel, ATTEMPT_COMPLETE, {
|
|
316
365
|
final_dataclip_id: state.lastDataclipId,
|
|
317
|
-
|
|
318
|
-
reason: "ok"
|
|
366
|
+
...reason
|
|
319
367
|
});
|
|
320
|
-
onComplete(result);
|
|
368
|
+
onComplete({ reason, state: result });
|
|
321
369
|
}
|
|
322
370
|
async function onWorkflowError({ state, channel, onComplete }, event) {
|
|
371
|
+
const reason = calculateJobExitReason("", { data: {} }, event);
|
|
323
372
|
await sendEvent(channel, ATTEMPT_COMPLETE, {
|
|
324
|
-
reason: "fail",
|
|
325
373
|
final_dataclip_id: state.lastDataclipId,
|
|
326
|
-
|
|
374
|
+
...reason
|
|
327
375
|
});
|
|
328
|
-
onComplete({});
|
|
376
|
+
onComplete({ reason });
|
|
329
377
|
}
|
|
330
378
|
function onJobLog({ channel, state }, event) {
|
|
331
379
|
const timeInMicroseconds = BigInt(event.time) / BigInt(1e3);
|
|
@@ -380,6 +428,7 @@ async function loadAttempt(channel) {
|
|
|
380
428
|
}
|
|
381
429
|
|
|
382
430
|
// src/channels/worker-queue.ts
|
|
431
|
+
import EventEmitter from "node:events";
|
|
383
432
|
import { Socket as PhxSocket } from "phoenix";
|
|
384
433
|
import { WebSocket } from "ws";
|
|
385
434
|
|
|
@@ -405,9 +454,9 @@ var generateWorkerToken = async (secret, workerId) => {
|
|
|
405
454
|
var worker_token_default = generateWorkerToken;
|
|
406
455
|
|
|
407
456
|
// src/channels/worker-queue.ts
|
|
408
|
-
var connectToWorkerQueue = (endpoint, serverId, secret, SocketConstructor = PhxSocket) => {
|
|
409
|
-
|
|
410
|
-
|
|
457
|
+
var connectToWorkerQueue = (endpoint, serverId, secret, logger, SocketConstructor = PhxSocket) => {
|
|
458
|
+
const events = new EventEmitter();
|
|
459
|
+
worker_token_default(secret, serverId).then((token) => {
|
|
411
460
|
const socket = new SocketConstructor(endpoint, {
|
|
412
461
|
params: { token },
|
|
413
462
|
transport: WebSocket
|
|
@@ -417,20 +466,27 @@ var connectToWorkerQueue = (endpoint, serverId, secret, SocketConstructor = PhxS
|
|
|
417
466
|
didOpen = true;
|
|
418
467
|
const channel = socket.channel("worker:queue");
|
|
419
468
|
channel.join().receive("ok", () => {
|
|
420
|
-
|
|
469
|
+
logger.debug("Connected to worker queue socket");
|
|
470
|
+
events.emit("connect", { socket, channel });
|
|
421
471
|
}).receive("error", (e) => {
|
|
422
|
-
|
|
472
|
+
logger.error("ERROR", e);
|
|
423
473
|
}).receive("timeout", (e) => {
|
|
424
|
-
|
|
474
|
+
logger.error("TIMEOUT", e);
|
|
425
475
|
});
|
|
426
476
|
});
|
|
477
|
+
socket.onClose((_e) => {
|
|
478
|
+
logger.debug("queue socket closed");
|
|
479
|
+
events.emit("disconnect");
|
|
480
|
+
});
|
|
427
481
|
socket.onError((e) => {
|
|
428
482
|
if (!didOpen) {
|
|
429
|
-
|
|
483
|
+
events.emit("error", e.message);
|
|
484
|
+
didOpen = false;
|
|
430
485
|
}
|
|
431
486
|
});
|
|
432
487
|
socket.connect();
|
|
433
488
|
});
|
|
489
|
+
return events;
|
|
434
490
|
};
|
|
435
491
|
var worker_queue_default = connectToWorkerQueue;
|
|
436
492
|
|
|
@@ -438,17 +494,13 @@ var worker_queue_default = connectToWorkerQueue;
|
|
|
438
494
|
var DEFAULT_PORT = 1234;
|
|
439
495
|
var MIN_BACKOFF = 1e3;
|
|
440
496
|
var MAX_BACKOFF = 1e3 * 30;
|
|
441
|
-
function connect(app,
|
|
497
|
+
function connect(app, logger, options = {}) {
|
|
442
498
|
logger.debug("Connecting to Lightning at", options.lightning);
|
|
443
|
-
|
|
499
|
+
const onConnect = ({ socket, channel }) => {
|
|
444
500
|
logger.success("Connected to Lightning at", options.lightning);
|
|
445
501
|
app.socket = socket;
|
|
446
502
|
app.channel = channel;
|
|
447
503
|
if (!options.noLoop) {
|
|
448
|
-
if (app.killWorkloop) {
|
|
449
|
-
logger.info("Terminating old workloop");
|
|
450
|
-
app.killWorkloop();
|
|
451
|
-
}
|
|
452
504
|
logger.info("Starting workloop");
|
|
453
505
|
app.killWorkloop = workloop_default(
|
|
454
506
|
app,
|
|
@@ -467,17 +519,25 @@ function connect(app, engine, logger, options = {}) {
|
|
|
467
519
|
);
|
|
468
520
|
logger.break();
|
|
469
521
|
}
|
|
470
|
-
}
|
|
522
|
+
};
|
|
523
|
+
const onDisconnect = () => {
|
|
524
|
+
if (app.killWorkloop) {
|
|
525
|
+
app.killWorkloop();
|
|
526
|
+
delete app.killWorkloop;
|
|
527
|
+
logger.info("Connection to lightning lost.");
|
|
528
|
+
logger.info(
|
|
529
|
+
"Worker will automatically reconnect when lightning is back online."
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
const onError = (e) => {
|
|
471
534
|
logger.error(
|
|
472
535
|
"CRITICAL ERROR: could not connect to lightning at",
|
|
473
536
|
options.lightning
|
|
474
537
|
);
|
|
475
538
|
logger.debug(e);
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
connect(app, engine, logger, options);
|
|
479
|
-
}, 1e4);
|
|
480
|
-
});
|
|
539
|
+
};
|
|
540
|
+
worker_queue_default(options.lightning, app.id, options.secret, logger).on("connect", onConnect).on("disconnect", onDisconnect).on("error", onError);
|
|
481
541
|
}
|
|
482
542
|
function createServer(engine, options = {}) {
|
|
483
543
|
const logger = options.logger || createMockLogger2();
|
|
@@ -540,7 +600,7 @@ function createServer(engine, options = {}) {
|
|
|
540
600
|
};
|
|
541
601
|
app.use(router.routes());
|
|
542
602
|
if (options.lightning) {
|
|
543
|
-
connect(app,
|
|
603
|
+
connect(app, logger, options);
|
|
544
604
|
} else {
|
|
545
605
|
logger.warn("No lightning URL provided");
|
|
546
606
|
}
|
package/dist/start.js
CHANGED
|
@@ -5062,6 +5062,11 @@ var claim = (app, logger2 = mockLogger, maxWorkers = 5) => {
|
|
|
5062
5062
|
app.execute(attempt);
|
|
5063
5063
|
resolve5();
|
|
5064
5064
|
});
|
|
5065
|
+
}).receive("error", () => {
|
|
5066
|
+
logger2.debug("pull err");
|
|
5067
|
+
}).receive("timeout", () => {
|
|
5068
|
+
logger2.debug("pull timeout");
|
|
5069
|
+
reject(new Error("timeout"));
|
|
5065
5070
|
});
|
|
5066
5071
|
});
|
|
5067
5072
|
};
|
|
@@ -5192,6 +5197,30 @@ var stringify_default = (obj) => stringify(obj, (_key, value) => {
|
|
|
5192
5197
|
return value;
|
|
5193
5198
|
});
|
|
5194
5199
|
|
|
5200
|
+
// src/api/reasons.ts
|
|
5201
|
+
var calculateJobExitReason = (jobId, state, error) => {
|
|
5202
|
+
let reason = "success";
|
|
5203
|
+
let error_type = null;
|
|
5204
|
+
let error_message = null;
|
|
5205
|
+
if (error) {
|
|
5206
|
+
reason = error.severity ?? "crash";
|
|
5207
|
+
error_message = error.message;
|
|
5208
|
+
error_type = error.subtype || error.type || error.name;
|
|
5209
|
+
} else if (state.errors?.[jobId]) {
|
|
5210
|
+
reason = "fail";
|
|
5211
|
+
({ message: error_message, type: error_type } = state.errors[jobId]);
|
|
5212
|
+
}
|
|
5213
|
+
return { reason, error_type, error_message };
|
|
5214
|
+
};
|
|
5215
|
+
var calculateAttemptExitReason = (state) => {
|
|
5216
|
+
if (state.reasons) {
|
|
5217
|
+
const fail = Object.values(state.reasons).find(
|
|
5218
|
+
({ reason }) => reason === "fail"
|
|
5219
|
+
);
|
|
5220
|
+
return fail || { reason: "success", error_type: null, error_message: null };
|
|
5221
|
+
}
|
|
5222
|
+
};
|
|
5223
|
+
|
|
5195
5224
|
// src/api/execute.ts
|
|
5196
5225
|
var enc = new TextDecoder("utf-8");
|
|
5197
5226
|
var eventMap = {
|
|
@@ -5201,15 +5230,17 @@ var eventMap = {
|
|
|
5201
5230
|
"workflow-log": ATTEMPT_LOG,
|
|
5202
5231
|
"workflow-complete": ATTEMPT_COMPLETE
|
|
5203
5232
|
};
|
|
5233
|
+
var createAttemptState = (plan, options = {}) => ({
|
|
5234
|
+
plan,
|
|
5235
|
+
lastDataclipId: plan.initialState,
|
|
5236
|
+
dataclips: {},
|
|
5237
|
+
reasons: {},
|
|
5238
|
+
options
|
|
5239
|
+
});
|
|
5204
5240
|
function execute(channel, engine, logger2, plan, options = {}, onComplete = (_result) => {
|
|
5205
5241
|
}) {
|
|
5206
5242
|
logger2.info("execute...");
|
|
5207
|
-
const state =
|
|
5208
|
-
plan,
|
|
5209
|
-
lastDataclipId: plan.initialState,
|
|
5210
|
-
dataclips: {},
|
|
5211
|
-
options
|
|
5212
|
-
};
|
|
5243
|
+
const state = createAttemptState(plan, options);
|
|
5213
5244
|
const context = { channel, state, logger: logger2, onComplete };
|
|
5214
5245
|
const addEvent = (eventName, handler) => {
|
|
5215
5246
|
const wrappedFn = async (event) => {
|
|
@@ -5233,6 +5264,7 @@ function execute(channel, engine, logger2, plan, options = {}, onComplete = (_re
|
|
|
5233
5264
|
addEvent("workflow-start", onWorkflowStart),
|
|
5234
5265
|
addEvent("job-start", onJobStart),
|
|
5235
5266
|
addEvent("job-complete", onJobComplete),
|
|
5267
|
+
addEvent("job-error", onJobError),
|
|
5236
5268
|
addEvent("workflow-log", onJobLog),
|
|
5237
5269
|
addEvent("workflow-complete", onWorkflowComplete),
|
|
5238
5270
|
addEvent("workflow-error", onWorkflowError)
|
|
@@ -5270,7 +5302,15 @@ function onJobStart({ channel, state }, event) {
|
|
|
5270
5302
|
input_dataclip_id: state.lastDataclipId
|
|
5271
5303
|
});
|
|
5272
5304
|
}
|
|
5273
|
-
function
|
|
5305
|
+
function onJobError(context, event) {
|
|
5306
|
+
const { state, error, jobId } = event;
|
|
5307
|
+
if (state.errors?.[jobId]?.message === error.message) {
|
|
5308
|
+
onJobComplete(context, event);
|
|
5309
|
+
} else {
|
|
5310
|
+
onJobComplete(context, event, event.error);
|
|
5311
|
+
}
|
|
5312
|
+
}
|
|
5313
|
+
function onJobComplete({ channel, state }, event, error) {
|
|
5274
5314
|
const dataclipId = crypto3.randomUUID();
|
|
5275
5315
|
const run_id = state.activeRun;
|
|
5276
5316
|
const job_id = state.activeJob;
|
|
@@ -5281,12 +5321,20 @@ function onJobComplete({ channel, state }, event) {
|
|
|
5281
5321
|
state.lastDataclipId = dataclipId;
|
|
5282
5322
|
delete state.activeRun;
|
|
5283
5323
|
delete state.activeJob;
|
|
5324
|
+
const { reason, error_message, error_type } = calculateJobExitReason(
|
|
5325
|
+
job_id,
|
|
5326
|
+
event.state,
|
|
5327
|
+
error
|
|
5328
|
+
);
|
|
5329
|
+
state.reasons[job_id] = { reason, error_message, error_type };
|
|
5284
5330
|
return sendEvent(channel, RUN_COMPLETE, {
|
|
5285
5331
|
run_id,
|
|
5286
5332
|
job_id,
|
|
5287
5333
|
output_dataclip_id: dataclipId,
|
|
5288
5334
|
output_dataclip: stringify_default(event.state),
|
|
5289
|
-
reason
|
|
5335
|
+
reason,
|
|
5336
|
+
error_message,
|
|
5337
|
+
error_type
|
|
5290
5338
|
});
|
|
5291
5339
|
}
|
|
5292
5340
|
function onWorkflowStart({ channel }, _event) {
|
|
@@ -5294,20 +5342,20 @@ function onWorkflowStart({ channel }, _event) {
|
|
|
5294
5342
|
}
|
|
5295
5343
|
async function onWorkflowComplete({ state, channel, onComplete }, _event) {
|
|
5296
5344
|
const result = state.dataclips[state.lastDataclipId];
|
|
5345
|
+
const reason = calculateAttemptExitReason(state);
|
|
5297
5346
|
await sendEvent(channel, ATTEMPT_COMPLETE, {
|
|
5298
5347
|
final_dataclip_id: state.lastDataclipId,
|
|
5299
|
-
|
|
5300
|
-
reason: "ok"
|
|
5348
|
+
...reason
|
|
5301
5349
|
});
|
|
5302
|
-
onComplete(result);
|
|
5350
|
+
onComplete({ reason, state: result });
|
|
5303
5351
|
}
|
|
5304
5352
|
async function onWorkflowError({ state, channel, onComplete }, event) {
|
|
5353
|
+
const reason = calculateJobExitReason("", { data: {} }, event);
|
|
5305
5354
|
await sendEvent(channel, ATTEMPT_COMPLETE, {
|
|
5306
|
-
reason: "fail",
|
|
5307
5355
|
final_dataclip_id: state.lastDataclipId,
|
|
5308
|
-
|
|
5356
|
+
...reason
|
|
5309
5357
|
});
|
|
5310
|
-
onComplete({});
|
|
5358
|
+
onComplete({ reason });
|
|
5311
5359
|
}
|
|
5312
5360
|
function onJobLog({ channel, state }, event) {
|
|
5313
5361
|
const timeInMicroseconds = BigInt(event.time) / BigInt(1e3);
|
|
@@ -5362,6 +5410,7 @@ async function loadAttempt(channel) {
|
|
|
5362
5410
|
}
|
|
5363
5411
|
|
|
5364
5412
|
// src/channels/worker-queue.ts
|
|
5413
|
+
import EventEmitter2 from "node:events";
|
|
5365
5414
|
import { Socket as PhxSocket } from "phoenix";
|
|
5366
5415
|
import { WebSocket } from "ws";
|
|
5367
5416
|
|
|
@@ -5387,9 +5436,9 @@ var generateWorkerToken = async (secret, workerId) => {
|
|
|
5387
5436
|
var worker_token_default = generateWorkerToken;
|
|
5388
5437
|
|
|
5389
5438
|
// src/channels/worker-queue.ts
|
|
5390
|
-
var connectToWorkerQueue = (endpoint, serverId, secret, SocketConstructor = PhxSocket) => {
|
|
5391
|
-
|
|
5392
|
-
|
|
5439
|
+
var connectToWorkerQueue = (endpoint, serverId, secret, logger2, SocketConstructor = PhxSocket) => {
|
|
5440
|
+
const events = new EventEmitter2();
|
|
5441
|
+
worker_token_default(secret, serverId).then((token) => {
|
|
5393
5442
|
const socket = new SocketConstructor(endpoint, {
|
|
5394
5443
|
params: { token },
|
|
5395
5444
|
transport: WebSocket
|
|
@@ -5399,20 +5448,27 @@ var connectToWorkerQueue = (endpoint, serverId, secret, SocketConstructor = PhxS
|
|
|
5399
5448
|
didOpen = true;
|
|
5400
5449
|
const channel = socket.channel("worker:queue");
|
|
5401
5450
|
channel.join().receive("ok", () => {
|
|
5402
|
-
|
|
5451
|
+
logger2.debug("Connected to worker queue socket");
|
|
5452
|
+
events.emit("connect", { socket, channel });
|
|
5403
5453
|
}).receive("error", (e) => {
|
|
5404
|
-
|
|
5454
|
+
logger2.error("ERROR", e);
|
|
5405
5455
|
}).receive("timeout", (e) => {
|
|
5406
|
-
|
|
5456
|
+
logger2.error("TIMEOUT", e);
|
|
5407
5457
|
});
|
|
5408
5458
|
});
|
|
5459
|
+
socket.onClose((_e) => {
|
|
5460
|
+
logger2.debug("queue socket closed");
|
|
5461
|
+
events.emit("disconnect");
|
|
5462
|
+
});
|
|
5409
5463
|
socket.onError((e) => {
|
|
5410
5464
|
if (!didOpen) {
|
|
5411
|
-
|
|
5465
|
+
events.emit("error", e.message);
|
|
5466
|
+
didOpen = false;
|
|
5412
5467
|
}
|
|
5413
5468
|
});
|
|
5414
5469
|
socket.connect();
|
|
5415
5470
|
});
|
|
5471
|
+
return events;
|
|
5416
5472
|
};
|
|
5417
5473
|
var worker_queue_default = connectToWorkerQueue;
|
|
5418
5474
|
|
|
@@ -5420,17 +5476,13 @@ var worker_queue_default = connectToWorkerQueue;
|
|
|
5420
5476
|
var DEFAULT_PORT = 1234;
|
|
5421
5477
|
var MIN_BACKOFF = 1e3;
|
|
5422
5478
|
var MAX_BACKOFF = 1e3 * 30;
|
|
5423
|
-
function connect(app,
|
|
5479
|
+
function connect(app, logger2, options = {}) {
|
|
5424
5480
|
logger2.debug("Connecting to Lightning at", options.lightning);
|
|
5425
|
-
|
|
5481
|
+
const onConnect = ({ socket, channel }) => {
|
|
5426
5482
|
logger2.success("Connected to Lightning at", options.lightning);
|
|
5427
5483
|
app.socket = socket;
|
|
5428
5484
|
app.channel = channel;
|
|
5429
5485
|
if (!options.noLoop) {
|
|
5430
|
-
if (app.killWorkloop) {
|
|
5431
|
-
logger2.info("Terminating old workloop");
|
|
5432
|
-
app.killWorkloop();
|
|
5433
|
-
}
|
|
5434
5486
|
logger2.info("Starting workloop");
|
|
5435
5487
|
app.killWorkloop = workloop_default(
|
|
5436
5488
|
app,
|
|
@@ -5449,17 +5501,25 @@ function connect(app, engine, logger2, options = {}) {
|
|
|
5449
5501
|
);
|
|
5450
5502
|
logger2.break();
|
|
5451
5503
|
}
|
|
5452
|
-
}
|
|
5504
|
+
};
|
|
5505
|
+
const onDisconnect = () => {
|
|
5506
|
+
if (app.killWorkloop) {
|
|
5507
|
+
app.killWorkloop();
|
|
5508
|
+
delete app.killWorkloop;
|
|
5509
|
+
logger2.info("Connection to lightning lost.");
|
|
5510
|
+
logger2.info(
|
|
5511
|
+
"Worker will automatically reconnect when lightning is back online."
|
|
5512
|
+
);
|
|
5513
|
+
}
|
|
5514
|
+
};
|
|
5515
|
+
const onError = (e) => {
|
|
5453
5516
|
logger2.error(
|
|
5454
5517
|
"CRITICAL ERROR: could not connect to lightning at",
|
|
5455
5518
|
options.lightning
|
|
5456
5519
|
);
|
|
5457
5520
|
logger2.debug(e);
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
connect(app, engine, logger2, options);
|
|
5461
|
-
}, 1e4);
|
|
5462
|
-
});
|
|
5521
|
+
};
|
|
5522
|
+
worker_queue_default(options.lightning, app.id, options.secret, logger2).on("connect", onConnect).on("disconnect", onDisconnect).on("error", onError);
|
|
5463
5523
|
}
|
|
5464
5524
|
function createServer(engine, options = {}) {
|
|
5465
5525
|
const logger2 = options.logger || createMockLogger2();
|
|
@@ -5522,7 +5582,7 @@ function createServer(engine, options = {}) {
|
|
|
5522
5582
|
};
|
|
5523
5583
|
app.use(router.routes());
|
|
5524
5584
|
if (options.lightning) {
|
|
5525
|
-
connect(app,
|
|
5585
|
+
connect(app, logger2, options);
|
|
5526
5586
|
} else {
|
|
5527
5587
|
logger2.warn("No lightning URL provided");
|
|
5528
5588
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openfn/ws-worker",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "A Websocket Worker to connect Lightning to a Runtime Engine",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
"koa-logger": "^3.2.1",
|
|
22
22
|
"phoenix": "^1.7.7",
|
|
23
23
|
"ws": "^8.14.1",
|
|
24
|
-
"@openfn/engine-multi": "0.1.
|
|
24
|
+
"@openfn/engine-multi": "0.1.7",
|
|
25
25
|
"@openfn/logger": "0.0.19",
|
|
26
|
-
"@openfn/runtime": "0.0
|
|
26
|
+
"@openfn/runtime": "0.1.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/koa": "^2.13.5",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"tsup": "^6.2.3",
|
|
41
41
|
"typescript": "^4.6.4",
|
|
42
42
|
"yargs": "^17.6.2",
|
|
43
|
-
"@openfn/lightning-mock": "1.0.
|
|
43
|
+
"@openfn/lightning-mock": "1.0.8"
|
|
44
44
|
},
|
|
45
45
|
"files": [
|
|
46
46
|
"dist",
|