@openfn/ws-worker 1.15.1 → 1.15.3

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,18 @@
1
1
  # ws-worker
2
2
 
3
+ ## 1.15.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 5688813: Allow the worker to shutdown gracefully while claims are still in-flight. Runs will be completed before the server closes
8
+
9
+ ## 1.15.2
10
+
11
+ ### Patch Changes
12
+
13
+ - Updated dependencies [362d6bf]
14
+ - @openfn/engine-multi@1.6.12
15
+
3
16
  ## 1.15.1
4
17
 
5
18
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -99,6 +99,7 @@ interface ServerApp extends Koa {
99
99
  execute: ({ id, token }: ClaimRun) => Promise<void>;
100
100
  destroy: () => void;
101
101
  resumeWorkloop: () => void;
102
+ claim: () => Promise<any>;
102
103
  }
103
104
  declare function createServer(engine: RuntimeEngine, options?: ServerOptions): ServerApp;
104
105
 
@@ -113,6 +114,9 @@ declare const RUN_LOG = "run:log";
113
114
  declare const STEP_START = "step:start";
114
115
  declare const STEP_COMPLETE = "step:complete";
115
116
  declare const INTERNAL_RUN_COMPLETE = "server:run-complete";
117
+ declare const INTERNAL_CLAIM_START = "server:claim-start";
118
+ declare const INTERNAL_CLAIM_COMPLETE = "server:claim-complete";
119
+ declare const INTERNAL_SOCKET_READY = "server:socket-ready";
116
120
  type QueueEvents = {
117
121
  [CLAIM]: l.ClaimPayload;
118
122
  };
@@ -140,4 +144,4 @@ type RunReplies = {
140
144
  [STEP_COMPLETE]: l.StepCompleteReply;
141
145
  };
142
146
 
143
- export { CLAIM, GET_CREDENTIAL, GET_DATACLIP, GET_PLAN, INTERNAL_RUN_COMPLETE, QueueEventReplies, QueueEvents, RUN_COMPLETE, RUN_LOG, RUN_START, RunEvents, RunReplies, STEP_COMPLETE, STEP_START, WORK_AVAILABLE, createServer as default };
147
+ export { CLAIM, GET_CREDENTIAL, GET_DATACLIP, GET_PLAN, INTERNAL_CLAIM_COMPLETE, INTERNAL_CLAIM_START, INTERNAL_RUN_COMPLETE, INTERNAL_SOCKET_READY, QueueEventReplies, QueueEvents, RUN_COMPLETE, RUN_LOG, RUN_START, RunEvents, RunReplies, STEP_COMPLETE, STEP_START, WORK_AVAILABLE, createServer as default };
package/dist/index.js CHANGED
@@ -22,6 +22,9 @@ var RUN_LOG = "run:log";
22
22
  var STEP_START = "step:start";
23
23
  var STEP_COMPLETE = "step:complete";
24
24
  var INTERNAL_RUN_COMPLETE = "server:run-complete";
25
+ var INTERNAL_CLAIM_START = "server:claim-start";
26
+ var INTERNAL_CLAIM_COMPLETE = "server:claim-complete";
27
+ var INTERNAL_SOCKET_READY = "server:socket-ready";
25
28
 
26
29
  // src/api/destroy.ts
27
30
  var destroy = async (app, logger) => {
@@ -30,13 +33,13 @@ var destroy = async (app, logger) => {
30
33
  new Promise((resolve) => {
31
34
  app.destroyed = true;
32
35
  app.workloop?.stop("server closed");
33
- app.queueChannel?.leave();
34
36
  app.server.close(async () => {
35
37
  resolve();
36
38
  });
37
39
  }),
38
40
  new Promise(async (resolve) => {
39
- await waitForRuns(app, logger);
41
+ await waitForRunsAndClaims(app, logger);
42
+ app.queueChannel?.leave();
40
43
  await app.engine.destroy();
41
44
  app.socket?.disconnect();
42
45
  resolve();
@@ -44,24 +47,26 @@ var destroy = async (app, logger) => {
44
47
  ]);
45
48
  logger.success("Server closed");
46
49
  };
47
- var waitForRuns = (app, logger) => new Promise((resolve) => {
50
+ var waitForRunsAndClaims = (app, logger) => new Promise((resolve) => {
48
51
  const log = () => {
49
52
  logger.debug(
50
- `Waiting for ${Object.keys(app.workflows).length} runs to complete...`
53
+ `Waiting for ${Object.keys(app.workflows).length} runs and ${Object.keys(app.openClaims).length} claims to complete...`
51
54
  );
52
55
  };
53
- const onRunComplete = () => {
54
- if (Object.keys(app.workflows).length === 0) {
56
+ const checkAllClear = () => {
57
+ if (Object.keys(app.workflows).length + Object.keys(app.openClaims).length === 0) {
55
58
  logger.debug("All runs completed!");
56
- app.events.off(INTERNAL_RUN_COMPLETE, onRunComplete);
59
+ app.events.off(INTERNAL_RUN_COMPLETE, checkAllClear);
60
+ app.events.off(INTERNAL_CLAIM_COMPLETE, checkAllClear);
57
61
  resolve();
58
62
  } else {
59
63
  log();
60
64
  }
61
65
  };
62
- if (Object.keys(app.workflows).length) {
66
+ if (Object.keys(app.workflows).length || Object.keys(app.openClaims).length) {
63
67
  log();
64
- app.events.on(INTERNAL_RUN_COMPLETE, onRunComplete);
68
+ app.events.on(INTERNAL_RUN_COMPLETE, checkAllClear);
69
+ app.events.on(INTERNAL_CLAIM_COMPLETE, checkAllClear);
65
70
  } else {
66
71
  logger.debug("No active runs detected, closing immediately");
67
72
  resolve();
@@ -173,6 +178,7 @@ var claim = (app, logger = mockLogger, options = {}) => {
173
178
  const claimId = ++claimIdGen;
174
179
  app.openClaims[claimId] = demand;
175
180
  logger.debug(`requesting run (capacity ${activeWorkers}/${maxWorkers})`);
181
+ app.events.emit(INTERNAL_CLAIM_START);
176
182
  const start = Date.now();
177
183
  app.queueChannel.push(CLAIM, {
178
184
  demand,
@@ -184,6 +190,7 @@ var claim = (app, logger = mockLogger, options = {}) => {
184
190
  `${podName}claimed ${runs.length} runs in ${duration}ms (${runs.length ? runs.map((r) => r.id).join(",") : "-"})`
185
191
  );
186
192
  if (!runs?.length) {
193
+ app.events.emit(INTERNAL_CLAIM_COMPLETE, { runs });
187
194
  return reject(new Error("No runs returned"));
188
195
  }
189
196
  runs.forEach(async (run) => {
@@ -203,8 +210,9 @@ var claim = (app, logger = mockLogger, options = {}) => {
203
210
  }
204
211
  logger.debug(`${podName} starting run ${run.id}`);
205
212
  app.execute(run);
206
- resolve();
207
213
  });
214
+ resolve();
215
+ app.events.emit(INTERNAL_CLAIM_COMPLETE, { runs });
208
216
  }).receive("error", (e) => {
209
217
  delete app.openClaims[claimId];
210
218
  logger.error("Error on claim", e);
@@ -1180,6 +1188,7 @@ function connect(app, logger, options = {}) {
1180
1188
  logger.info(` curl -X POST http://localhost:${port}/claim`);
1181
1189
  logger.break();
1182
1190
  }
1191
+ app.events.emit(INTERNAL_SOCKET_READY);
1183
1192
  app.resumeWorkloop();
1184
1193
  };
1185
1194
  const onDisconnect = () => {
@@ -1205,8 +1214,10 @@ function connect(app, logger, options = {}) {
1205
1214
  };
1206
1215
  const onMessage = (event) => {
1207
1216
  if (event === WORK_AVAILABLE) {
1208
- claim_default(app, logger, { maxWorkers: options.maxWorkflows }).catch(() => {
1209
- });
1217
+ if (!app.destroyed) {
1218
+ claim_default(app, logger, { maxWorkers: options.maxWorkflows }).catch(() => {
1219
+ });
1220
+ }
1210
1221
  }
1211
1222
  };
1212
1223
  worker_queue_default(options.lightning, app.id, options.secret, logger, {
@@ -1355,6 +1366,11 @@ function createServer(engine, options = {}) {
1355
1366
  ctx.status = 204;
1356
1367
  });
1357
1368
  });
1369
+ app.claim = () => {
1370
+ return claim_default(app, logger, {
1371
+ maxWorkers: options.maxWorkflows
1372
+ });
1373
+ };
1358
1374
  app.destroy = () => destroy_default(app, logger);
1359
1375
  app.use(router.routes());
1360
1376
  if (options.lightning) {
@@ -1390,7 +1406,10 @@ export {
1390
1406
  GET_CREDENTIAL,
1391
1407
  GET_DATACLIP,
1392
1408
  GET_PLAN,
1409
+ INTERNAL_CLAIM_COMPLETE,
1410
+ INTERNAL_CLAIM_START,
1393
1411
  INTERNAL_RUN_COMPLETE,
1412
+ INTERNAL_SOCKET_READY,
1394
1413
  RUN_COMPLETE,
1395
1414
  RUN_LOG,
1396
1415
  RUN_START,
package/dist/start.js CHANGED
@@ -162,6 +162,9 @@ var RUN_LOG = "run:log";
162
162
  var STEP_START = "step:start";
163
163
  var STEP_COMPLETE = "step:complete";
164
164
  var INTERNAL_RUN_COMPLETE = "server:run-complete";
165
+ var INTERNAL_CLAIM_START = "server:claim-start";
166
+ var INTERNAL_CLAIM_COMPLETE = "server:claim-complete";
167
+ var INTERNAL_SOCKET_READY = "server:socket-ready";
165
168
 
166
169
  // src/api/destroy.ts
167
170
  var destroy = async (app, logger2) => {
@@ -170,13 +173,13 @@ var destroy = async (app, logger2) => {
170
173
  new Promise((resolve5) => {
171
174
  app.destroyed = true;
172
175
  app.workloop?.stop("server closed");
173
- app.queueChannel?.leave();
174
176
  app.server.close(async () => {
175
177
  resolve5();
176
178
  });
177
179
  }),
178
180
  new Promise(async (resolve5) => {
179
- await waitForRuns(app, logger2);
181
+ await waitForRunsAndClaims(app, logger2);
182
+ app.queueChannel?.leave();
180
183
  await app.engine.destroy();
181
184
  app.socket?.disconnect();
182
185
  resolve5();
@@ -184,24 +187,26 @@ var destroy = async (app, logger2) => {
184
187
  ]);
185
188
  logger2.success("Server closed");
186
189
  };
187
- var waitForRuns = (app, logger2) => new Promise((resolve5) => {
190
+ var waitForRunsAndClaims = (app, logger2) => new Promise((resolve5) => {
188
191
  const log = () => {
189
192
  logger2.debug(
190
- `Waiting for ${Object.keys(app.workflows).length} runs to complete...`
193
+ `Waiting for ${Object.keys(app.workflows).length} runs and ${Object.keys(app.openClaims).length} claims to complete...`
191
194
  );
192
195
  };
193
- const onRunComplete = () => {
194
- if (Object.keys(app.workflows).length === 0) {
196
+ const checkAllClear = () => {
197
+ if (Object.keys(app.workflows).length + Object.keys(app.openClaims).length === 0) {
195
198
  logger2.debug("All runs completed!");
196
- app.events.off(INTERNAL_RUN_COMPLETE, onRunComplete);
199
+ app.events.off(INTERNAL_RUN_COMPLETE, checkAllClear);
200
+ app.events.off(INTERNAL_CLAIM_COMPLETE, checkAllClear);
197
201
  resolve5();
198
202
  } else {
199
203
  log();
200
204
  }
201
205
  };
202
- if (Object.keys(app.workflows).length) {
206
+ if (Object.keys(app.workflows).length || Object.keys(app.openClaims).length) {
203
207
  log();
204
- app.events.on(INTERNAL_RUN_COMPLETE, onRunComplete);
208
+ app.events.on(INTERNAL_RUN_COMPLETE, checkAllClear);
209
+ app.events.on(INTERNAL_CLAIM_COMPLETE, checkAllClear);
205
210
  } else {
206
211
  logger2.debug("No active runs detected, closing immediately");
207
212
  resolve5();
@@ -313,6 +318,7 @@ var claim = (app, logger2 = mockLogger, options = {}) => {
313
318
  const claimId = ++claimIdGen;
314
319
  app.openClaims[claimId] = demand;
315
320
  logger2.debug(`requesting run (capacity ${activeWorkers}/${maxWorkers})`);
321
+ app.events.emit(INTERNAL_CLAIM_START);
316
322
  const start = Date.now();
317
323
  app.queueChannel.push(CLAIM, {
318
324
  demand,
@@ -324,6 +330,7 @@ var claim = (app, logger2 = mockLogger, options = {}) => {
324
330
  `${podName}claimed ${runs.length} runs in ${duration}ms (${runs.length ? runs.map((r) => r.id).join(",") : "-"})`
325
331
  );
326
332
  if (!runs?.length) {
333
+ app.events.emit(INTERNAL_CLAIM_COMPLETE, { runs });
327
334
  return reject(new Error("No runs returned"));
328
335
  }
329
336
  runs.forEach(async (run2) => {
@@ -343,8 +350,9 @@ var claim = (app, logger2 = mockLogger, options = {}) => {
343
350
  }
344
351
  logger2.debug(`${podName} starting run ${run2.id}`);
345
352
  app.execute(run2);
346
- resolve5();
347
353
  });
354
+ resolve5();
355
+ app.events.emit(INTERNAL_CLAIM_COMPLETE, { runs });
348
356
  }).receive("error", (e) => {
349
357
  delete app.openClaims[claimId];
350
358
  logger2.error("Error on claim", e);
@@ -1320,6 +1328,7 @@ function connect(app, logger2, options = {}) {
1320
1328
  logger2.info(` curl -X POST http://localhost:${port}/claim`);
1321
1329
  logger2.break();
1322
1330
  }
1331
+ app.events.emit(INTERNAL_SOCKET_READY);
1323
1332
  app.resumeWorkloop();
1324
1333
  };
1325
1334
  const onDisconnect = () => {
@@ -1345,8 +1354,10 @@ function connect(app, logger2, options = {}) {
1345
1354
  };
1346
1355
  const onMessage = (event) => {
1347
1356
  if (event === WORK_AVAILABLE) {
1348
- claim_default(app, logger2, { maxWorkers: options.maxWorkflows }).catch(() => {
1349
- });
1357
+ if (!app.destroyed) {
1358
+ claim_default(app, logger2, { maxWorkers: options.maxWorkflows }).catch(() => {
1359
+ });
1360
+ }
1350
1361
  }
1351
1362
  };
1352
1363
  worker_queue_default(options.lightning, app.id, options.secret, logger2, {
@@ -1495,6 +1506,11 @@ function createServer(engine, options = {}) {
1495
1506
  ctx.status = 204;
1496
1507
  });
1497
1508
  });
1509
+ app.claim = () => {
1510
+ return claim_default(app, logger2, {
1511
+ maxWorkers: options.maxWorkflows
1512
+ });
1513
+ };
1498
1514
  app.destroy = () => destroy_default(app, logger2);
1499
1515
  app.use(router.routes());
1500
1516
  if (options.lightning) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openfn/ws-worker",
3
- "version": "1.15.1",
3
+ "version": "1.15.3",
4
4
  "description": "A Websocket Worker to connect Lightning to a Runtime Engine",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -23,9 +23,9 @@
23
23
  "koa-logger": "^3.2.1",
24
24
  "phoenix": "1.7.10",
25
25
  "ws": "^8.18.3",
26
- "@openfn/engine-multi": "1.6.11",
27
- "@openfn/logger": "1.0.6",
26
+ "@openfn/engine-multi": "1.6.12",
28
27
  "@openfn/lexicon": "^1.2.3",
28
+ "@openfn/logger": "1.0.6",
29
29
  "@openfn/runtime": "1.7.2"
30
30
  },
31
31
  "devDependencies": {
@@ -43,7 +43,7 @@
43
43
  "tsup": "^6.7.0",
44
44
  "typescript": "^4.9.5",
45
45
  "yargs": "^17.7.2",
46
- "@openfn/lightning-mock": "2.2.6"
46
+ "@openfn/lightning-mock": "2.2.7"
47
47
  },
48
48
  "files": [
49
49
  "dist",