querysub 0.405.0 → 0.407.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.
Files changed (31) hide show
  1. package/bin/deploy-prefixes.js +7 -0
  2. package/package.json +2 -1
  3. package/src/-f-node-discovery/NodeDiscovery.ts +0 -2
  4. package/src/0-path-value-core/AuthorityLookup.ts +7 -2
  5. package/src/0-path-value-core/PathRouter.ts +170 -84
  6. package/src/0-path-value-core/PathRouterRouteOverride.ts +2 -2
  7. package/src/0-path-value-core/PathRouterServerAuthoritySpec.tsx +2 -8
  8. package/src/0-path-value-core/PathValueCommitter.ts +65 -30
  9. package/src/0-path-value-core/PathValueController.ts +2 -4
  10. package/src/0-path-value-core/PathWatcher.ts +7 -4
  11. package/src/0-path-value-core/ShardPrefixes.ts +4 -0
  12. package/src/0-path-value-core/hackedPackedPathParentFiltering.ts +12 -31
  13. package/src/0-path-value-core/pathValueArchives.ts +3 -3
  14. package/src/0-path-value-core/pathValueCore.ts +32 -24
  15. package/src/0-path-value-core/startupAuthority.ts +9 -9
  16. package/src/1-path-client/RemoteWatcher.ts +188 -170
  17. package/src/1-path-client/pathValueClientWatcher.ts +6 -11
  18. package/src/2-proxy/garbageCollection.ts +4 -3
  19. package/src/2-proxy/pathValueProxy.ts +2 -3
  20. package/src/3-path-functions/PathFunctionRunner.ts +3 -1
  21. package/src/3-path-functions/syncSchema.ts +6 -2
  22. package/src/4-deploy/deployGetFunctionsInner.ts +1 -1
  23. package/src/4-deploy/deployPrefixes.ts +14 -0
  24. package/src/4-deploy/edgeNodes.ts +1 -1
  25. package/src/diagnostics/SyncTestPage.tsx +19 -8
  26. package/src/functional/promiseCache.ts +5 -2
  27. package/src/path.ts +15 -2
  28. package/src/rangeMath.ts +41 -0
  29. package/tempnotes.txt +12 -26
  30. package/test.ts +8 -4
  31. package/src/diagnostics/logs/BrowserLargeFileCache.ts +0 -64
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Always local, as we want to always use the local code? Might not be needed anymore?
4
+ process.argv.push("--local");
5
+
6
+ require("typenode");
7
+ require("../src/4-deploy/deployPrefixes");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "querysub",
3
- "version": "0.405.0",
3
+ "version": "0.407.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",
@@ -23,6 +23,7 @@
23
23
  },
24
24
  "bin": {
25
25
  "deploy": "./bin/deploy.js",
26
+ "deploy-prefixes": "./bin/deploy-prefixes.js",
26
27
  "server": "./bin/server.js",
27
28
  "server-public": "./bin/server-public.js",
28
29
  "function": "./bin/function.js",
@@ -46,8 +46,6 @@ let DISK_AUDIT_RATE = timeInMinute * 15;
46
46
  // probably is less than that). Which is around 2.5 cents on digital ocean IF we go over
47
47
  // our 1TB/month allowance.
48
48
  let API_AUDIT_RATE = timeInSecond * 30;
49
- // BUT, for now, poll less often... because I think it is lagging our 2 core potato digital ocean server.
50
- API_AUDIT_RATE = timeInMinute * 5;
51
49
  let API_AUDIT_COUNT = 12;
52
50
 
53
51
 
@@ -5,7 +5,7 @@ import { archiveJSONT } from "../-a-archives/archivesJSONT";
5
5
  import { getDomain, isPublic } from "../config";
6
6
  import { cache, lazy } from "socket-function/src/caching";
7
7
  import { SocketFunction } from "socket-function/SocketFunction";
8
- import { runInSerial, runInfinitePollCallAtStart } from "socket-function/src/batching";
8
+ import { delay, runInSerial, runInfinitePollCallAtStart } from "socket-function/src/batching";
9
9
  import { getAllNodeIds, getOwnNodeId, isOwnNodeId, onNodeBroadcasted, syncNodesNow, watchDeltaNodeIds, watchNodeIds } from "../-f-node-discovery/NodeDiscovery";
10
10
  import { IdentityController_getCurrentReconnectNodeIdAssert } from "../-c-identity/IdentityController";
11
11
  import { requiresNetworkTrustHook } from "../-d-trust/NetworkTrust2";
@@ -15,6 +15,7 @@ import { timeoutToError } from "../errors";
15
15
  import { AuthoritySpec } from "./PathRouter";
16
16
  import { formatTime } from "socket-function/src/formatting/format";
17
17
  import { getAllAuthoritySpec, getEmptyAuthoritySpec } from "./PathRouterServerAuthoritySpec";
18
+ import { getPrefixesForDeploy } from "../3-path-functions/syncSchema";
18
19
 
19
20
 
20
21
  let NETWORK_POLL_INTERVAL = timeInMinute * 5;
@@ -201,9 +202,13 @@ class AuthorityLookup {
201
202
  if (isClient()) {
202
203
  // Doesn't matter what the node ID is, we should really only be connecting to the browser node ID, Which will have all the data for the current domain.
203
204
  // - Get all node IDs should restrict our nodes to just the browser node ID. If we ever change this, then either it's redundant nodes and they all have all the same data, or we need to figure out what data they have, And as their proxies, it probably won't be their actual authority data. So that will require new API functions, etc.
205
+ await new Promise(r => setImmediate(r));
206
+ await delay(1);
204
207
  this.updatePaths(nodeId, {
205
- ...getAllAuthoritySpec(),
206
208
  nodeId: nodeId,
209
+ prefixes: await getPrefixesForDeploy(),
210
+ routeStart: 0,
211
+ routeEnd: 1,
207
212
  }, true);
208
213
  return;
209
214
  }
@@ -9,6 +9,7 @@ import { unique } from "../misc";
9
9
  import { measureFnc } from "socket-function/src/profiling/measure";
10
10
  import { getRoutingOverride, hasPrefixHash } from "./PathRouterRouteOverride";
11
11
  import { sha256 } from "js-sha256";
12
+ import { removeRange } from "../rangeMath";
12
13
 
13
14
 
14
15
  // Cases
@@ -32,13 +33,29 @@ export type AuthoritySpec = {
32
33
  routeEnd: number;
33
34
  // If the path.startsWith(prefix), but prefix !== path, then we hash getPathIndex(path, hashIndex)
34
35
  // - For now, let's just never add overlapping prefixes.
35
- prefixes: {
36
- prefix: string;
37
- hashIndex: number;
38
- }[];
36
+ prefixes: string[];
39
37
  excludeDefault?: boolean;
40
38
  };
41
39
 
40
+ export function debugSpec(spec: AuthoritySpec) {
41
+ return {
42
+ info: `${spec.routeStart}-${spec.routeEnd} (${spec.prefixes.length} prefixes${spec.excludeDefault ? " excluding default" : ""})`,
43
+ spec: { do: { not: { expand: { spec } } } },
44
+ };
45
+ }
46
+
47
+ function getMatchingPrefix(spec: AuthoritySpec, path: string): string | undefined {
48
+ let longestPrefix: string | undefined;
49
+ for (let prefix of spec.prefixes) {
50
+ if (path.startsWith(prefix) && prefix !== path) {
51
+ if (!longestPrefix || longestPrefix.length < prefix.length) {
52
+ longestPrefix = prefix;
53
+ }
54
+ }
55
+ }
56
+ return longestPrefix;
57
+ }
58
+
42
59
 
43
60
  export class PathRouter {
44
61
 
@@ -50,6 +67,10 @@ export class PathRouter {
50
67
  */
51
68
  @measureFnc
52
69
  public static getRouteChildKey(path: string): number {
70
+ let override = getRoutingOverride(path);
71
+ if (override) {
72
+ return override.route;
73
+ }
53
74
  let key = getLastPathPart(path);
54
75
  return this.getSingleKeyRoute(key);
55
76
  }
@@ -64,17 +85,16 @@ export class PathRouter {
64
85
  path = hack_stripPackedPath(path);
65
86
  let override = getRoutingOverride(path);
66
87
  if (override) {
67
- if (!hasPrefixHash({ spec, prefixHash: override.prefixHash })) return -1;
88
+ if (spec.excludeDefault && !hasPrefixHash({ spec, prefixHash: override.prefixHash })) return -1;
89
+ if (override.route < spec.routeStart || override.route >= spec.routeEnd) return -1;
68
90
  return override.route;
69
91
  }
70
92
 
71
- let prefix = spec.prefixes.find(x => path.startsWith(x.prefix) && x.prefix !== path);
93
+ let prefix = getMatchingPrefix(spec, path);
72
94
  if (prefix) {
73
- let key = getPathIndex(path, prefix.hashIndex);
95
+ let key = getPathIndex(path, getPathDepth(prefix));
74
96
  if (key === undefined) {
75
- require("debugbreak")(2);
76
- debugger;
77
- throw new Error(`Impossible, hash index ${prefix.hashIndex} is out of range for path ${path}, but it matched the prefix ${prefix.prefix}`);
97
+ throw new Error(`Impossible, hash index ${getPathDepth(prefix)} is out of range for path ${path}, but it matched the prefix ${prefix}`);
78
98
  }
79
99
  return this.getSingleKeyRoute(key);
80
100
  }
@@ -84,6 +104,17 @@ export class PathRouter {
84
104
  return hash;
85
105
  }
86
106
 
107
+ // Mostly for debugging
108
+ @measureFnc
109
+ public static getAllRoutes(path: string): number[] {
110
+ let routes: number[] = [];
111
+ for (let authority of authorityLookup.getTopologySync()) {
112
+ let route = this.getRouteFull({ path, spec: authority.authoritySpec });
113
+ routes.push(route);
114
+ }
115
+ return routes;
116
+ }
117
+
87
118
  private static lastKeyRoute = {
88
119
  key: "",
89
120
  route: -1,
@@ -91,6 +122,12 @@ export class PathRouter {
91
122
  // Takes a key which is a part of a path. Mostly used PathRouterRouteOverride, or other PathRouter helpers.
92
123
  public static getSingleKeyRoute(key: string): number {
93
124
  if (key && this.lastKeyRoute.key === key) return this.lastKeyRoute.route;
125
+ let override = getRoutingOverride(key);
126
+ if (override) {
127
+ this.lastKeyRoute.key = key;
128
+ this.lastKeyRoute.route = override.route;
129
+ return override.route;
130
+ }
94
131
  let hash = fastHash(key);
95
132
  let route = hash % (1000 * 1000 * 1000) / (1000 * 1000 * 1000);
96
133
  this.lastKeyRoute.key = key;
@@ -112,7 +149,15 @@ export class PathRouter {
112
149
  private static encodeIdentifier(config: { prefixes: string[]; rangeStart: number; rangeEnd: number } | "remaining"): string {
113
150
  if (config === "remaining") return "P!REMAINING";
114
151
  let { prefixes, rangeStart, rangeEnd } = config;
115
- return ["P", rangeStart.toString(), rangeEnd.toString(), ...prefixes.map(x => this.getPrefixHash(x)), ""].join("!");
152
+ return [
153
+ "P",
154
+ rangeStart.toString(),
155
+ rangeEnd.toString(),
156
+ ...prefixes.map(x => this.getPrefixHash(x)),
157
+ // Add some prefixes. They'll be interpreted as hashes, but it's fine because it's highly unlikely they'll collide with a real hash. AND... It's very useful for debugging what's in which file.
158
+ ...prefixes.slice(0, 5).map(x => getLastPathPart(x).replaceAll(/[^a-zA-Z0-9]/g, "").slice(0, 20)),
159
+ ""
160
+ ].join("!");
116
161
 
117
162
  }
118
163
  private static decodeIdentifier(identifier: string): { prefixHashes: string[]; rangeStart: number; rangeEnd: number } | "remaining" {
@@ -141,19 +186,12 @@ export class PathRouter {
141
186
  }
142
187
 
143
188
  let prefixes = ourSpec.prefixes.slice();
144
- sort(prefixes, x => x.prefix.length);
145
- function getPrefix(path: string): string | undefined {
146
- for (let prefix of prefixes) {
147
- if (path.startsWith(prefix.prefix) && prefix.prefix !== path) return prefix.prefix;
148
- }
149
- return undefined;
150
- }
151
-
189
+ sort(prefixes, x => x.length);
152
190
 
153
191
  // NOTE: If there are few enough path values for a prefix, we don't even need to calculate the routing hash.
154
192
  let byPrefix = new Map<string | undefined, PathValue[]>();
155
193
  for (let value of values) {
156
- let prefix = getPrefix(value.path);
194
+ let prefix = getMatchingPrefix(ourSpec, value.path);
157
195
  let values = byPrefix.get(prefix);
158
196
  if (!values) {
159
197
  values = [];
@@ -168,9 +206,6 @@ export class PathRouter {
168
206
  }));
169
207
  sort(prefixGroups, x => -x.values.length);
170
208
 
171
- require("debugbreak")(2);
172
- debugger;
173
-
174
209
  let groups: {
175
210
  prefixes: string[];
176
211
  values: PathValue[][];
@@ -261,7 +296,7 @@ export class PathRouter {
261
296
  // Match all remaining, as we don't store enough information to know what prefixes they excluded
262
297
  if (decodeObj === "remaining") return true;
263
298
 
264
- let ourHashes = authority.prefixes.map(x => this.getPrefixHash(x.prefix));
299
+ let ourHashes = authority.prefixes.map(x => this.getPrefixHash(x));
265
300
  // If it pulled off some values as prefixes, but we did full path hashes, then the hashes are going to be totally different. And so there could easily be overlap.
266
301
  if (decodeObj.prefixHashes.some(x => !ourHashes.includes(x))) return true;
267
302
  // However, if we hash everything the same, then the overlap is purely a case of if the route ranges overlap.
@@ -283,11 +318,12 @@ export class PathRouter {
283
318
 
284
319
 
285
320
  // NOTE: This might return overlapping specs. Presently, this is only used when we're loading our initial data, so it's fine. However, if we use this for another purpose in the future, it might cause problems. So we might need to implement a different function for that theoretical future purpose.
321
+ // NOTE: Only takes remote nodes as presently this is just used during startup.
286
322
  @measureFnc
287
323
  public static getAuthoritySources(config: {
288
324
  target: AuthoritySpec;
289
325
  preferredNodeIds?: string[];
290
- }): (AuthoritySpec & { useFullPathHash?: boolean })[] {
326
+ }): AuthoritySpec[] {
291
327
  let { target } = config;
292
328
  let allSources = authorityLookup.getTopologySync();
293
329
  allSources = allSources.filter(x => !isOwnNodeId(x.nodeId));
@@ -305,12 +341,12 @@ export class PathRouter {
305
341
 
306
342
  let groupByPrefixHash = new Map<string, AuthoritySpec[]>();
307
343
  for (let source of allSources) {
308
- let usedPrefixes = source.authoritySpec.prefixes.map(x => x.prefix);
344
+ let usedPrefixes = source.authoritySpec.prefixes;
309
345
 
310
346
  if (target.excludeDefault) {
311
347
  // This relaxes the restrictions, as with no default hashes, it means if a value isn't in one of our prefixes, even if it's inconsistent in the sources, it's fine.
312
348
  // (And... Otherwise, we can't filter these at all because otherwise the default/remaining group will be different, even if it's a superset/subset relationship)
313
- let targetUsedPrefixes = new Set(target.prefixes.map(x => x.prefix));
349
+ let targetUsedPrefixes = new Set(target.prefixes);
314
350
  usedPrefixes = usedPrefixes.filter(x => targetUsedPrefixes.has(x));
315
351
  }
316
352
  let prefixHash = sha256(JSON.stringify(usedPrefixes.sort()));
@@ -329,17 +365,17 @@ export class PathRouter {
329
365
  }
330
366
 
331
367
  function hashesSameAsTarget(spec: AuthoritySpec): boolean {
332
- let targetUsedPrefixes = new Set(target.prefixes.map(x => x.prefix));
368
+ let targetUsedPrefixes = new Set(target.prefixes);
333
369
  if (target.excludeDefault) {
334
370
  // If target is excluding default, then it doesn't matter if the spec is including it. We're only looking for matching values in target.
335
- return spec.prefixes.every(x => targetUsedPrefixes.has(x.prefix));
371
+ return spec.prefixes.every(x => targetUsedPrefixes.has(x));
336
372
  }
337
373
  // if !targetExcludeDefault, then !spec.excludeDefault, because we filtered out other cases earlier (so we don't need to handle spec.
338
374
 
339
375
  if (spec.prefixes.length !== target.prefixes.length) return false;
340
376
  // Otherwise, All of the prefixes have to be identical
341
377
  for (let prefix of spec.prefixes) {
342
- if (!targetUsedPrefixes.has(prefix.prefix)) return false;
378
+ if (!targetUsedPrefixes.has(prefix)) return false;
343
379
  }
344
380
  return true;
345
381
  }
@@ -368,41 +404,21 @@ export class PathRouter {
368
404
  for (let source of group) {
369
405
  let s = source.routeStart;
370
406
  let e = source.routeEnd;
371
- // Find if it overlaps any missing range, and if so, cut out that part of the missing change, and add the nodeId to usedNodeIds
372
- for (let i = missingRanges.length - 1; i >= 0; i--) {
373
- let missingRange = missingRanges[i];
374
- if (s >= missingRange.end || e <= missingRange.start) continue;
375
- let startTaken = Math.max(missingRange.start, s);
376
- let endTaken = Math.min(missingRange.end, e);
377
- // NOTE: It's not ideal that we might have to fragment one node ID between multiple requests. However, in practice, there shouldn't be much fragmentation here. The ranges that our nodes are breaking down by should be consistent, so there's actually no overlap or subsets.
407
+ let { removedRanges } = removeRange(missingRanges, { start: s, end: e });
408
+ for (let removedRange of removedRanges) {
378
409
  usedParts.push({
379
410
  nodeId: source.nodeId,
380
- routeStart: startTaken,
381
- routeEnd: endTaken,
411
+ routeStart: removedRange.start,
412
+ routeEnd: removedRange.end,
382
413
  prefixes: target.prefixes,
383
414
  excludeDefault: target.excludeDefault,
384
- useFullPathHash,
385
415
  });
386
- missingRanges.splice(i, 1);
387
- // Add back the parts we didn't overlap
388
- if (missingRange.start < s) {
389
- missingRanges.push({
390
- start: missingRange.start,
391
- end: s,
392
- });
393
- }
394
- if (missingRange.end > e) {
395
- missingRanges.push({
396
- start: e,
397
- end: missingRange.end,
398
- });
399
- }
400
416
  }
401
417
  if (missingRanges.length === 0) break;
402
418
  }
403
419
  if (missingRanges.length === 0) {
404
420
  if (!hashesSameAsTarget(group[0])) {
405
- console.warn(`Found a cohesive group that doesn't hash the same way we do. Expanding our range to be the full range of the group. This will be slower than it needs to.`);
421
+ console.warn(`Could only match a cohesive group that doesn't hash the same way we do. Expanding our range to be the full range of the group. This will be slower than it needs to, and might cause issues that result in only partial synchronization. This should be fixable with a deploy.`, config.target);
406
422
  }
407
423
  return usedParts;
408
424
  }
@@ -418,9 +434,6 @@ export class PathRouter {
418
434
  public static getChildReadNodes(path: string, config?: {
419
435
  preferredNodeIds?: string[];
420
436
  }): {
421
- // By default we hash the key directly under the path. However, If that is not how our nodes were sharded, as in they didn't use it as a prefix, then they will be sharded by hashing the full path, and so when we receive the data, we need to keep this in mind and filter the data based on the full path hash.
422
- useFullPathHash?: boolean;
423
-
424
437
  // NOTE: If at all possible, we will cover all ranges. Node of the returned nodes will be redundant.
425
438
  // - Sorted by range.start
426
439
  nodes: {
@@ -433,20 +446,66 @@ export class PathRouter {
433
446
  if (this.isSelfAuthority(path)) {
434
447
  return { nodes: [{ nodeId: getOwnNodeId(), range: { start: 0, end: 1 } }] };
435
448
  }
449
+ let preferredNodeIds = new Set(config?.preferredNodeIds ?? []);
436
450
 
437
451
  // If a prefix is a parent of path, then it is the same as matching just the path directly
438
452
  // (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).
439
453
  // - 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.
440
454
  let allSources = authorityLookup.getTopologySync();
441
- allSources = allSources.filter(x => !isOwnNodeId(x.nodeId));
442
- let nestedMatches = allSources.filter(x =>
443
- x.authoritySpec.prefixes.some(y => path.startsWith(y.prefix) && y.prefix !== path)
444
- && this.matchesAuthoritySpec(x.authoritySpec, path)
445
- );
455
+ // Prefer our own node
456
+ sort(allSources, x => isOwnNodeId(x.nodeId) ? -1 : 1);
457
+
458
+
459
+ // Direct prefixes always take priority, as almost everything is under a prefix anyways...
460
+
461
+
462
+ // 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
463
+ let hasPrefix = allSources.filter(x => x.authoritySpec.prefixes.some(y => y === path)).map(x => x.authoritySpec);
464
+ if (hasPrefix.length > 0) {
465
+ shuffle(hasPrefix, Math.random());
466
+ sort(hasPrefix, x => preferredNodeIds.has(x.nodeId) ? -1 : 1);
467
+
468
+ let missingRanges: { start: number; end: number }[] = [{
469
+ start: 0,
470
+ end: 1,
471
+ }];
472
+ let usedParts: {
473
+ nodeId: string;
474
+ range: { start: number; end: number };
475
+ }[] = [];
476
+ for (let source of hasPrefix) {
477
+ let s = source.routeStart;
478
+ let e = source.routeEnd;
479
+ let { removedRanges } = removeRange(missingRanges, { start: s, end: e });
480
+ // NOTE: It's not ideal that we might have to fragment one node ID between multiple requests. However, in practice, there shouldn't be much fragmentation here. The ranges that our nodes are breaking down by should be consistent, so there's actually no overlap or subsets.
481
+ for (let removedRange of removedRanges) {
482
+ usedParts.push({
483
+ nodeId: source.nodeId,
484
+ range: removedRange,
485
+ });
486
+ }
487
+ if (missingRanges.length === 0) break;
488
+ }
489
+ if (missingRanges.length === 0) {
490
+ return { nodes: usedParts };
491
+ }
492
+ }
493
+
494
+ let nestedMatches = allSources.filter(x => {
495
+ // 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.
496
+ // - 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.
497
+ if (x.authoritySpec.prefixes.some(y => y === path)) return false;
498
+
499
+ // 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.
500
+ return (
501
+ x.authoritySpec.prefixes.some(y => path.startsWith(y) && y !== path)
502
+ && this.matchesAuthoritySpec(x.authoritySpec, path)
503
+ );
504
+ });
446
505
  if (nestedMatches.length > 0) {
447
506
  shuffle(nestedMatches, Math.random());
448
- let preferredNodeIds = new Set(config?.preferredNodeIds ?? []);
449
507
  sort(nestedMatches, x => preferredNodeIds.has(x.nodeId) ? -1 : 1);
508
+ sort(allSources, x => isOwnNodeId(x.nodeId) ? -1 : 1);
450
509
  return {
451
510
  nodes: nestedMatches.map(x => ({
452
511
  nodeId: x.nodeId,
@@ -456,28 +515,53 @@ export class PathRouter {
456
515
  };
457
516
  }
458
517
 
459
- let fullSources = this.getAuthoritySources({
460
- target: {
461
- nodeId: "",
462
- prefixes: [{
463
- prefix: path,
464
- hashIndex: getPathDepth(path),
465
- }],
466
- routeStart: 0,
467
- routeEnd: 1,
468
- excludeDefault: true,
469
- },
470
- preferredNodeIds: config?.preferredNodeIds,
518
+ // If we are not under any prefixes of it, then it will be a full path hash
519
+ let fullPathMatches = allSources.filter(x => {
520
+ return !x.authoritySpec.prefixes.some(y => path.startsWith(y) && y !== path);
471
521
  });
472
- if (fullSources.length === 0) return { nodes: [] };
522
+ // Same as prefix matches. Not preferred, and not preferred over being under a prefix, but required for some root data, or data with no prefixes.
523
+ if (fullPathMatches.length > 0) {
524
+ shuffle(fullPathMatches, Math.random());
525
+ sort(fullPathMatches, x => preferredNodeIds.has(x.nodeId) ? -1 : 1);
526
+ sort(allSources, x => isOwnNodeId(x.nodeId) ? -1 : 1);
527
+ let missingRanges: { start: number; end: number }[] = [{
528
+ start: 0,
529
+ end: 1,
530
+ }];
531
+ let usedParts: {
532
+ nodeId: string;
533
+ range: { start: number; end: number };
534
+ }[] = [];
535
+ for (let source of fullPathMatches) {
536
+ let s = source.authoritySpec.routeStart;
537
+ let e = source.authoritySpec.routeEnd;
538
+ let { removedRanges } = removeRange(missingRanges, { start: s, end: e });
539
+ // NOTE: It's not ideal that we might have to fragment one node ID between multiple requests. However, in practice, there shouldn't be much fragmentation here. The ranges that our nodes are breaking down by should be consistent, so there's actually no overlap or subsets.
540
+ for (let removedRange of removedRanges) {
541
+ usedParts.push({
542
+ nodeId: source.nodeId,
543
+ range: removedRange,
544
+ });
545
+ }
546
+ if (missingRanges.length === 0) break;
547
+ }
548
+ if (missingRanges.length === 0) {
549
+ return { nodes: usedParts };
550
+ }
551
+ }
473
552
 
474
- return {
475
- useFullPathHash: fullSources.some(x => x.useFullPathHash),
476
- nodes: fullSources.map(x => ({
477
- nodeId: x.nodeId,
478
- range: { start: x.routeStart, end: x.routeEnd },
479
- })),
480
- };
553
+
554
+
555
+ // 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...
556
+
557
+
558
+ require("debugbreak")(2);
559
+ debugger;
560
+
561
+
562
+ // 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.
563
+ 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 });
564
+ return { nodes: [] };
481
565
  }
482
566
 
483
567
 
@@ -541,3 +625,5 @@ export class PathRouter {
541
625
 
542
626
  }
543
627
 
628
+
629
+ (globalThis as any).PathRouter = PathRouter;
@@ -22,7 +22,7 @@ let keySpecialIdentifier = (
22
22
  export function createRoutingOverrideKey(config: {
23
23
  originalKey: string;
24
24
  routeKey: string;
25
- // This is not the prefix that it has to match. This is the prefix we remap it to. And so anything which matches this remap prefix will match this routing key.
25
+ // This is the prefix it has the equivalent of. We need this, so if something excludes default, it doesn't automatically get every routing overridden value.
26
26
  remappedPrefix: string;
27
27
  }) {
28
28
  let { originalKey, routeKey, remappedPrefix } = config;
@@ -62,7 +62,7 @@ export const hasPrefixHash = cacheLimited(1000 * 10,
62
62
  (config: { spec: AuthoritySpec, prefixHash: string }) => {
63
63
  let { spec, prefixHash } = config;
64
64
  for (let prefix of spec.prefixes) {
65
- if (getPrefixHash(prefix.prefix) === prefixHash) {
65
+ if (getPrefixHash(prefix) === prefixHash) {
66
66
  return true;
67
67
  }
68
68
  }
@@ -20,10 +20,7 @@ export async function getOurAuthoritySpec(defaultToAll?: boolean): Promise<Autho
20
20
  nodeId: "",
21
21
  routeStart: 0,
22
22
  routeEnd: 1,
23
- prefixes: prefixes.map(prefix => ({
24
- prefix,
25
- hashIndex: getPathDepth(prefix),
26
- })),
23
+ prefixes: prefixes,
27
24
  };
28
25
  }
29
26
  return undefined;
@@ -46,10 +43,7 @@ export async function getOurAuthoritySpec(defaultToAll?: boolean): Promise<Autho
46
43
  nodeId: "",
47
44
  routeStart: rangeStart,
48
45
  routeEnd: rangeEnd,
49
- prefixes: usePrefixes.map(prefix => ({
50
- prefix,
51
- hashIndex: getPathDepth(prefix),
52
- })),
46
+ prefixes: usePrefixes,
53
47
  excludeDefault: excludeDefault || undefined,
54
48
  };
55
49
  }