querysub 0.146.0 → 0.148.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "querysub",
3
- "version": "0.146.0",
3
+ "version": "0.148.0",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "note1": "note on node-forge fork, see https://github.com/digitalbazaar/forge/issues/744 for details",
@@ -580,8 +580,10 @@ class AuthorityPathValueStorage {
580
580
  childPaths.add(path);
581
581
  }
582
582
 
583
- /** Called by PathWatcher / ourself when paths are unwatched / destroyed */
584
- public markPathAsUnwatched(path: string) {
583
+ /** Called by PathWatcher / ourself when paths are unwatched / destroyed, to free memory.
584
+ * NOOPS if we are the authority of the path.
585
+ */
586
+ public destroyPath(path: string) {
585
587
  // If we are the authority we:
586
588
  // 1) Need to maintain the values for other nodes
587
589
  // 2) Don't need to worry about the values getting out of date,
@@ -1096,7 +1098,7 @@ class AuthorityPathValueStorage {
1096
1098
 
1097
1099
  if (pathsToClear.size > 0) {
1098
1100
  for (let path of pathsToClear) {
1099
- this.markPathAsUnwatched(path);
1101
+ this.destroyPath(path);
1100
1102
  // I'm not sure if removing it as a parent is needed, or... maybe it is needed,
1101
1103
  // and this isn't enough of a check?
1102
1104
  this.markParentPathAsUnwatched(path);
@@ -1322,27 +1324,6 @@ class PathWatcher {
1322
1324
 
1323
1325
  let pathsWatched = this.watchersToPaths.get(callback);
1324
1326
 
1325
- for (let path of config.paths) {
1326
- if (pathsWatched) {
1327
- pathsWatched.paths.delete(path);
1328
- }
1329
-
1330
- let watchers = this.watchers.get(path);
1331
- if (!watchers) continue;
1332
- watchers.watchers.delete(callback);
1333
- if (watchers.watchers.size === 0) {
1334
- this.watchers.delete(path);
1335
-
1336
- let parentPath = getParentPathStr(path);
1337
- let parentWatchers = this.watcherParentToPath.get(parentPath);
1338
- if (parentWatchers) {
1339
- parentWatchers.delete(path);
1340
- }
1341
-
1342
- fullyUnwatched.paths.push(path);
1343
- authorityStorage.markPathAsUnwatched(path);
1344
- }
1345
- }
1346
1327
  for (let path of config.parentPaths) {
1347
1328
  if (pathsWatched) {
1348
1329
  pathsWatched.parents.delete(path);
@@ -1364,6 +1345,46 @@ class PathWatcher {
1364
1345
 
1365
1346
  if (watchersObj.size === 0) {
1366
1347
  this.parentWatchers.delete(unpackedPath);
1348
+
1349
+ let childPaths = authorityStorage.getPathsFromParent(unpackedPath);
1350
+ // Destroy values that now have no watchers (no value watchers, and now no parent watcher
1351
+ if (childPaths) {
1352
+ for (let childPath of childPaths) {
1353
+ if (!this.watchers.has(childPath)) {
1354
+ authorityStorage.destroyPath(childPath);
1355
+ }
1356
+ }
1357
+ }
1358
+ }
1359
+ }
1360
+ }
1361
+
1362
+ // NOTE: Unwatch parents, then values, as parent paths might keep alive value path watches.
1363
+ for (let path of config.paths) {
1364
+ if (pathsWatched) {
1365
+ pathsWatched.paths.delete(path);
1366
+ }
1367
+
1368
+ let watchers = this.watchers.get(path);
1369
+ if (!watchers) continue;
1370
+ watchers.watchers.delete(callback);
1371
+ if (watchers.watchers.size === 0) {
1372
+ this.watchers.delete(path);
1373
+
1374
+ let parentPath = getParentPathStr(path);
1375
+ let parentWatchers = this.watcherParentToPath.get(parentPath);
1376
+ if (parentWatchers) {
1377
+ parentWatchers.delete(path);
1378
+ }
1379
+
1380
+ fullyUnwatched.paths.push(path);
1381
+ // NOTE: If the parent is being watched, don't destroy the value.
1382
+ // - The fact that we only do the check here does mean that if the path is unwatched
1383
+ // first and then later the parent path is unwatched we will fail to properly destroy
1384
+ // the path. However, in practice, either both are unwatched at the same time, or more
1385
+ // likely, the value watch only goes away because it's made redundant by the parent watch.
1386
+ if (!this.parentWatchers.has(parentPath)) {
1387
+ authorityStorage.destroyPath(path);
1367
1388
  }
1368
1389
  }
1369
1390
  }
@@ -367,8 +367,8 @@ async function edgeNodeFunction(config: {
367
367
  let PARALLEL_FACTOR = 3;
368
368
  for (let i = 0; i < edgeNodes.length; i += PARALLEL_FACTOR) {
369
369
  let node = await new Promise<EdgeNodeConfig | undefined>(resolve => {
370
+ let finished = 0;
370
371
  for (let j = 0; j < PARALLEL_FACTOR; j++) {
371
- let finished = 0;
372
372
  ((async () => {
373
373
  try {
374
374
  let node = edgeNodes[i + j];
@@ -422,11 +422,6 @@ function predictCall(config: {
422
422
  logErrors(predictPromise);
423
423
 
424
424
  let didCancel = false;
425
- // ALWAYS reject the prediction eventually, in case the function runner server is down.
426
- // - ALSO, once it goes back up, the write will be too far in the future, and our lock won't be rejected.
427
- // And don't really want endTime to be infinitely in the future, as then cleanup code has a harder time
428
- // removing the lock, as then in theory it can always be rejected, no matter how long we wait.
429
- setTimeout(rejectPrediction, Querysub.PREDICTION_MAX_LIFESPAN);
430
425
  function rejectPrediction() {
431
426
  if (didCancel) return;
432
427
  didCancel = true;
@@ -439,9 +434,18 @@ function predictCall(config: {
439
434
  reason: "prediction timeout rejected",
440
435
  }]);
441
436
  }
442
- setTimeout(auditPrediction, Querysub.PREDICTION_MAX_LIFESPAN);
443
- function auditPrediction() {
437
+
438
+ setTimeout(cleanupPrediction, Querysub.PREDICTION_MAX_LIFESPAN);
439
+ function cleanupPrediction() {
444
440
  if (didCancel) return;
441
+
442
+ // ALWAYS reject the prediction, in case the function runner server is down.
443
+ // - ALSO, once it goes back up, the write likely will be too far in the future, so our lock won't be rejected,
444
+ // will will cause the bad value to stick around.
445
+ // We could fix this with an endTime infinitely in the future, but then the lock can never be GCed,
446
+ // which create a memory leak.
447
+ rejectPrediction();
448
+
445
449
  if (Querysub.AUDIT_PREDICTIONS && predictions) {
446
450
  const afterTime = { time: call.runAtTime.time, version: Number.MAX_SAFE_INTEGER, creatorId: 0 };
447
451
  // Clone predictions, to strip symbols