@openfn/ws-worker 0.2.10 → 0.2.12

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 CHANGED
@@ -1,5 +1,25 @@
1
1
  # ws-worker
2
2
 
3
+ ## 0.2.12
4
+
5
+ ### Patch Changes
6
+
7
+ - 6c3e9e42: Ensure capacity is also set on the engine
8
+ - Updated dependencies [05ccc10b]
9
+ - Updated dependencies [7235bf5e]
10
+ - @openfn/engine-multi@0.2.3
11
+
12
+ ## 0.2.11
13
+
14
+ ### Patch Changes
15
+
16
+ - 22339c6: Add MAX_RUN_MEMORY env var and option to limit the memory available to each run
17
+ - 04ac3cc: Include duration and threadid in run-complete
18
+ - 340b96e: Send memory usage to lightning on run:complete
19
+ - Updated dependencies
20
+ - @openfn/engine-multi@0.2.2
21
+ - @openfn/runtime@0.2.1
22
+
3
23
  ## 0.2.10
4
24
 
5
25
  ### Patch Changes
package/dist/index.js CHANGED
@@ -409,7 +409,7 @@ function onJobStart({ channel, state }, event) {
409
409
  });
410
410
  }
411
411
  function onJobError(context, event) {
412
- const { state, error, jobId } = event;
412
+ const { state = {}, error, jobId } = event;
413
413
  if (state.errors?.[jobId]?.message === error.message) {
414
414
  onJobComplete(context, event);
415
415
  } else {
@@ -424,12 +424,12 @@ function onJobComplete({ channel, state }, event, error) {
424
424
  state.dataclips = {};
425
425
  }
426
426
  state.dataclips[dataclipId] = event.state;
427
+ delete state.activeRun;
428
+ delete state.activeJob;
427
429
  state.lastDataclipId = dataclipId;
428
430
  event.next?.forEach((nextJobId) => {
429
431
  state.inputDataclips[nextJobId] = dataclipId;
430
432
  });
431
- delete state.activeRun;
432
- delete state.activeJob;
433
433
  const { reason, error_message, error_type } = calculateJobExitReason(
434
434
  job_id,
435
435
  event.state,
@@ -443,7 +443,10 @@ function onJobComplete({ channel, state }, event, error) {
443
443
  output_dataclip: stringify_default(event.state),
444
444
  reason,
445
445
  error_message,
446
- error_type
446
+ error_type,
447
+ mem: event.mem,
448
+ duration: event.duration,
449
+ thread_id: event.threadId
447
450
  };
448
451
  return sendEvent(channel, RUN_COMPLETE, evt);
449
452
  }
@@ -459,9 +462,13 @@ async function onWorkflowComplete({ state, channel, onFinish }, _event) {
459
462
  });
460
463
  onFinish({ reason, state: result });
461
464
  }
462
- async function onWorkflowError({ state, channel, logger, onFinish }, event) {
465
+ async function onWorkflowError(context, event) {
466
+ const { state, channel, logger, onFinish } = context;
463
467
  try {
464
468
  const reason = calculateJobExitReason("", { data: {} }, event);
469
+ if (state.activeJob) {
470
+ await onJobError(context, { error: event });
471
+ }
465
472
  await sendEvent(channel, ATTEMPT_COMPLETE, {
466
473
  final_dataclip_id: state.lastDataclipId,
467
474
  ...reason
@@ -470,6 +477,7 @@ async function onWorkflowError({ state, channel, logger, onFinish }, event) {
470
477
  } catch (e) {
471
478
  logger.error("ERROR in workflow-error handler:", e.message);
472
479
  logger.error(e);
480
+ onFinish({});
473
481
  }
474
482
  }
475
483
  function onJobLog({ channel, state }, event) {
package/dist/start.js CHANGED
@@ -4865,6 +4865,7 @@ import createRTE from "@openfn/engine-multi";
4865
4865
 
4866
4866
  // src/mock/runtime-engine.ts
4867
4867
  import { EventEmitter } from "node:events";
4868
+ import crypto from "node:crypto";
4868
4869
  import run from "@openfn/runtime";
4869
4870
 
4870
4871
  // src/mock/resolvers.ts
@@ -4911,6 +4912,7 @@ async function createMock() {
4911
4912
  }) => {
4912
4913
  const { id, jobs } = xplan;
4913
4914
  activeWorkflows[id] = true;
4915
+ const threadId = crypto.randomUUID();
4914
4916
  for (const job of jobs) {
4915
4917
  if (typeof job.configuration === "string") {
4916
4918
  job.configuration = await options.resolvers?.credential?.(
@@ -4925,6 +4927,7 @@ async function createMock() {
4925
4927
  log: (...args2) => {
4926
4928
  dispatch("workflow-log", {
4927
4929
  workflowId: id,
4930
+ threadId,
4928
4931
  level: "info",
4929
4932
  json: true,
4930
4933
  message: args2,
@@ -4941,24 +4944,27 @@ async function createMock() {
4941
4944
  notify: (name, payload) => {
4942
4945
  dispatch(name, {
4943
4946
  workflowId: id,
4947
+ threadId,
4944
4948
  ...payload
4945
4949
  });
4946
4950
  }
4947
4951
  }
4948
4952
  };
4949
4953
  setTimeout(async () => {
4950
- dispatch("workflow-start", { workflowId: id });
4954
+ dispatch("workflow-start", { workflowId: id, threadId });
4951
4955
  try {
4952
4956
  await run(xplan, void 0, opts);
4957
+ dispatch("workflow-complete", { workflowId: id, threadId });
4953
4958
  } catch (e) {
4954
4959
  dispatch("workflow-error", {
4960
+ threadId,
4955
4961
  workflowId: id,
4956
4962
  type: e.name,
4957
4963
  message: e.message
4958
4964
  });
4965
+ } finally {
4966
+ delete activeWorkflows[id];
4959
4967
  }
4960
- delete activeWorkflows[id];
4961
- dispatch("workflow-complete", { workflowId: id });
4962
4968
  }, 1);
4963
4969
  };
4964
4970
  const getStatus = () => {
@@ -5146,10 +5152,10 @@ var startWorkloop = (app, logger2, minBackoff2, maxBackoff2, maxWorkers) => {
5146
5152
  var workloop_default = startWorkloop;
5147
5153
 
5148
5154
  // src/api/execute.ts
5149
- import crypto2 from "node:crypto";
5155
+ import crypto3 from "node:crypto";
5150
5156
 
5151
5157
  // src/util/convert-attempt.ts
5152
- import crypto from "node:crypto";
5158
+ import crypto2 from "node:crypto";
5153
5159
  var conditions = {
5154
5160
  on_job_success: (upstreamId) => `Boolean(!state.errors?.["${upstreamId}"] ?? true)`,
5155
5161
  on_job_failure: (upstreamId) => `Boolean(state.errors && state.errors["${upstreamId}"])`,
@@ -5196,7 +5202,7 @@ var convert_attempt_default = (attempt) => {
5196
5202
  }
5197
5203
  if (attempt.jobs?.length) {
5198
5204
  attempt.jobs.forEach((job) => {
5199
- const id = job.id || crypto.randomUUID();
5205
+ const id = job.id || crypto2.randomUUID();
5200
5206
  nodes[id] = {
5201
5207
  id,
5202
5208
  configuration: job.credential_id,
@@ -5379,7 +5385,7 @@ var sendEvent = (channel, event, payload) => new Promise((resolve5, reject) => {
5379
5385
  channel.push(event, payload).receive("error", reject).receive("timeout", () => reject(new Error("timeout"))).receive("ok", resolve5);
5380
5386
  });
5381
5387
  function onJobStart({ channel, state }, event) {
5382
- state.activeRun = crypto2.randomUUID();
5388
+ state.activeRun = crypto3.randomUUID();
5383
5389
  state.activeJob = event.jobId;
5384
5390
  const input_dataclip_id = state.inputDataclips[event.jobId];
5385
5391
  return sendEvent(channel, RUN_START, {
@@ -5389,7 +5395,7 @@ function onJobStart({ channel, state }, event) {
5389
5395
  });
5390
5396
  }
5391
5397
  function onJobError(context, event) {
5392
- const { state, error, jobId } = event;
5398
+ const { state = {}, error, jobId } = event;
5393
5399
  if (state.errors?.[jobId]?.message === error.message) {
5394
5400
  onJobComplete(context, event);
5395
5401
  } else {
@@ -5397,19 +5403,19 @@ function onJobError(context, event) {
5397
5403
  }
5398
5404
  }
5399
5405
  function onJobComplete({ channel, state }, event, error) {
5400
- const dataclipId = crypto2.randomUUID();
5406
+ const dataclipId = crypto3.randomUUID();
5401
5407
  const run_id = state.activeRun;
5402
5408
  const job_id = state.activeJob;
5403
5409
  if (!state.dataclips) {
5404
5410
  state.dataclips = {};
5405
5411
  }
5406
5412
  state.dataclips[dataclipId] = event.state;
5413
+ delete state.activeRun;
5414
+ delete state.activeJob;
5407
5415
  state.lastDataclipId = dataclipId;
5408
5416
  event.next?.forEach((nextJobId) => {
5409
5417
  state.inputDataclips[nextJobId] = dataclipId;
5410
5418
  });
5411
- delete state.activeRun;
5412
- delete state.activeJob;
5413
5419
  const { reason, error_message, error_type } = calculateJobExitReason(
5414
5420
  job_id,
5415
5421
  event.state,
@@ -5423,7 +5429,10 @@ function onJobComplete({ channel, state }, event, error) {
5423
5429
  output_dataclip: stringify_default(event.state),
5424
5430
  reason,
5425
5431
  error_message,
5426
- error_type
5432
+ error_type,
5433
+ mem: event.mem,
5434
+ duration: event.duration,
5435
+ thread_id: event.threadId
5427
5436
  };
5428
5437
  return sendEvent(channel, RUN_COMPLETE, evt);
5429
5438
  }
@@ -5439,9 +5448,13 @@ async function onWorkflowComplete({ state, channel, onFinish }, _event) {
5439
5448
  });
5440
5449
  onFinish({ reason, state: result });
5441
5450
  }
5442
- async function onWorkflowError({ state, channel, logger: logger2, onFinish }, event) {
5451
+ async function onWorkflowError(context, event) {
5452
+ const { state, channel, logger: logger2, onFinish } = context;
5443
5453
  try {
5444
5454
  const reason = calculateJobExitReason("", { data: {} }, event);
5455
+ if (state.activeJob) {
5456
+ await onJobError(context, { error: event });
5457
+ }
5445
5458
  await sendEvent(channel, ATTEMPT_COMPLETE, {
5446
5459
  final_dataclip_id: state.lastDataclipId,
5447
5460
  ...reason
@@ -5450,6 +5463,7 @@ async function onWorkflowError({ state, channel, logger: logger2, onFinish }, ev
5450
5463
  } catch (e) {
5451
5464
  logger2.error("ERROR in workflow-error handler:", e.message);
5452
5465
  logger2.error(e);
5466
+ onFinish({});
5453
5467
  }
5454
5468
  }
5455
5469
  function onJobLog({ channel, state }, event) {
@@ -5707,7 +5721,7 @@ function createServer(engine, options = {}) {
5707
5721
  var server_default = createServer;
5708
5722
 
5709
5723
  // src/start.ts
5710
- var { WORKER_REPO_DIR, WORKER_SECRET } = process.env;
5724
+ var { WORKER_REPO_DIR, WORKER_SECRET, MAX_RUN_MEMORY } = process.env;
5711
5725
  var args = yargs_default(hideBin(process.argv)).command("server", "Start a ws-worker server").option("port", {
5712
5726
  alias: "p",
5713
5727
  description: "Port to run the server on",
@@ -5743,6 +5757,10 @@ var args = yargs_default(hideBin(process.argv)).command("server", "Start a ws-wo
5743
5757
  description: "max concurrent workers",
5744
5758
  default: 5,
5745
5759
  type: "number"
5760
+ }).option("run-memory", {
5761
+ description: "Maximum memory allocated to a single run, in mb",
5762
+ type: "number",
5763
+ default: MAX_RUN_MEMORY ? parseInt(MAX_RUN_MEMORY) : 500
5746
5764
  }).parse();
5747
5765
  var logger = createLogger("SRV", { level: args.log });
5748
5766
  if (args.lightning === "mock") {
@@ -5778,7 +5796,11 @@ if (args.mock) {
5778
5796
  engineReady(engine);
5779
5797
  });
5780
5798
  } else {
5781
- createRTE({ repoDir: args.repoDir }).then((engine) => {
5799
+ createRTE({
5800
+ repoDir: args.repoDir,
5801
+ memoryLimitMb: args.runMemory,
5802
+ maxWorkers: args.capacity
5803
+ }).then((engine) => {
5782
5804
  logger.debug("engine created");
5783
5805
  engineReady(engine);
5784
5806
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openfn/ws-worker",
3
- "version": "0.2.10",
3
+ "version": "0.2.12",
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.2.1",
24
+ "@openfn/engine-multi": "0.2.3",
25
25
  "@openfn/logger": "0.0.19",
26
- "@openfn/runtime": "0.2.0"
26
+ "@openfn/runtime": "0.2.1"
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.1.3"
43
+ "@openfn/lightning-mock": "1.1.5"
44
44
  },
45
45
  "files": [
46
46
  "dist",