querysub 0.426.0 → 0.428.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.426.0",
3
+ "version": "0.428.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",
@@ -10,6 +10,7 @@ import { measureFnc } from "socket-function/src/profiling/measure";
10
10
  import { getRoutingOverride, hasPrefixHash } from "./PathRouterRouteOverride";
11
11
  import { sha256 } from "js-sha256";
12
12
  import { rangesOverlap, removeRange } from "../rangeMath";
13
+ import { decodeParentFilter } from "./hackedPackedPathParentFiltering";
13
14
 
14
15
 
15
16
  // Cases
@@ -57,6 +58,19 @@ function matchesPrefix(matcher: PrefixMatcher, path: string): boolean {
57
58
  return false;
58
59
  }
59
60
  }
61
+ if (getPathIndex(path, matcher.childKeyIndex) === undefined) return false;
62
+ return true;
63
+ }
64
+ // Checks if they're asking for the exact prefix, which would mean that the children would be the direct children of this prefix matcher.
65
+ function isPrefixParent(matcher: PrefixMatcher, path: string): boolean {
66
+ if (!path.startsWith(matcher.prefix)) return false;
67
+ let partsA = getPathFromStr(matcher.originalPrefix);
68
+ let partsB = getPathFromStr(path);
69
+ for (let i = 0; i < partsA.length; i++) {
70
+ // Empty string is wildcard
71
+ if (partsA[i] === "") continue;
72
+ if (partsB[i] !== partsA[i]) return false;
73
+ }
60
74
  return true;
61
75
  }
62
76
  /** Empty string path part becomes a wildcard */
@@ -535,11 +549,17 @@ export class PathRouter {
535
549
  range: { start: number; end: number };
536
550
  }[];
537
551
  } {
552
+ let parentRange = decodeParentFilter(path) || {
553
+ start: 0,
554
+ end: 1,
555
+ };
556
+ path = hack_stripPackedPath(path);
538
557
  let preferredNodeIds = new Set(config?.preferredNodeIds ?? []);
539
558
 
540
559
  // If a prefix is a parent of path, then it is the same as matching just the path directly
541
560
  // (If our prefix directly equals one of the other matches, then it's more complicated, As then, the child keys of path are what is hashed, and so all the children will have different routes, so we might match multiple nodes. The same thing if we're matching the remaining case, in which case it's a full path hash, so the child key matters, and again, different routes).
542
561
  // - The different route case is how the FuntionRunner works, and without it large databases couldn't run functions. However, most applications won't directly use it.
562
+ // NOTE: The only own nodes flag is actually so we can access this before the topology finishes synchronizing. Because we want to be able to call get child read nodes from path value core for some really basic stuff. It also is what the caller wants, but that's not a good enough reason to add this check. The reason we have this check is because without it, the topology won't have finished synchronizing and startup won't work.
543
563
  let allSources = config?.onlyOwnNodes ? [{ nodeId: getOwnNodeId(), authoritySpec: authorityLookup.getOurSpec() }] : authorityLookup.getTopologySync();
544
564
  // Prefer our own node
545
565
  sort(allSources, x => isOwnNodeId(x.nodeId) ? -1 : 1);
@@ -549,14 +569,13 @@ export class PathRouter {
549
569
 
550
570
 
551
571
  // Direct prefix. This happens for things like calls and functions, it requires more advanced routing as it means we're going to route between multiple servers, but... it is important
552
- let hasPrefix = allSources.filter(x => x.authoritySpec.prefixes.some(y => y.prefix === path)).map(x => x.authoritySpec);
572
+ let hasPrefix = allSources.filter(x => x.authoritySpec.prefixes.some(y => isPrefixParent(y, path))).map(x => x.authoritySpec);
553
573
  if (hasPrefix.length > 0) {
554
574
  shuffle(hasPrefix, Math.random());
555
575
  sort(hasPrefix, x => preferredNodeIds.has(x.nodeId) ? -1 : 1);
556
576
 
557
577
  let missingRanges: { start: number; end: number }[] = [{
558
- start: 0,
559
- end: 1,
578
+ ...parentRange
560
579
  }];
561
580
  let usedParts: {
562
581
  nodeId: string;
@@ -585,7 +604,7 @@ export class PathRouter {
585
604
  let nestedMatches = allSources.filter(x => {
586
605
  // There's nested prefixes, so if we match any prefix explicitly, we can't just take one of the previous prefixes because that isn't how the hashing will work.
587
606
  // - This happens if it's a direct match, but one of the shards is down, in which case we can't get a full match.
588
- if (x.authoritySpec.prefixes.some(y => y.prefix === path)) return false;
607
+ if (x.authoritySpec.prefixes.some(y => isPrefixParent(y, path))) return false;
589
608
 
590
609
  // If our path, which we're going to read the children of, is the child of another path, then it means in that other path, the child key will be known to us constant, and so we're going to match exactly one authority.
591
610
  return (
@@ -645,13 +664,11 @@ export class PathRouter {
645
664
  }
646
665
 
647
666
 
648
-
649
-
650
667
  if (!config?.onlyOwnNodes) {
651
668
  // TODO: We could maybe match a partial match. However, even that is suspect. The site being partially broken is almost worse than it being completely broken. We should just get ALL the shards running again...
652
669
 
653
670
  // NOTE: We *could* actually synchronize it even if it doesn't have a prefix shard as we can fall back to just the full path sharding. However, it becomes very complicated if we want a specific range, and then it becomes complicated if it then switches to prefix hashing (With the nodes that were using the full path hashing slowly going away). AND... key synchronization IS slow, so it's good to discourage it in general.
654
- console.error(`Want to sync a prefix which is not under an existing prefix, nor equal to a prefix. 1) The servers are down. 2) Don't access the .keys() 3) call addRoutingPrefixForDeploy to add a route/parent route explicitly (as is done in PathFunctionRunner.ts). Path: ${JSON.stringify(path)}`, { path, allSources });
671
+ console.error(`Want to sync a prefix which is not under an existing prefix, nor equal to a prefix. 1) The servers are down. 2) Don't access the .keys() 3) call addRoutingPrefixForDeploy to add a route/parent route explicitly (as is done in PathFunctionRunner.ts). Path: ${JSON.stringify(path)}`, { path, allSources: allSources.map(x => x.authoritySpec) });
655
672
  }
656
673
  return { nodes: [] };
657
674
  }
@@ -369,8 +369,10 @@ class PathWatcher {
369
369
 
370
370
  // initialTriggers.parentPaths => triggerPaths
371
371
  for (let parentPath of initialTriggers.parentPaths) {
372
- let valuePaths = authorityStorage.getPathsFromParent(parentPath);
372
+ // NOTE: We have to trigger all of the values without the hacked path restriction. This should be safe, if a little bit inefficient, because we're still not going to send the values to watchers who aren't watching them. But we need to trigger all of it because it might be the case that we had synchronized 99% of the path and someone was watching 100% of the path and we just finished synchronizing the last 1%. So the parent path trigger that we received only has 1%, but we need to send everything. And it's too complicated to determine exactly what we need to send as in the intersection of all the watchers. So it's easier to just trigger all the value paths and what they're watching will naturally be sent.
373
+ let valuePaths = authorityStorage.getPathsFromParent(hack_stripPackedPath(parentPath));
373
374
  for (let valuePath of valuePaths || []) {
375
+ if (!authorityStorage.isSynced(valuePath)) continue;
374
376
  // IMPORTANT! Add all the values to the initial triggers so then later we not only trigger them but know their initial trigger so we can make sure that the is initial trigger logic for each value runs as well.
375
377
  initialTriggers.values.add(valuePath);
376
378
  triggerPaths.add(valuePath);
@@ -378,7 +380,9 @@ class PathWatcher {
378
380
 
379
381
  let latestParentWatches = this.parentWatchers.get(hack_stripPackedPath(parentPath));
380
382
  if (!latestParentWatches) continue;
381
- for (let { watchers } of latestParentWatches.values()) {
383
+ for (let [packedPath, { watchers }] of latestParentWatches) {
384
+ // ONLY trigger, if packedPath is fully synchronized
385
+ if (!authorityStorage.isParentSynced(packedPath)) continue;
382
386
  for (let watcher of watchers) {
383
387
  if (onlyTriggerNodeId && watcher !== onlyTriggerNodeId) continue;
384
388
  let changes = changedPerCallbacks.get(watcher);
@@ -386,7 +390,7 @@ class PathWatcher {
386
390
  changes = { values: new Set(), initialTriggers: { values: new Set(), parentPaths: new Set() } };
387
391
  changedPerCallbacks.set(watcher, changes);
388
392
  }
389
- changes.initialTriggers.parentPaths.add(parentPath);
393
+ changes.initialTriggers.parentPaths.add(packedPath);
390
394
  }
391
395
  }
392
396
  }
@@ -609,11 +609,12 @@ class AuthorityPathValueStorage {
609
609
  return false;
610
610
  }
611
611
  public isParentSynced(path: string) {
612
+ let originalPath = path;
612
613
  let range = decodeParentFilter(path);
613
614
  path = hack_stripPackedPath(path);
614
615
  if (PathRouter.isLocalPath(path)) return true;
615
616
 
616
- let synced = this.parentsSynced.get(path);
617
+ let synced = this.parentsSynced.get(originalPath) || this.parentsSynced.get(path);
617
618
  if (synced === true) return true;
618
619
  // See if the ranges received so far cover the requested range. If we only request a partial range, then this will always be the case. We'll never fully synchronize the path.
619
620
  if (synced && range) {
@@ -626,24 +627,11 @@ class AuthorityPathValueStorage {
626
627
 
627
628
  // Self authority check
628
629
  {
629
- let nodes = PathRouter.getChildReadNodes(path, { onlyOwnNodes: true });
630
- if (nodes.nodes.length > 0) {
631
- if (nodes.nodes.every(x => isOwnNodeId(x.nodeId))) {
632
- // If it's not a partial 30 and we're self authority, we're always going to be self authority, so we can just cache it in the lookup.
633
- this.parentsSynced.set(path, true);
634
- return true;
635
- }
636
- // Might be a partial authority
637
- if (range) {
638
- let selfNodes = nodes.nodes.filter(x => isOwnNodeId(x.nodeId));
639
- let missingRanges: { start: number; end: number }[] = [{ start: range.start, end: range.end }];
640
- for (let range of selfNodes) {
641
- removeRange(missingRanges, range.range);
642
- }
643
- if (missingRanges.length === 0) {
644
- return true;
645
- }
646
- }
630
+ let nodes = PathRouter.getChildReadNodes(originalPath, { onlyOwnNodes: true });
631
+ if (nodes.nodes.length > 0 && nodes.nodes.every(x => isOwnNodeId(x.nodeId))) {
632
+ // If we're the self authority of it, it's not going to change, so we can just cache it safely.
633
+ this.parentsSynced.set(originalPath, true);
634
+ return true;
647
635
  }
648
636
  }
649
637