querysub 0.504.0 → 0.506.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/bin/function.js +3 -3
- package/bin/server-dev.js +11 -0
- package/bin/server.js +2 -0
- package/package.json +2 -1
- package/src/0-path-value-core/AuthorityLookup.ts +1 -0
- package/src/0-path-value-core/PathRouter.ts +27 -4
- package/src/0-path-value-core/PathRouterRouteOverride.ts +98 -18
- package/src/0-path-value-core/PathRouterServerAuthoritySpec.tsx +4 -1
- package/src/0-path-value-core/PathValueCommitter.ts +6 -1
- package/src/0-path-value-core/PathValueController.ts +7 -5
- package/src/0-path-value-core/PathWatcher.ts +11 -8
- package/src/0-path-value-core/hackedPackedPathParentFiltering.ts +24 -10
- package/src/0-path-value-core/pathValueCore.ts +15 -7
- package/src/1-path-client/RemoteWatcher.ts +35 -18
- package/src/3-path-functions/PathFunctionHelpers.ts +14 -15
- package/src/3-path-functions/PathFunctionRunner.ts +18 -12
- package/src/3-path-functions/PathFunctionRunnerMain.ts +5 -7
- package/src/3-path-functions/syncSchema.ts +3 -1
- package/src/4-querysub/Querysub.ts +14 -2
- package/src/4-querysub/QuerysubController.ts +13 -6
- package/src/config.ts +39 -3
- package/src/config2.ts +2 -8
- package/src/diagnostics/logs/errorTickets/TicketPage.tsx +4 -3
- package/src/diagnostics/managementPages.tsx +1 -1
- package/src/diagnostics/misc-pages/AuthoritySpecPage.tsx +30 -5
- package/src/misc/filterable.ts +0 -395
package/bin/function.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
process.argv.push("--local");
|
|
4
|
-
// Only dev. If we listen to all
|
|
5
|
-
process.argv.push("--
|
|
6
|
-
process.argv.push("
|
|
4
|
+
// Only the dev network. If we listen to all networks it will start picking up production function calls, which breaks things (unlike the path value server, where we are unreachable, so nothing breaks).
|
|
5
|
+
process.argv.push("--networkfile");
|
|
6
|
+
process.argv.push("~/devnetwork.txt");
|
|
7
7
|
|
|
8
8
|
require("typenode");
|
|
9
9
|
require("../src/3-path-functions/PathFunctionRunnerMain");
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
process.argv.push("--local");
|
|
4
|
+
// Serves the dev network AND the default network, so one server can satisfy paths with no network as well as paths on our dev network.
|
|
5
|
+
process.argv.push("--networkfile");
|
|
6
|
+
process.argv.push("~/devnetwork.txt");
|
|
7
|
+
process.argv.push("--network");
|
|
8
|
+
process.argv.push("default");
|
|
9
|
+
|
|
10
|
+
require("typenode");
|
|
11
|
+
require("../src/server.ts");
|
package/bin/server.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "querysub",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.506.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",
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"deploy": "./bin/deploy.js",
|
|
32
32
|
"deploy-prefixes": "./bin/deploy-prefixes.js",
|
|
33
33
|
"server": "./bin/server.js",
|
|
34
|
+
"server-dev": "./bin/server-dev.js",
|
|
34
35
|
"server-public": "./bin/server-public.js",
|
|
35
36
|
"function": "./bin/function.js",
|
|
36
37
|
"function-public": "./bin/function-public.js",
|
|
@@ -8,6 +8,7 @@ import { getOwnNodeId, isOwnNodeId } from "../-f-node-discovery/NodeDiscovery";
|
|
|
8
8
|
import { unique } from "../misc";
|
|
9
9
|
import { measureFnc } from "socket-function/src/profiling/measure";
|
|
10
10
|
import { getRoutingOverride, hasPrefixHash } from "./PathRouterRouteOverride";
|
|
11
|
+
import { DEFAULT_NETWORK } from "../config";
|
|
11
12
|
import { sha256 } from "js-sha256";
|
|
12
13
|
import { rangesOverlap, removeRange } from "../rangeMath";
|
|
13
14
|
import { decodeParentFilter } from "./hackedPackedPathParentFiltering";
|
|
@@ -37,8 +38,23 @@ export type AuthoritySpec = {
|
|
|
37
38
|
prefixes: PrefixMatcher[];
|
|
38
39
|
// - Make sure to set this if you just want the prefix values, otherwise you will get all that that prefixes don't match (the prefixes exclude prefix match but where route does not match).
|
|
39
40
|
excludeDefault?: boolean;
|
|
41
|
+
// The networks this authority satisfies. Unset is equivalent to ["default"]. Paths only route to non-default networks if their routing override key explicitly asks for that network. "all" is ONLY for client proxy specs (the querysub server the client talks to proxies to the real authorities, which do their own network routing), and is not valid for real authorities.
|
|
42
|
+
networks?: string[] | "all";
|
|
40
43
|
};
|
|
41
44
|
|
|
45
|
+
function getSpecNetworkList(networks: string[] | undefined): string[] {
|
|
46
|
+
return networks && networks.length > 0 && networks || [DEFAULT_NETWORK];
|
|
47
|
+
}
|
|
48
|
+
export function specSatisfiesNetwork(spec: AuthoritySpec, network: string | undefined): boolean {
|
|
49
|
+
if (spec.networks === "all") return true;
|
|
50
|
+
return getSpecNetworkList(spec.networks).includes(network || DEFAULT_NETWORK);
|
|
51
|
+
}
|
|
52
|
+
export function networksOverlap(spec1: AuthoritySpec, spec2: AuthoritySpec): boolean {
|
|
53
|
+
if (spec1.networks === "all" || spec2.networks === "all") return true;
|
|
54
|
+
let networks2 = getSpecNetworkList(spec2.networks);
|
|
55
|
+
return getSpecNetworkList(spec1.networks).some(x => networks2.includes(x));
|
|
56
|
+
}
|
|
57
|
+
|
|
42
58
|
// If you want to match just a simple path, use parsePrefixMatcher. It won't match ===, so get the parent if you want that (and then filter the result).
|
|
43
59
|
export type PrefixMatcher = {
|
|
44
60
|
prefix: string;
|
|
@@ -142,7 +158,8 @@ export class PathRouter {
|
|
|
142
158
|
let { path, spec } = config;
|
|
143
159
|
path = hack_stripPackedPath(path);
|
|
144
160
|
let override = getRoutingOverride(path);
|
|
145
|
-
if (override)
|
|
161
|
+
if (!specSatisfiesNetwork(spec, override?.network)) return -1;
|
|
162
|
+
if (override && override.route !== undefined) {
|
|
146
163
|
if (spec.excludeDefault && !hasPrefixHash({ spec, prefixHash: override.prefixHash })) return -1;
|
|
147
164
|
if (override.route < spec.routeStart || override.route >= spec.routeEnd) return -1;
|
|
148
165
|
return override.route;
|
|
@@ -185,7 +202,7 @@ export class PathRouter {
|
|
|
185
202
|
public static getSingleKeyRoute(key: string): number {
|
|
186
203
|
if (key && this.lastKeyRoute.key === key) return this.lastKeyRoute.route;
|
|
187
204
|
let override = getRoutingOverride(key);
|
|
188
|
-
if (override) {
|
|
205
|
+
if (override && override.route !== undefined) {
|
|
189
206
|
this.lastKeyRoute.key = key;
|
|
190
207
|
this.lastKeyRoute.route = override.route;
|
|
191
208
|
return override.route;
|
|
@@ -331,6 +348,7 @@ export class PathRouter {
|
|
|
331
348
|
prefixes: group.prefixes,
|
|
332
349
|
routeStart: 0,
|
|
333
350
|
routeEnd: 1,
|
|
351
|
+
networks: ourSpec.networks,
|
|
334
352
|
}
|
|
335
353
|
});
|
|
336
354
|
let routeIndex = Math.floor(route * splitCount);
|
|
@@ -374,6 +392,7 @@ export class PathRouter {
|
|
|
374
392
|
|
|
375
393
|
@measureFnc
|
|
376
394
|
public static overlapsAuthority(authority1: AuthoritySpec, authority2: AuthoritySpec): boolean {
|
|
395
|
+
if (!networksOverlap(authority1, authority2)) return false;
|
|
377
396
|
// TODO: This becomes complicated because of exclude default, although I feel like there has to be a way to simplify it? Eh... whatever.
|
|
378
397
|
|
|
379
398
|
let doRangesOverlap = rangesOverlap({ start: authority1.routeStart, end: authority1.routeEnd }, { start: authority2.routeStart, end: authority2.routeEnd });
|
|
@@ -438,6 +457,7 @@ export class PathRouter {
|
|
|
438
457
|
let { target } = config;
|
|
439
458
|
let allSources = authorityLookup.getTopologySync();
|
|
440
459
|
allSources = allSources.filter(x => !isOwnNodeId(x.nodeId));
|
|
460
|
+
allSources = allSources.filter(x => networksOverlap(x.authoritySpec, target));
|
|
441
461
|
// THIS is normal during initial server startup
|
|
442
462
|
if (!allSources.length) {
|
|
443
463
|
return [];
|
|
@@ -557,10 +577,12 @@ export class PathRouter {
|
|
|
557
577
|
range: { start: number; end: number };
|
|
558
578
|
}[];
|
|
559
579
|
} {
|
|
560
|
-
let
|
|
580
|
+
let parentFilter = decodeParentFilter(path);
|
|
581
|
+
let parentRange = parentFilter || {
|
|
561
582
|
start: 0,
|
|
562
583
|
end: 1,
|
|
563
584
|
};
|
|
585
|
+
let parentNetwork = parentFilter?.network;
|
|
564
586
|
path = hack_stripPackedPath(path);
|
|
565
587
|
let preferredNodeIds = new Set(config?.preferredNodeIds ?? []);
|
|
566
588
|
|
|
@@ -569,6 +591,7 @@ export class PathRouter {
|
|
|
569
591
|
// - 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.
|
|
570
592
|
// 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.
|
|
571
593
|
let allSources = config?.onlyOwnNodes ? [{ nodeId: getOwnNodeId(), authoritySpec: authorityLookup.getOurSpec() }] : authorityLookup.getTopologySync();
|
|
594
|
+
allSources = allSources.filter(x => specSatisfiesNetwork(x.authoritySpec, parentNetwork));
|
|
572
595
|
// Prefer our own node
|
|
573
596
|
sort(allSources, x => isOwnNodeId(x.nodeId) ? -1 : 1);
|
|
574
597
|
|
|
@@ -674,7 +697,7 @@ export class PathRouter {
|
|
|
674
697
|
if (!config?.onlyOwnNodes) {
|
|
675
698
|
|
|
676
699
|
// 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.
|
|
677
|
-
console.error(`Want to sync a prefix which is not under an existing prefix, nor equal to a prefix. 1) The servers are down. 1.5) The servers are in an inconsistent state (one shard range added a parent path, the other didn't). 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) });
|
|
700
|
+
console.error(`Want to sync a prefix which is not under an existing prefix, nor equal to a prefix. 1) The servers are down. 1.5) The servers are in an inconsistent state (one shard range added a parent path, the other didn't). 2) Don't access the .keys() 3) call addRoutingPrefixForDeploy to add a route/parent route explicitly (as is done in PathFunctionRunner.ts). 4) No server is on the network (only servers on the network can satisfy it). Path: ${JSON.stringify(path)}, network: ${parentNetwork || DEFAULT_NETWORK}`, { path, network: parentNetwork || DEFAULT_NETWORK, allSources: allSources.map(x => x.authoritySpec) });
|
|
678
701
|
}
|
|
679
702
|
return { nodes: [] };
|
|
680
703
|
}
|
|
@@ -2,13 +2,12 @@ import { cacheLimited } from "socket-function/src/caching";
|
|
|
2
2
|
import { AuthoritySpec, PathRouter } from "./PathRouter";
|
|
3
3
|
import { sha256 } from "js-sha256";
|
|
4
4
|
import { getPathFromStr } from "../path";
|
|
5
|
+
import { DEFAULT_NETWORK } from "../config";
|
|
5
6
|
|
|
6
7
|
function getPrefixHash(prefix: string): string {
|
|
7
8
|
return Buffer.from(sha256(prefix), "hex").toString("base64").slice(0, 12);
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
// So... should be use unicode characters?
|
|
11
|
-
// - Both to ensure all of our code supports them in keys, and because it allows us to us a lot fewer characters.
|
|
12
11
|
// NOTE: If we want, we could add code to prevent users from creating keys that use these. However, I think it's fine, because the querysub server is still the first node you talk to, so it can throttle your traffic. ALSO, ideally all of your traffic DOES go to the same PathValueServer. That's why prefix routing exists. And PathValueServers should be able to handle A LOT of traffic.
|
|
13
12
|
let keySpecialIdentifier = (
|
|
14
13
|
"ROUTE_"
|
|
@@ -18,22 +17,102 @@ let keySpecialIdentifier = (
|
|
|
18
17
|
+ String.fromCharCode(0xF4D7)
|
|
19
18
|
);
|
|
20
19
|
|
|
21
|
-
|
|
20
|
+
export function assertValidNetwork(network: string) {
|
|
21
|
+
if (!/^[a-z]+$/.test(network)) {
|
|
22
|
+
throw new Error(`Networks must be lowercase letters (a-z) only, was ${JSON.stringify(network)}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// The route slot is either a route number ("0.12345"), a network ("dev"), or both ("dev:0.12345"). Networks are strictly lowercase letters and routes are strictly numeric, so the forms are unambiguous.
|
|
27
|
+
function encodeRouteSlot(config: { route?: number; network?: string }): string {
|
|
28
|
+
let { route, network } = config;
|
|
29
|
+
let routeStr = route !== undefined && route.toString().slice(0, 7) || "";
|
|
30
|
+
if (network && routeStr) return `${network}:${routeStr}`;
|
|
31
|
+
if (network) return network;
|
|
32
|
+
return routeStr;
|
|
33
|
+
}
|
|
34
|
+
function decodeRouteSlot(slot: string): { route?: number; network?: string } | undefined {
|
|
35
|
+
if (!slot) return undefined;
|
|
36
|
+
let network: string | undefined;
|
|
37
|
+
let routeStr = slot;
|
|
38
|
+
let colonIndex = slot.indexOf(":");
|
|
39
|
+
if (colonIndex >= 0) {
|
|
40
|
+
network = slot.slice(0, colonIndex);
|
|
41
|
+
routeStr = slot.slice(colonIndex + 1);
|
|
42
|
+
} else if (/^[a-z]+$/.test(slot)) {
|
|
43
|
+
return { network: slot };
|
|
44
|
+
}
|
|
45
|
+
if (network && !/^[a-z]+$/.test(network)) return undefined;
|
|
46
|
+
let route = parseFloat(routeStr);
|
|
47
|
+
if (isNaN(route)) return undefined;
|
|
48
|
+
return { route, network };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function createRoutingOverrideKeyBase(config: {
|
|
52
|
+
originalKey: string;
|
|
53
|
+
// Hashed to get the route, if route is not directly provided
|
|
54
|
+
routeKey?: string;
|
|
55
|
+
route?: number;
|
|
56
|
+
network?: string;
|
|
57
|
+
// 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. Required whenever a route (or routeKey) is set.
|
|
58
|
+
remappedPrefix?: string;
|
|
59
|
+
}) {
|
|
60
|
+
let { originalKey, routeKey, route, network, remappedPrefix } = config;
|
|
61
|
+
if (network) {
|
|
62
|
+
assertValidNetwork(network);
|
|
63
|
+
}
|
|
64
|
+
if (route === undefined && routeKey !== undefined) {
|
|
65
|
+
route = PathRouter.getSingleKeyRoute(routeKey);
|
|
66
|
+
}
|
|
67
|
+
if (route === undefined && !network) {
|
|
68
|
+
throw new Error(`createRoutingOverrideKeyBase requires at least one of route, routeKey, or network`);
|
|
69
|
+
}
|
|
70
|
+
if (route !== undefined && !remappedPrefix) {
|
|
71
|
+
throw new Error(`createRoutingOverrideKeyBase requires remappedPrefix when a route is set, so excludeDefault authorities can match it correctly`);
|
|
72
|
+
}
|
|
73
|
+
let prefixHash = remappedPrefix && getPrefixHash(remappedPrefix) || "";
|
|
74
|
+
return keySpecialIdentifier + "!" + prefixHash + "!" + encodeRouteSlot({ route, network }) + "!" + originalKey;
|
|
75
|
+
}
|
|
76
|
+
|
|
22
77
|
export function createRoutingOverrideKey(config: {
|
|
23
78
|
originalKey: string;
|
|
24
79
|
routeKey: string;
|
|
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
80
|
remappedPrefix: string;
|
|
81
|
+
network?: string;
|
|
82
|
+
}) {
|
|
83
|
+
return createRoutingOverrideKeyBase(config);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Creates a key that routes to a specific network, but otherwise routes normally (via prefix / full path hashing). */
|
|
87
|
+
export function createNetworkKey(config: {
|
|
88
|
+
originalKey: string;
|
|
89
|
+
network: string;
|
|
27
90
|
}) {
|
|
28
|
-
|
|
29
|
-
let route = PathRouter.getSingleKeyRoute(routeKey);
|
|
30
|
-
return keySpecialIdentifier + "!" + getPrefixHash(remappedPrefix) + "!" + route.toString().slice(0, 7) + "!" + originalKey;
|
|
91
|
+
return createRoutingOverrideKeyBase({ originalKey: config.originalKey, network: config.network });
|
|
31
92
|
}
|
|
32
93
|
|
|
33
|
-
|
|
34
|
-
|
|
94
|
+
/** Rewrites the routing override part inside key (which must contain one) to be on the given network. */
|
|
95
|
+
export function setRoutingOverrideKeyNetwork(key: string, network: string): string {
|
|
96
|
+
assertValidNetwork(network);
|
|
97
|
+
if (!key.startsWith(keySpecialIdentifier)) {
|
|
98
|
+
return createNetworkKey({ originalKey: key, network });
|
|
99
|
+
}
|
|
100
|
+
let parts = key.split("!");
|
|
101
|
+
if (parts.length < 4) {
|
|
102
|
+
return createNetworkKey({ originalKey: key, network });
|
|
103
|
+
}
|
|
104
|
+
let slot = decodeRouteSlot(parts[2]) || {};
|
|
105
|
+
parts[2] = encodeRouteSlot({ route: slot.route, network });
|
|
106
|
+
return parts.join("!");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export type RoutingOverride = {
|
|
35
110
|
prefixHash: string;
|
|
36
|
-
|
|
111
|
+
route?: number;
|
|
112
|
+
network?: string;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export function getRoutingOverride(path: string): RoutingOverride | undefined {
|
|
37
116
|
if (!path.includes(keySpecialIdentifier)) return undefined;
|
|
38
117
|
let parts = getPathFromStr(path);
|
|
39
118
|
for (let part of parts) {
|
|
@@ -42,22 +121,24 @@ export function getRoutingOverride(path: string): {
|
|
|
42
121
|
}
|
|
43
122
|
return undefined;
|
|
44
123
|
}
|
|
45
|
-
export function getRoutingOverridePart(part: string): {
|
|
46
|
-
prefixHash: string;
|
|
47
|
-
route: number;
|
|
48
|
-
} | undefined {
|
|
124
|
+
export function getRoutingOverridePart(part: string): RoutingOverride | undefined {
|
|
49
125
|
if (!part.startsWith(keySpecialIdentifier)) return undefined;
|
|
50
126
|
let parts = part.split("!");
|
|
51
127
|
if (parts.length < 4) return undefined;
|
|
52
128
|
let prefixHash = parts[1];
|
|
53
|
-
let
|
|
54
|
-
if (
|
|
129
|
+
let slot = decodeRouteSlot(parts[2]);
|
|
130
|
+
if (!slot) return undefined;
|
|
55
131
|
return {
|
|
56
132
|
prefixHash,
|
|
57
|
-
route,
|
|
133
|
+
route: slot.route,
|
|
134
|
+
network: slot.network,
|
|
58
135
|
};
|
|
59
136
|
}
|
|
60
137
|
|
|
138
|
+
export function getPathNetwork(path: string): string {
|
|
139
|
+
return getRoutingOverride(path)?.network || DEFAULT_NETWORK;
|
|
140
|
+
}
|
|
141
|
+
|
|
61
142
|
export const hasPrefixHash = cacheLimited(1000 * 10,
|
|
62
143
|
(config: { spec: AuthoritySpec, prefixHash: string }) => {
|
|
63
144
|
let { spec, prefixHash } = config;
|
|
@@ -69,4 +150,3 @@ export const hasPrefixHash = cacheLimited(1000 * 10,
|
|
|
69
150
|
return false;
|
|
70
151
|
}
|
|
71
152
|
);
|
|
72
|
-
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
import { AuthoritySpec, parsePrefixMatcher } from "./PathRouter";
|
|
3
3
|
import { getPathDepth } from "../path";
|
|
4
|
-
import { getAuthorityRange, getAuthorityExcludeDefault, getAuthorityPrefix } from "../config";
|
|
4
|
+
import { getAuthorityRange, getAuthorityExcludeDefault, getAuthorityPrefix, getNetworks } from "../config";
|
|
5
5
|
import { getShardPrefixes } from "./ShardPrefixes";
|
|
6
6
|
import { getOwnNodeId } from "../-f-node-discovery/NodeDiscovery";
|
|
7
7
|
|
|
@@ -22,6 +22,7 @@ export async function getOurAuthoritySpec(defaultToAll?: boolean): Promise<Autho
|
|
|
22
22
|
routeStart: 0,
|
|
23
23
|
routeEnd: 1,
|
|
24
24
|
prefixes: prefixMatchers,
|
|
25
|
+
networks: getNetworks(),
|
|
25
26
|
};
|
|
26
27
|
}
|
|
27
28
|
return undefined;
|
|
@@ -46,6 +47,7 @@ export async function getOurAuthoritySpec(defaultToAll?: boolean): Promise<Autho
|
|
|
46
47
|
routeEnd: rangeEnd,
|
|
47
48
|
prefixes: usePrefixes,
|
|
48
49
|
excludeDefault: excludeDefault || undefined,
|
|
50
|
+
networks: getNetworks(),
|
|
49
51
|
};
|
|
50
52
|
}
|
|
51
53
|
|
|
@@ -67,5 +69,6 @@ export async function getAllAuthoritySpec(): Promise<AuthoritySpec> {
|
|
|
67
69
|
routeStart: 0,
|
|
68
70
|
routeEnd: 1,
|
|
69
71
|
prefixes: prefixMatchers,
|
|
72
|
+
networks: getNetworks(),
|
|
70
73
|
};
|
|
71
74
|
}
|
|
@@ -20,6 +20,8 @@ import { debugNodeId } from "../-c-identity/IdentityController";
|
|
|
20
20
|
import { decodeNodeId } from "sliftutils/misc/https/certs";
|
|
21
21
|
import { getDomain } from "../config";
|
|
22
22
|
import { decodeParentFilter, encodeParentFilter } from "./hackedPackedPathParentFiltering";
|
|
23
|
+
import { getPathNetwork } from "./PathRouterRouteOverride";
|
|
24
|
+
import { DEFAULT_NETWORK } from "../config";
|
|
23
25
|
import { deepCloneCborx } from "../misc/cloneHelpers";
|
|
24
26
|
import { removeRange } from "../rangeMath";
|
|
25
27
|
import { registerShutdownHandler } from "../diagnostics/periodic";
|
|
@@ -205,8 +207,9 @@ class PathValueCommitter {
|
|
|
205
207
|
parentSyncs: [],
|
|
206
208
|
initialTriggers: { values: new Set(), parentPaths: new Set() },
|
|
207
209
|
});
|
|
208
|
-
console.error(`There are no authorities for path ${pathValue.path}. The write will be lost.`, {
|
|
210
|
+
console.error(`There are no authorities for path ${pathValue.path} (network ${getPathNetwork(pathValue.path)}). The write will be lost.`, {
|
|
209
211
|
path: pathValue.path,
|
|
212
|
+
network: getPathNetwork(pathValue.path),
|
|
210
213
|
timeId: pathValue.time.time,
|
|
211
214
|
source: pathValue.source,
|
|
212
215
|
otherAuthorities,
|
|
@@ -349,6 +352,7 @@ class PathValueCommitter {
|
|
|
349
352
|
remoteWatcher.getExistingWatchRemoteNodeId(path);
|
|
350
353
|
console.warn(`Ignoring value from wrong authority. Should have been ${debugNodeId(watchingAuthorityId)}, but was received from ${debugNodeId(batch.sourceNodeId)}.`, {
|
|
351
354
|
path,
|
|
355
|
+
network: getPathNetwork(path),
|
|
352
356
|
type,
|
|
353
357
|
timeId: value?.time.time,
|
|
354
358
|
source: value?.source,
|
|
@@ -390,6 +394,7 @@ class PathValueCommitter {
|
|
|
390
394
|
|
|
391
395
|
console.warn(`Ignoring parent path which we aren't watching. From ${debugNodeId(batch.sourceNodeId)}.`, {
|
|
392
396
|
parentPath,
|
|
397
|
+
network: decodeParentFilter(parentPath)?.network || DEFAULT_NETWORK,
|
|
393
398
|
sourceNodeId: debugNodeId(batch.sourceNodeId),
|
|
394
399
|
sourceNodeThreadId: decodeNodeId(batch.sourceNodeId, getDomain())?.threadId,
|
|
395
400
|
});
|
|
@@ -9,7 +9,9 @@ import { pathValueSerializer } from "../-h-path-value-serialize/PathValueSeriali
|
|
|
9
9
|
import { pathValueCommitter } from "./PathValueCommitter";
|
|
10
10
|
import { auditLog, isDebugLogEnabled } from "./auditLogs";
|
|
11
11
|
import { debugNodeId, debugNodeThread } from "../-c-identity/IdentityController";
|
|
12
|
-
import { getSlowdown, isDiskAudit, getDomain } from "../config";
|
|
12
|
+
import { getSlowdown, isDiskAudit, getDomain, DEFAULT_NETWORK } from "../config";
|
|
13
|
+
import { getPathNetwork } from "./PathRouterRouteOverride";
|
|
14
|
+
import { decodeParentFilter } from "./hackedPackedPathParentFiltering";
|
|
13
15
|
import { decodeNodeId } from "sliftutils/misc/https/certs";
|
|
14
16
|
import { areNodeIdsEqual, isOwnNodeId } from "../-f-node-discovery/NodeDiscovery";
|
|
15
17
|
import { getNodeIdIP } from "socket-function/src/nodeCache";
|
|
@@ -233,10 +235,10 @@ export class PathValueControllerBase {
|
|
|
233
235
|
if (isDebugLogEnabled()) {
|
|
234
236
|
let sourceNodeId = debugNodeId(callerId);
|
|
235
237
|
for (let value of config.paths) {
|
|
236
|
-
auditLog("WATCH PATH", { path: value, sourceNodeId, sourceNodeThreadId: debugNodeThread(callerId) });
|
|
238
|
+
auditLog("WATCH PATH", { path: value, network: getPathNetwork(value), sourceNodeId, sourceNodeThreadId: debugNodeThread(callerId) });
|
|
237
239
|
}
|
|
238
240
|
for (let value of config.parentPaths) {
|
|
239
|
-
auditLog("WATCH PARENT PATH", { path: value, sourceNodeId });
|
|
241
|
+
auditLog("WATCH PARENT PATH", { path: value, network: decodeParentFilter(value)?.network || DEFAULT_NETWORK, sourceNodeId });
|
|
240
242
|
}
|
|
241
243
|
}
|
|
242
244
|
pathWatcher.watchPath({
|
|
@@ -252,10 +254,10 @@ export class PathValueControllerBase {
|
|
|
252
254
|
if (isDebugLogEnabled()) {
|
|
253
255
|
let sourceNodeId = debugNodeId(SocketFunction.getCaller().nodeId);
|
|
254
256
|
for (let value of config.paths) {
|
|
255
|
-
auditLog("UNWATCHING PATH", { path: value, sourceNodeId, sourceNodeThreadId: debugNodeThread(callerId) });
|
|
257
|
+
auditLog("UNWATCHING PATH", { path: value, network: getPathNetwork(value), sourceNodeId, sourceNodeThreadId: debugNodeThread(callerId) });
|
|
256
258
|
}
|
|
257
259
|
for (let value of config.parentPaths) {
|
|
258
|
-
auditLog("UNWATCHING PARENT PATH", { path: value, sourceNodeId });
|
|
260
|
+
auditLog("UNWATCHING PARENT PATH", { path: value, network: decodeParentFilter(value)?.network || DEFAULT_NETWORK, sourceNodeId });
|
|
259
261
|
}
|
|
260
262
|
}
|
|
261
263
|
pathWatcher.unwatchPath({ paths: config.paths, parentPaths: config.parentPaths, callback: callerId, reason: "PathValueController.unwatchLatest" });
|
|
@@ -11,7 +11,9 @@ import { PathValueControllerBase } from "./PathValueController";
|
|
|
11
11
|
import { PathRouter } from "./PathRouter";
|
|
12
12
|
import { auditLog } from "./auditLogs";
|
|
13
13
|
import { WatchConfig, authorityStorage, PathValue, NodeId, compareTime, isCoreQuiet, MAX_CHANGE_AGE, createMissingEpochValue } from "./pathValueCore";
|
|
14
|
-
import { matchesParentRangeFilter } from "./hackedPackedPathParentFiltering";
|
|
14
|
+
import { decodeParentFilter, matchesParentRangeFilter } from "./hackedPackedPathParentFiltering";
|
|
15
|
+
import { getPathNetwork } from "./PathRouterRouteOverride";
|
|
16
|
+
import { DEFAULT_NETWORK } from "../config";
|
|
15
17
|
import { isClient } from "../config2";
|
|
16
18
|
import { delay } from "socket-function/src/batching";
|
|
17
19
|
import { isClientNodeId } from "socket-function/src/nodeCache";
|
|
@@ -170,21 +172,22 @@ class PathWatcher {
|
|
|
170
172
|
incrementWatcherSequence();
|
|
171
173
|
if (isOwnNodeId(config.nodeId)) {
|
|
172
174
|
for (let path of newPathsWatched) {
|
|
173
|
-
auditLog("new local WATCH VALUE", { path });
|
|
175
|
+
auditLog("new local WATCH VALUE", { path, network: getPathNetwork(path) });
|
|
174
176
|
}
|
|
175
177
|
for (let path of newParentsWatched) {
|
|
176
|
-
auditLog("new local WATCH PARENT", { path });
|
|
178
|
+
auditLog("new local WATCH PARENT", { path, network: decodeParentFilter(path)?.network || DEFAULT_NETWORK });
|
|
177
179
|
}
|
|
178
180
|
} else {
|
|
179
181
|
for (let path of newPathsWatched) {
|
|
180
182
|
auditLog("new non-local WATCH VALUE", {
|
|
181
183
|
path,
|
|
184
|
+
network: getPathNetwork(path),
|
|
182
185
|
watcher: config.nodeId,
|
|
183
186
|
sourceNodeThreadId: debugNodeThread(config.nodeId),
|
|
184
187
|
});
|
|
185
188
|
}
|
|
186
189
|
for (let path of newParentsWatched) {
|
|
187
|
-
auditLog("new non-local WATCH PARENT", { path, watcher: config.nodeId });
|
|
190
|
+
auditLog("new non-local WATCH PARENT", { path, network: decodeParentFilter(path)?.network || DEFAULT_NETWORK, watcher: config.nodeId });
|
|
188
191
|
}
|
|
189
192
|
}
|
|
190
193
|
console.info(`New PathValue watches`, {
|
|
@@ -244,9 +247,9 @@ class PathWatcher {
|
|
|
244
247
|
obj.watchers.delete(callback);
|
|
245
248
|
|
|
246
249
|
if (isOwnNodeId(callback)) {
|
|
247
|
-
auditLog("local UNWATCH PARENT", { path, reason });
|
|
250
|
+
auditLog("local UNWATCH PARENT", { path, network: decodeParentFilter(path)?.network || DEFAULT_NETWORK, reason });
|
|
248
251
|
} else {
|
|
249
|
-
auditLog("non-local UNWATCH PARENT", { path, watcher: callback, remoteNodeId: debugNodeId(callback), remoteNodeThreadId: debugNodeThread(callback), reason });
|
|
252
|
+
auditLog("non-local UNWATCH PARENT", { path, network: decodeParentFilter(path)?.network || DEFAULT_NETWORK, watcher: callback, remoteNodeId: debugNodeId(callback), remoteNodeThreadId: debugNodeThread(callback), reason });
|
|
250
253
|
}
|
|
251
254
|
|
|
252
255
|
if (obj.watchers.size === 0) {
|
|
@@ -281,9 +284,9 @@ class PathWatcher {
|
|
|
281
284
|
watchers.watchers.delete(callback);
|
|
282
285
|
|
|
283
286
|
if (isOwnNodeId(callback)) {
|
|
284
|
-
auditLog("local UNWATCH VALUE", { path, reason });
|
|
287
|
+
auditLog("local UNWATCH VALUE", { path, network: getPathNetwork(path), reason });
|
|
285
288
|
} else {
|
|
286
|
-
auditLog("non-local UNWATCH VALUE", { path, watcher: callback, reason });
|
|
289
|
+
auditLog("non-local UNWATCH VALUE", { path, network: getPathNetwork(path), watcher: callback, reason });
|
|
287
290
|
}
|
|
288
291
|
|
|
289
292
|
if (watchers.watchers.size === 0) {
|
|
@@ -3,6 +3,8 @@ import { getPathDepth, getPathIndexAssert, hack_getPackedPathSuffix, hack_setPac
|
|
|
3
3
|
import { AuthoritySpec, PathRouter } from "./PathRouter";
|
|
4
4
|
import { cache } from "socket-function/src/caching";
|
|
5
5
|
import { authorityLookup } from "./AuthorityLookup";
|
|
6
|
+
import { assertValidNetwork, getPathNetwork } from "./PathRouterRouteOverride";
|
|
7
|
+
import { DEFAULT_NETWORK } from "../config";
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
let getSpecForChildPath = (path: string) => authorityLookup.getOurSpec();
|
|
@@ -15,10 +17,15 @@ export function matchesParentRangeFilter(config: {
|
|
|
15
17
|
fullPath: string;
|
|
16
18
|
packedPath: string;
|
|
17
19
|
}) {
|
|
18
|
-
// If it equals the non packed path, then it must not be packed, and so it must match.
|
|
19
|
-
if (config.parentPath === config.packedPath)
|
|
20
|
+
// If it equals the non packed path, then it must not be packed, and so it must match.
|
|
21
|
+
if (config.parentPath === config.packedPath) {
|
|
22
|
+
return getPathNetwork(config.fullPath) === DEFAULT_NETWORK;
|
|
23
|
+
}
|
|
20
24
|
let filter = decodeParentFilter(config.packedPath);
|
|
21
|
-
if (!filter)
|
|
25
|
+
if (!filter) {
|
|
26
|
+
return getPathNetwork(config.fullPath) === DEFAULT_NETWORK;
|
|
27
|
+
}
|
|
28
|
+
if (getPathNetwork(config.fullPath) !== (filter.network || DEFAULT_NETWORK)) return false;
|
|
22
29
|
if (filter.start <= 0 && filter.end >= 1) return true;
|
|
23
30
|
let route = PathRouter.getRouteFull({ path: config.fullPath, spec: getSpecForChildPath(config.fullPath) });
|
|
24
31
|
return filter.start <= route && route < filter.end;
|
|
@@ -26,12 +33,14 @@ export function matchesParentRangeFilter(config: {
|
|
|
26
33
|
|
|
27
34
|
export const filterChildPathsBase = measureWrap(
|
|
28
35
|
function filterChildPathsBase(parentPath: string, packedSuffix: string, paths: Set<string>): Set<string> {
|
|
29
|
-
let [startFractionStr, endFractionStr] = packedSuffix.split("|");
|
|
36
|
+
let [startFractionStr, endFractionStr, network] = packedSuffix.split("|");
|
|
30
37
|
let startFraction = Number(startFractionStr);
|
|
31
38
|
let endFraction = Number(endFractionStr);
|
|
39
|
+
let filterNetwork = network || DEFAULT_NETWORK;
|
|
32
40
|
|
|
33
41
|
let filtered = new Set<string>();
|
|
34
42
|
for (let path of paths) {
|
|
43
|
+
if (getPathNetwork(path) !== filterNetwork) continue;
|
|
35
44
|
// TODO: We can make this significantly more efficient as once we know if it's a prefix or not, we know the underlying function call every time. However, that starts to get complicated, and I don't think this is going to be a bottleneck.
|
|
36
45
|
let route = PathRouter.getRouteFull({ path, spec: getSpecForChildPath(path) });
|
|
37
46
|
if (startFraction <= route && route < endFraction) {
|
|
@@ -47,14 +56,19 @@ export function encodeParentFilter(config: {
|
|
|
47
56
|
path: string;
|
|
48
57
|
startFraction: number;
|
|
49
58
|
endFraction: number;
|
|
59
|
+
network?: string;
|
|
50
60
|
}) {
|
|
51
|
-
|
|
61
|
+
let suffix = `${config.startFraction}|${config.endFraction}`;
|
|
62
|
+
if (config.network) {
|
|
63
|
+
assertValidNetwork(config.network);
|
|
64
|
+
suffix += `|${config.network}`;
|
|
65
|
+
}
|
|
66
|
+
return hack_setPackedPathSuffix(config.path, suffix);
|
|
52
67
|
}
|
|
53
|
-
export function decodeParentFilter(path: string): { path: string; start: number, end: number } | undefined {
|
|
68
|
+
export function decodeParentFilter(path: string): { path: string; start: number, end: number; network?: string } | undefined {
|
|
54
69
|
let packedSuffix = hack_getPackedPathSuffix(path);
|
|
55
70
|
if (!packedSuffix) return undefined;
|
|
56
|
-
let [startStr, endStr] = packedSuffix.split("|");
|
|
71
|
+
let [startStr, endStr, network] = packedSuffix.split("|");
|
|
57
72
|
let unpackedPath = hack_stripPackedPath(path);
|
|
58
|
-
return { path: unpackedPath, start: Number(startStr), end: Number(endStr) };
|
|
59
|
-
|
|
60
|
-
}
|
|
73
|
+
return { path: unpackedPath, start: Number(startStr), end: Number(endStr), network: network || undefined };
|
|
74
|
+
}
|
|
@@ -24,7 +24,8 @@ import { fastHash } from "../misc/hash";
|
|
|
24
24
|
import { authorityLookup } from "./AuthorityLookup";
|
|
25
25
|
import { onPathInteracted } from "../diagnostics/pathAuditerCallback";
|
|
26
26
|
import { decodeParentFilter, filterChildPathsBase } from "./hackedPackedPathParentFiltering";
|
|
27
|
-
import {
|
|
27
|
+
import { getPathNetwork } from "./PathRouterRouteOverride";
|
|
28
|
+
import { isDiskAudit, DEFAULT_NETWORK } from "../config";
|
|
28
29
|
import { removeRange } from "../rangeMath";
|
|
29
30
|
import { remoteWatcher } from "../1-path-client/RemoteWatcher";
|
|
30
31
|
import { setFlag } from "socket-function/require/compileFlags";
|
|
@@ -569,18 +570,20 @@ class AuthorityPathValueStorage {
|
|
|
569
570
|
if (this.DEBUG_UNWATCH) {
|
|
570
571
|
console.log(blue(`Unsyncing path at ${Date.now()}`), path);
|
|
571
572
|
}
|
|
572
|
-
auditLog("DESTROY PATH", { path });
|
|
573
|
+
auditLog("DESTROY PATH", { path, network: getPathNetwork(path) });
|
|
573
574
|
|
|
574
575
|
this.isSyncedCache.delete(path);
|
|
575
576
|
this.removePathFromStorage(path, "unwatched");
|
|
576
577
|
}
|
|
577
578
|
public markParentPathAsUnwatched(path: string) {
|
|
578
579
|
if (this.parentsSynced.has(path)) {
|
|
579
|
-
console.info(`Unwatching path that is a parent synced path (fine, but might be an issue)`, { path });
|
|
580
|
+
console.info(`Unwatching path that is a parent synced path (fine, but might be an issue)`, { path, network: decodeParentFilter(path)?.network || DEFAULT_NETWORK });
|
|
580
581
|
}
|
|
581
582
|
// NOTE: I don't think we have to handle the case where we are the authority,
|
|
582
583
|
// because we don't delete any actual data here.
|
|
583
584
|
this.parentsSynced.delete(path);
|
|
585
|
+
let decoded = decodeParentFilter(path);
|
|
586
|
+
this.parentsSynced.delete(this.parentSyncKey(hack_stripPackedPath(path), decoded?.network));
|
|
584
587
|
}
|
|
585
588
|
|
|
586
589
|
/** Used for atomic operations, to ensure a path is stable enough (and to check that it hasn't
|
|
@@ -607,6 +610,11 @@ class AuthorityPathValueStorage {
|
|
|
607
610
|
}
|
|
608
611
|
return true;
|
|
609
612
|
}
|
|
613
|
+
private parentSyncKey(strippedParentPath: string, network: string | undefined): string {
|
|
614
|
+
if (!network || network === DEFAULT_NETWORK) return strippedParentPath;
|
|
615
|
+
return strippedParentPath + String.fromCharCode(1) + "network=" + network;
|
|
616
|
+
}
|
|
617
|
+
|
|
610
618
|
public isSynced(path: string) {
|
|
611
619
|
if (PathRouter.isSelfAuthority(path)) return true;
|
|
612
620
|
|
|
@@ -635,7 +643,7 @@ class AuthorityPathValueStorage {
|
|
|
635
643
|
let remoteWatchRoute = remoteWatcher.getRemoteParentWatchRoute(path);
|
|
636
644
|
if (remoteWatchRoute !== undefined) {
|
|
637
645
|
let parent = getParentPathStr(path);
|
|
638
|
-
let ranges = this.parentsSynced.get(parent);
|
|
646
|
+
let ranges = this.parentsSynced.get(this.parentSyncKey(parent, getPathNetwork(path)));
|
|
639
647
|
// NOTE: We can't cache this because when we stop watching the parent, the remote watch will remove it, which will cause us to be no longer synced. However, it's not going to know to go into our synced cache and remove it.
|
|
640
648
|
if (ranges === true) return true;
|
|
641
649
|
if (ranges) {
|
|
@@ -657,7 +665,7 @@ class AuthorityPathValueStorage {
|
|
|
657
665
|
parentPath = hack_stripPackedPath(parentPath);
|
|
658
666
|
if (PathRouter.isLocalPath(parentPath)) return true;
|
|
659
667
|
|
|
660
|
-
let synced = this.parentsSynced.get(originalPath) || this.parentsSynced.get(parentPath);
|
|
668
|
+
let synced = this.parentsSynced.get(originalPath) || this.parentsSynced.get(this.parentSyncKey(parentPath, range?.network));
|
|
661
669
|
if (synced === true) return true;
|
|
662
670
|
// 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.
|
|
663
671
|
if (synced && range) {
|
|
@@ -694,12 +702,12 @@ class AuthorityPathValueStorage {
|
|
|
694
702
|
public addParentSyncs(parentSyncs: { parentPath: string; sourceNodeId: string }[]) {
|
|
695
703
|
for (let obj of parentSyncs) {
|
|
696
704
|
if (isDebugLogEnabled()) {
|
|
697
|
-
auditLog("RECEIVED PARENT PATH", { parentPath: obj.parentPath, remoteNodeId: debugNodeId(obj.sourceNodeId), remoteNodeThreadId: debugNodeThread(obj.sourceNodeId) });
|
|
705
|
+
auditLog("RECEIVED PARENT PATH", { parentPath: obj.parentPath, network: decodeParentFilter(obj.parentPath)?.network || DEFAULT_NETWORK, remoteNodeId: debugNodeId(obj.sourceNodeId), remoteNodeThreadId: debugNodeThread(obj.sourceNodeId) });
|
|
698
706
|
}
|
|
699
707
|
|
|
700
708
|
let decoded = decodeParentFilter(obj.parentPath);
|
|
701
709
|
let range = decoded ? { start: decoded.start, end: decoded.end } : { start: 0, end: 1 };
|
|
702
|
-
let parentPath = hack_stripPackedPath(obj.parentPath);
|
|
710
|
+
let parentPath = this.parentSyncKey(hack_stripPackedPath(obj.parentPath), decoded?.network);
|
|
703
711
|
|
|
704
712
|
let prevSynced = this.parentsSynced.get(parentPath);
|
|
705
713
|
if (prevSynced === true) continue;
|