@openfn/ws-worker 1.14.3 → 1.14.4

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,12 @@
1
1
  # ws-worker
2
2
 
3
+ ## 1.14.4
4
+
5
+ ### Patch Changes
6
+
7
+ - 5a613e7: Adjust logging
8
+ - 3a33557: Count outstanding claim requests as capacity. This fixes an issue where `work-available` messages can cause a worker to over-claim, particularly during periods of high load on the Lightning database.
9
+
3
10
  ## 1.14.3
4
11
 
5
12
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -89,6 +89,7 @@ interface ServerApp extends Koa {
89
89
  socket?: any;
90
90
  queueChannel?: Channel;
91
91
  workflows: Record<string, true | Context>;
92
+ openClaims: Record<string, number>;
92
93
  destroyed: boolean;
93
94
  events: EventEmitter;
94
95
  server: Server;
package/dist/index.js CHANGED
@@ -140,14 +140,25 @@ var ClaimError = class extends Error {
140
140
  this.abort = true;
141
141
  }
142
142
  };
143
+ var claimIdGen = 0;
143
144
  var claim = (app, logger = mockLogger, options = {}) => {
144
145
  return new Promise((resolve, reject) => {
145
- const { maxWorkers = 5 } = options;
146
+ app.openClaims ??= {};
147
+ const { maxWorkers = 5, demand = 1 } = options;
146
148
  const podName = NAME ? `[${NAME}] ` : "";
147
149
  const activeWorkers = Object.keys(app.workflows).length;
150
+ const pendingClaims = Object.values(app.openClaims).reduce(
151
+ (a, b) => a + b,
152
+ 0
153
+ );
148
154
  if (activeWorkers >= maxWorkers) {
149
155
  app.workloop?.stop(`server at capacity (${activeWorkers}/${maxWorkers})`);
150
156
  return reject(new ClaimError("Server at capacity"));
157
+ } else if (activeWorkers + pendingClaims >= maxWorkers) {
158
+ app.workloop?.stop(
159
+ `server at capacity (${activeWorkers}/${maxWorkers}, ${pendingClaims} pending)`
160
+ );
161
+ return reject(new ClaimError("Server at capacity"));
151
162
  }
152
163
  if (!app.queueChannel) {
153
164
  logger.warn("skipping claim attempt: websocket unavailable");
@@ -159,12 +170,15 @@ var claim = (app, logger = mockLogger, options = {}) => {
159
170
  logger.warn("skipping claim attempt: channel closed");
160
171
  return reject(e);
161
172
  }
173
+ const claimId = ++claimIdGen;
174
+ app.openClaims[claimId] = demand;
162
175
  logger.debug(`requesting run (capacity ${activeWorkers}/${maxWorkers})`);
163
176
  const start = Date.now();
164
177
  app.queueChannel.push(CLAIM, {
165
- demand: 1,
178
+ demand,
166
179
  worker_name: NAME || null
167
180
  }).receive("ok", ({ runs }) => {
181
+ delete app.openClaims[claimId];
168
182
  const duration = Date.now() - start;
169
183
  logger.debug(
170
184
  `${podName}claimed ${runs.length} runs in ${duration}ms (${runs.length ? runs.map((r) => r.id).join(",") : "-"})`
@@ -192,8 +206,11 @@ var claim = (app, logger = mockLogger, options = {}) => {
192
206
  resolve();
193
207
  });
194
208
  }).receive("error", (e) => {
209
+ delete app.openClaims[claimId];
195
210
  logger.error("Error on claim", e);
211
+ reject(new Error("claim error"));
196
212
  }).receive("timeout", () => {
213
+ delete app.openClaims[claimId];
197
214
  logger.error("TIMEOUT on claim. Runs may be lost.");
198
215
  reject(new Error("timeout"));
199
216
  });
@@ -1015,9 +1032,8 @@ var joinRunChannel = (socket, token, runId, logger, timeout = 30) => {
1015
1032
  channel.onClose(() => {
1016
1033
  logger.debug(`Leaving ${channelName}`);
1017
1034
  });
1018
- channel.onError((e) => {
1019
- logger.debug(`Error in ${channelName}`);
1020
- logger.debug(e);
1035
+ channel.onError((...args) => {
1036
+ logger.debug(`Critical error in channel ${channelName}`, args);
1021
1037
  });
1022
1038
  });
1023
1039
  };
@@ -1240,6 +1256,7 @@ function createServer(engine, options = {}) {
1240
1256
  logger.debug(str);
1241
1257
  })
1242
1258
  );
1259
+ app.openClaims = {};
1243
1260
  app.workflows = {};
1244
1261
  app.destroyed = false;
1245
1262
  app.server = app.listen(port);
@@ -1279,7 +1296,6 @@ function createServer(engine, options = {}) {
1279
1296
  collectionsVersion: app.options.collectionsVersion,
1280
1297
  monorepoPath: app.options.monorepoDir
1281
1298
  });
1282
- logger.debug("converted run body into execution plan:", plan);
1283
1299
  if (plan.workflow.credentials?.collections_token) {
1284
1300
  plan.workflow.credentials.collections_token = token;
1285
1301
  }
package/dist/start.js CHANGED
@@ -280,14 +280,25 @@ var ClaimError = class extends Error {
280
280
  this.abort = true;
281
281
  }
282
282
  };
283
+ var claimIdGen = 0;
283
284
  var claim = (app, logger2 = mockLogger, options = {}) => {
284
285
  return new Promise((resolve5, reject) => {
285
- const { maxWorkers = 5 } = options;
286
+ app.openClaims ??= {};
287
+ const { maxWorkers = 5, demand = 1 } = options;
286
288
  const podName = NAME ? `[${NAME}] ` : "";
287
289
  const activeWorkers = Object.keys(app.workflows).length;
290
+ const pendingClaims = Object.values(app.openClaims).reduce(
291
+ (a, b) => a + b,
292
+ 0
293
+ );
288
294
  if (activeWorkers >= maxWorkers) {
289
295
  app.workloop?.stop(`server at capacity (${activeWorkers}/${maxWorkers})`);
290
296
  return reject(new ClaimError("Server at capacity"));
297
+ } else if (activeWorkers + pendingClaims >= maxWorkers) {
298
+ app.workloop?.stop(
299
+ `server at capacity (${activeWorkers}/${maxWorkers}, ${pendingClaims} pending)`
300
+ );
301
+ return reject(new ClaimError("Server at capacity"));
291
302
  }
292
303
  if (!app.queueChannel) {
293
304
  logger2.warn("skipping claim attempt: websocket unavailable");
@@ -299,12 +310,15 @@ var claim = (app, logger2 = mockLogger, options = {}) => {
299
310
  logger2.warn("skipping claim attempt: channel closed");
300
311
  return reject(e);
301
312
  }
313
+ const claimId = ++claimIdGen;
314
+ app.openClaims[claimId] = demand;
302
315
  logger2.debug(`requesting run (capacity ${activeWorkers}/${maxWorkers})`);
303
316
  const start = Date.now();
304
317
  app.queueChannel.push(CLAIM, {
305
- demand: 1,
318
+ demand,
306
319
  worker_name: NAME || null
307
320
  }).receive("ok", ({ runs }) => {
321
+ delete app.openClaims[claimId];
308
322
  const duration = Date.now() - start;
309
323
  logger2.debug(
310
324
  `${podName}claimed ${runs.length} runs in ${duration}ms (${runs.length ? runs.map((r) => r.id).join(",") : "-"})`
@@ -332,8 +346,11 @@ var claim = (app, logger2 = mockLogger, options = {}) => {
332
346
  resolve5();
333
347
  });
334
348
  }).receive("error", (e) => {
349
+ delete app.openClaims[claimId];
335
350
  logger2.error("Error on claim", e);
351
+ reject(new Error("claim error"));
336
352
  }).receive("timeout", () => {
353
+ delete app.openClaims[claimId];
337
354
  logger2.error("TIMEOUT on claim. Runs may be lost.");
338
355
  reject(new Error("timeout"));
339
356
  });
@@ -1155,9 +1172,8 @@ var joinRunChannel = (socket, token, runId, logger2, timeout = 30) => {
1155
1172
  channel.onClose(() => {
1156
1173
  logger2.debug(`Leaving ${channelName}`);
1157
1174
  });
1158
- channel.onError((e) => {
1159
- logger2.debug(`Error in ${channelName}`);
1160
- logger2.debug(e);
1175
+ channel.onError((...args2) => {
1176
+ logger2.debug(`Critical error in channel ${channelName}`, args2);
1161
1177
  });
1162
1178
  });
1163
1179
  };
@@ -1380,6 +1396,7 @@ function createServer(engine, options = {}) {
1380
1396
  logger2.debug(str);
1381
1397
  })
1382
1398
  );
1399
+ app.openClaims = {};
1383
1400
  app.workflows = {};
1384
1401
  app.destroyed = false;
1385
1402
  app.server = app.listen(port);
@@ -1419,7 +1436,6 @@ function createServer(engine, options = {}) {
1419
1436
  collectionsVersion: app.options.collectionsVersion,
1420
1437
  monorepoPath: app.options.monorepoDir
1421
1438
  });
1422
- logger2.debug("converted run body into execution plan:", plan);
1423
1439
  if (plan.workflow.credentials?.collections_token) {
1424
1440
  plan.workflow.credentials.collections_token = token;
1425
1441
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openfn/ws-worker",
3
- "version": "1.14.3",
3
+ "version": "1.14.4",
4
4
  "description": "A Websocket Worker to connect Lightning to a Runtime Engine",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -25,8 +25,8 @@
25
25
  "ws": "^8.18.0",
26
26
  "@openfn/engine-multi": "1.6.8",
27
27
  "@openfn/logger": "1.0.5",
28
- "@openfn/runtime": "1.7.1",
29
- "@openfn/lexicon": "^1.2.2"
28
+ "@openfn/lexicon": "^1.2.2",
29
+ "@openfn/runtime": "1.7.1"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@types/koa": "^2.13.5",
@@ -43,7 +43,7 @@
43
43
  "tsup": "^6.2.3",
44
44
  "typescript": "^4.6.4",
45
45
  "yargs": "^17.6.2",
46
- "@openfn/lightning-mock": "2.2.2"
46
+ "@openfn/lightning-mock": "2.2.3"
47
47
  },
48
48
  "files": [
49
49
  "dist",