querysub 0.406.0 → 0.408.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/audit-disk-values.js +7 -0
- package/bin/deploy-prefixes.js +7 -0
- package/package.json +5 -3
- package/src/-a-archives/archiveCache.ts +12 -9
- package/src/-a-auth/certs.ts +1 -1
- package/src/-c-identity/IdentityController.ts +9 -1
- package/src/-f-node-discovery/NodeDiscovery.ts +63 -10
- package/src/0-path-value-core/AuthorityLookup.ts +14 -4
- package/src/0-path-value-core/PathRouter.ts +247 -117
- package/src/0-path-value-core/PathRouterRouteOverride.ts +1 -1
- package/src/0-path-value-core/PathRouterServerAuthoritySpec.tsx +4 -2
- package/src/0-path-value-core/PathValueCommitter.ts +68 -31
- package/src/0-path-value-core/PathValueController.ts +77 -8
- package/src/0-path-value-core/PathWatcher.ts +46 -4
- package/src/0-path-value-core/ShardPrefixes.ts +6 -0
- package/src/0-path-value-core/ValidStateComputer.ts +20 -8
- package/src/0-path-value-core/hackedPackedPathParentFiltering.ts +18 -55
- package/src/0-path-value-core/pathValueArchives.ts +19 -8
- package/src/0-path-value-core/pathValueCore.ts +75 -27
- package/src/0-path-value-core/startupAuthority.ts +9 -9
- package/src/1-path-client/RemoteWatcher.ts +217 -178
- package/src/1-path-client/pathValueClientWatcher.ts +6 -11
- package/src/2-proxy/pathValueProxy.ts +2 -3
- package/src/3-path-functions/PathFunctionRunner.ts +3 -1
- package/src/3-path-functions/syncSchema.ts +6 -2
- package/src/4-deploy/deployGetFunctionsInner.ts +1 -1
- package/src/4-deploy/deployPrefixes.ts +14 -0
- package/src/4-deploy/edgeNodes.ts +1 -1
- package/src/4-querysub/Querysub.ts +17 -5
- package/src/4-querysub/QuerysubController.ts +21 -10
- package/src/4-querysub/predictionQueue.tsx +3 -0
- package/src/4-querysub/querysubPrediction.ts +27 -20
- package/src/5-diagnostics/nodeMetadata.ts +17 -0
- package/src/diagnostics/NodeConnectionsPage.tsx +167 -0
- package/src/diagnostics/NodeViewer.tsx +11 -15
- package/src/diagnostics/PathDistributionInfo.tsx +102 -0
- package/src/diagnostics/SyncTestPage.tsx +19 -8
- package/src/diagnostics/auditDiskValues.ts +221 -0
- package/src/diagnostics/auditDiskValuesEntry.ts +43 -0
- package/src/diagnostics/logs/IndexedLogs/LogViewer3.tsx +5 -1
- package/src/diagnostics/logs/TimeRangeSelector.tsx +3 -3
- package/src/diagnostics/logs/lifeCycleAnalysis/LifeCycleRenderer.tsx +2 -0
- package/src/diagnostics/managementPages.tsx +10 -1
- package/src/diagnostics/misc-pages/ArchiveViewer.tsx +3 -2
- package/src/diagnostics/pathAuditer.ts +21 -0
- package/src/path.ts +9 -2
- package/src/rangeMath.ts +41 -0
- package/tempnotes.txt +5 -58
- package/test.ts +13 -295
- package/src/diagnostics/benchmark.ts +0 -139
- package/src/diagnostics/runSaturationTest.ts +0 -416
- package/src/diagnostics/satSchema.ts +0 -64
- package/src/test/mongoSatTest.tsx +0 -55
- package/src/test/satTest.ts +0 -193
- package/src/test/test.tsx +0 -552
|
@@ -1,77 +1,41 @@
|
|
|
1
1
|
import { measureWrap } from "socket-function/src/profiling/measure";
|
|
2
2
|
import { getPathDepth, getPathIndexAssert, hack_getPackedPathSuffix, hack_setPackedPathSuffix, hack_stripPackedPath } from "../path";
|
|
3
|
-
import { PathRouter } from "./PathRouter";
|
|
3
|
+
import { AuthoritySpec, PathRouter } from "./PathRouter";
|
|
4
4
|
import { cache } from "socket-function/src/caching";
|
|
5
5
|
import { authorityLookup } from "./AuthorityLookup";
|
|
6
6
|
|
|
7
|
-
// NOTE: This code has been moved into PathRouter
|
|
8
|
-
// /** Returns a number between 0 (inclusive) and 1 (exclusive)
|
|
9
|
-
// * - See matchesParentRangeFilter, matchesParentRangeFilterPart, and filterChildPaths.
|
|
10
|
-
// */
|
|
11
|
-
// export function __getRoutingHash(key: string): number {
|
|
12
|
-
// // Using fastHash is about twice as fast as sha256 (although that might be due to the buffer allocation?)
|
|
13
|
-
// let hash = fastHash(key);
|
|
14
|
-
// return hash % (1000 * 1000 * 1000) / (1000 * 1000 * 1000);
|
|
15
|
-
// // let hash = sha256(key);
|
|
16
|
-
// // return getBufferFraction(Buffer.from(hash, "hex"));
|
|
17
|
-
// }
|
|
18
7
|
|
|
19
|
-
let
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (path.startsWith(prefix)) return true;
|
|
24
|
-
}
|
|
25
|
-
return false;
|
|
26
|
-
});
|
|
8
|
+
let getSpecForChildPath = (path: string) => authorityLookup.getOurSpec();
|
|
9
|
+
export function registerGetSpecForChildPath(fnc: (path: string) => AuthoritySpec) {
|
|
10
|
+
getSpecForChildPath = fnc;
|
|
11
|
+
}
|
|
27
12
|
|
|
28
|
-
// NOTE: Assumes fullPath is already a child of parentPath, and only checks for hash
|
|
29
13
|
export function matchesParentRangeFilter(config: {
|
|
30
14
|
parentPath: string;
|
|
31
15
|
fullPath: string;
|
|
32
16
|
packedPath: string;
|
|
33
17
|
}) {
|
|
34
|
-
|
|
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) return true;
|
|
35
20
|
let filter = decodeParentFilter(config.packedPath);
|
|
36
21
|
if (!filter) return true;
|
|
37
|
-
if (filter.
|
|
38
|
-
|
|
39
|
-
return filter.start <= route && route < filter.end;
|
|
40
|
-
}
|
|
41
|
-
let route = PathRouter.getRouteChildKey(config.fullPath);
|
|
22
|
+
if (filter.start <= 0 && filter.end >= 1) return true;
|
|
23
|
+
let route = PathRouter.getRouteFull({ path: config.fullPath, spec: getSpecForChildPath(config.fullPath) });
|
|
42
24
|
return filter.start <= route && route < filter.end;
|
|
43
25
|
}
|
|
44
|
-
// export function matchesParentRangeFilterPart(config: {
|
|
45
|
-
// part: string;
|
|
46
|
-
// start: number;
|
|
47
|
-
// end: number;
|
|
48
|
-
// }) {
|
|
49
|
-
// if (config.start === 0 && config.end === 1) return true;
|
|
50
|
-
// let hash = __getRoutingHash(config.part);
|
|
51
|
-
// return config.start <= hash && hash < config.end;
|
|
52
|
-
// }
|
|
53
26
|
|
|
54
27
|
export const filterChildPathsBase = measureWrap(
|
|
55
28
|
function filterChildPathsBase(parentPath: string, packedSuffix: string, paths: Set<string>): Set<string> {
|
|
56
|
-
let [startFractionStr, endFractionStr
|
|
29
|
+
let [startFractionStr, endFractionStr] = packedSuffix.split("|");
|
|
57
30
|
let startFraction = Number(startFractionStr);
|
|
58
31
|
let endFraction = Number(endFractionStr);
|
|
59
32
|
|
|
60
|
-
let depth = getPathDepth(parentPath);
|
|
61
|
-
|
|
62
33
|
let filtered = new Set<string>();
|
|
63
34
|
for (let path of paths) {
|
|
64
|
-
if
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
} else {
|
|
70
|
-
let key = getPathIndexAssert(path, depth);
|
|
71
|
-
let hash = PathRouter.getSingleKeyRoute(key);
|
|
72
|
-
if (startFraction <= hash && hash < endFraction) {
|
|
73
|
-
filtered.add(path);
|
|
74
|
-
}
|
|
35
|
+
// 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
|
+
let route = PathRouter.getRouteFull({ path, spec: getSpecForChildPath(path) });
|
|
37
|
+
if (startFraction <= route && route < endFraction) {
|
|
38
|
+
filtered.add(path);
|
|
75
39
|
}
|
|
76
40
|
}
|
|
77
41
|
//console.log(`Filtered ${paths.size} paths to ${filtered.size} paths`);
|
|
@@ -83,15 +47,14 @@ export function encodeParentFilter(config: {
|
|
|
83
47
|
path: string;
|
|
84
48
|
startFraction: number;
|
|
85
49
|
endFraction: number;
|
|
86
|
-
useFullPathHash?: boolean;
|
|
87
50
|
}) {
|
|
88
|
-
return hack_setPackedPathSuffix(config.path, `${config.startFraction}|${config.endFraction}
|
|
51
|
+
return hack_setPackedPathSuffix(config.path, `${config.startFraction}|${config.endFraction}`);
|
|
89
52
|
}
|
|
90
|
-
export function decodeParentFilter(path: string): { path: string; start: number, end: number
|
|
53
|
+
export function decodeParentFilter(path: string): { path: string; start: number, end: number } | undefined {
|
|
91
54
|
let packedSuffix = hack_getPackedPathSuffix(path);
|
|
92
55
|
if (!packedSuffix) return undefined;
|
|
93
|
-
let [startStr, endStr
|
|
56
|
+
let [startStr, endStr] = packedSuffix.split("|");
|
|
94
57
|
let unpackedPath = hack_stripPackedPath(path);
|
|
95
|
-
return { path: unpackedPath, start: Number(startStr), end: Number(endStr)
|
|
58
|
+
return { path: unpackedPath, start: Number(startStr), end: Number(endStr) };
|
|
96
59
|
|
|
97
60
|
}
|
|
@@ -12,7 +12,7 @@ import { NodeIdParts, decodeNodeId, decodeNodeIdAssert, encodeNodeId } from "../
|
|
|
12
12
|
import { createArchiveLocker2 } from "./archiveLocks/ArchiveLocks2";
|
|
13
13
|
import { devDebugbreak, isNoNetwork } from "../config";
|
|
14
14
|
import { wrapArchivesWithCache } from "../-a-archives/archiveCache";
|
|
15
|
-
import { AuthoritySpec, PathRouter } from "./PathRouter";
|
|
15
|
+
import { AuthoritySpec, PathRouter, debugSpec } from "./PathRouter";
|
|
16
16
|
import { authorityLookup } from "./AuthorityLookup";
|
|
17
17
|
|
|
18
18
|
export const archives = lazy(() => wrapArchivesWithCache(getArchives("path-values/")));
|
|
@@ -209,7 +209,7 @@ export class PathValueArchives {
|
|
|
209
209
|
|
|
210
210
|
@measureFnc
|
|
211
211
|
public async loadValues(authority: AuthoritySpec): Promise<PathValueSnapshot> {
|
|
212
|
-
console.log(blue(`Loading data at ${new Date().toISOString()}`), { authority });
|
|
212
|
+
console.log(blue(`Loading data at ${new Date().toISOString()}`), { authority: debugSpec(authority) });
|
|
213
213
|
|
|
214
214
|
// let testFileName = getOwnNodeId() + "_" + Date.now() + "_" + 1_000_000_000;
|
|
215
215
|
// await archives().assertPathValid(dirName + "data_" + testFileName + ".data");
|
|
@@ -223,6 +223,7 @@ export class PathValueArchives {
|
|
|
223
223
|
let totalSize = 0;
|
|
224
224
|
|
|
225
225
|
let dataPaths: string[] = [];
|
|
226
|
+
let allPaths: string[] = [];
|
|
226
227
|
let readCache = new Map<string, Buffer>();
|
|
227
228
|
|
|
228
229
|
let downloadStartTime = Date.now();
|
|
@@ -233,8 +234,12 @@ export class PathValueArchives {
|
|
|
233
234
|
// the source file, but by now it has moved, so we don't see either file, even though at all times
|
|
234
235
|
// both files existed).
|
|
235
236
|
let time = Date.now();
|
|
236
|
-
|
|
237
|
-
|
|
237
|
+
let valueObjs = await this.getValuePaths(authority);
|
|
238
|
+
dataPaths = valueObjs.pickedPaths;
|
|
239
|
+
allPaths = valueObjs.allPaths;
|
|
240
|
+
let allValueCount = allPaths.reduce((acc, path) => acc + this.decodeDataPath(path).valueCount, 0);
|
|
241
|
+
let pickedValueCount = dataPaths.reduce((acc, path) => acc + this.decodeDataPath(path).valueCount, 0);
|
|
242
|
+
console.log(green(`${dataPaths.length}/${allPaths.length} (values ${formatNumber(pickedValueCount)} / ${formatNumber(allValueCount)}) data paths in ${formatTime(Date.now() - time)}`));
|
|
238
243
|
// NOTE: If the notMatched count is high enough, it is possible NodePathAuthorities.ts:authoritiesMightOverlap is
|
|
239
244
|
// too loose, and should be removing more cases.
|
|
240
245
|
|
|
@@ -318,7 +323,7 @@ export class PathValueArchives {
|
|
|
318
323
|
|
|
319
324
|
let relevantCount = totalCount - notMatched;
|
|
320
325
|
parseTime = Date.now() - parseTime;
|
|
321
|
-
console.log((`${green("Finished parsing")} ${formatNumber(totalCount)} in ${formatNumber(totalSize)}B (${formatNumber(uniqueCount / relevantCount * 100)}% unique paths = ${formatNumber(uniqueCount)}), in ${green(formatTime(parseTime))}, ${fileCount} files, (${formatNumber(notMatched / totalCount * 100)}% ignored, due to being for another authority) at ${new Date().toISOString()}. Startup time ${green(formatTime(process.uptime() * 1000))}`), { authority });
|
|
326
|
+
console.log((`${green("Finished parsing")} ${formatNumber(totalCount)} in ${formatNumber(totalSize)}B (${formatNumber(uniqueCount / relevantCount * 100)}% unique paths = ${formatNumber(uniqueCount)}), in ${green(formatTime(parseTime))}, ${fileCount} files, (${formatNumber(notMatched / totalCount * 100)}% ignored, due to being for another authority) at ${new Date().toISOString()}. Startup time ${green(formatTime(process.uptime() * 1000))}`), { authority: debugSpec(authority) });
|
|
322
327
|
|
|
323
328
|
if (tooOldValues) {
|
|
324
329
|
console.warn(yellow(`Discarded ${formatNumber(tooOldValues)} values discarded for being part of hanging server writes`));
|
|
@@ -437,7 +442,10 @@ export class PathValueArchives {
|
|
|
437
442
|
}
|
|
438
443
|
|
|
439
444
|
@measureFnc
|
|
440
|
-
public async getValuePaths(authority: AuthoritySpec): Promise<
|
|
445
|
+
public async getValuePaths(authority: AuthoritySpec): Promise<{
|
|
446
|
+
pickedPaths: string[];
|
|
447
|
+
allPaths: string[];
|
|
448
|
+
}> {
|
|
441
449
|
// let paths: string[] = [];
|
|
442
450
|
// let pickedDirectories = await this.getAuthorityDirs(authority);
|
|
443
451
|
|
|
@@ -450,8 +458,11 @@ export class PathValueArchives {
|
|
|
450
458
|
|
|
451
459
|
let locker = await this.getArchiveLocker();
|
|
452
460
|
let allFiles = (await locker.getAllValidFiles()).map(x => x.file);
|
|
453
|
-
let
|
|
454
|
-
return
|
|
461
|
+
let pickedPaths = allFiles.filter(x => PathRouter.overlapsPathIdentifier(authority, x));
|
|
462
|
+
return {
|
|
463
|
+
pickedPaths,
|
|
464
|
+
allPaths: allFiles,
|
|
465
|
+
};
|
|
455
466
|
}
|
|
456
467
|
@measureFnc
|
|
457
468
|
public async getValuePathSizes(paths: string[]): Promise<number[]> {
|
|
@@ -23,7 +23,9 @@ import { ellipsisMiddle } from "../misc";
|
|
|
23
23
|
import { fastHash } from "../misc/hash";
|
|
24
24
|
import { authorityLookup } from "./AuthorityLookup";
|
|
25
25
|
import { onPathInteracted } from "../diagnostics/pathAuditerCallback";
|
|
26
|
-
import { filterChildPathsBase } from "./hackedPackedPathParentFiltering";
|
|
26
|
+
import { decodeParentFilter, filterChildPathsBase } from "./hackedPackedPathParentFiltering";
|
|
27
|
+
import { isDiskAudit } from "../config";
|
|
28
|
+
import { removeRange } from "../rangeMath";
|
|
27
29
|
|
|
28
30
|
setImmediate(async () => {
|
|
29
31
|
// Import everything will dynamically import, so the client side can tell that it's required.
|
|
@@ -401,8 +403,8 @@ class AuthorityPathValueStorage {
|
|
|
401
403
|
private isSyncedCache = registerResource("paths|isSyncedCache", new Set<string>());
|
|
402
404
|
private parentsSynced = registerResource("paths|parentsSynced", new Map<string,
|
|
403
405
|
true |
|
|
404
|
-
// If set, contains the
|
|
405
|
-
|
|
406
|
+
// If set, contains the ranges received so far for this parent path.
|
|
407
|
+
{ start: number; end: number }[]
|
|
406
408
|
>());
|
|
407
409
|
|
|
408
410
|
public DEBUG_UNWATCH = false;
|
|
@@ -443,9 +445,10 @@ class AuthorityPathValueStorage {
|
|
|
443
445
|
// Include all values with other.time <= time, returning the one with the highest time
|
|
444
446
|
// (either that is valid, or, if readInvalid is true, just the highest time).
|
|
445
447
|
time?: Time,
|
|
446
|
-
readInvalid = false
|
|
448
|
+
readInvalid = false,
|
|
449
|
+
noAudit?: "noAudit"
|
|
447
450
|
): PathValue | undefined {
|
|
448
|
-
if (!this.isEventPath(path)) {
|
|
451
|
+
if (!this.isEventPath(path) && !noAudit) {
|
|
449
452
|
onPathInteracted(path, 0);
|
|
450
453
|
}
|
|
451
454
|
let overrideValue: PathValue | undefined;
|
|
@@ -542,6 +545,9 @@ class AuthorityPathValueStorage {
|
|
|
542
545
|
if (this.DEBUG_UNWATCH) {
|
|
543
546
|
console.log(blue(`Unsyncing path at ${Date.now()}`), path);
|
|
544
547
|
}
|
|
548
|
+
if (isDiskAudit()) {
|
|
549
|
+
auditLog("DESTROY PATH", { path });
|
|
550
|
+
}
|
|
545
551
|
|
|
546
552
|
this.isSyncedCache.delete(path);
|
|
547
553
|
this.removePathFromStorage(path, "unwatched");
|
|
@@ -603,10 +609,45 @@ class AuthorityPathValueStorage {
|
|
|
603
609
|
return false;
|
|
604
610
|
}
|
|
605
611
|
public isParentSynced(path: string) {
|
|
606
|
-
|
|
607
|
-
|
|
612
|
+
let range = decodeParentFilter(path);
|
|
613
|
+
path = hack_stripPackedPath(path);
|
|
614
|
+
if (PathRouter.isLocalPath(path)) return true;
|
|
615
|
+
|
|
616
|
+
let synced = this.parentsSynced.get(path);
|
|
617
|
+
if (synced === true) return true;
|
|
618
|
+
// 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
|
+
if (synced && range) {
|
|
620
|
+
let missingRanges: { start: number; end: number }[] = [{ start: range.start, end: range.end }];
|
|
621
|
+
for (let range of synced) {
|
|
622
|
+
removeRange(missingRanges, range);
|
|
623
|
+
}
|
|
624
|
+
if (missingRanges.length === 0) return true;
|
|
625
|
+
}
|
|
608
626
|
|
|
609
|
-
|
|
627
|
+
// Self authority check
|
|
628
|
+
{
|
|
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
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
return false;
|
|
610
651
|
}
|
|
611
652
|
|
|
612
653
|
/** Obviously just for debugging. Doesn't trigger any watchers, just erases it as if we never had it. There shouldn't be really any downstream caches within the same process at least, and so this should result in the next read having the value missing. If we in the future add downstream caches, we're going to have to invalidate them as well.
|
|
@@ -622,31 +663,37 @@ class AuthorityPathValueStorage {
|
|
|
622
663
|
// this.isSyncedCache.delete(path);
|
|
623
664
|
}
|
|
624
665
|
|
|
625
|
-
private getMultiNodesForParent: (path: string) => Map<string, unknown> | undefined = () => undefined;
|
|
626
|
-
public setGetMultiNodesForParent(fnc: (path: string) => Map<string, unknown> | undefined) {
|
|
627
|
-
this.getMultiNodesForParent = fnc;
|
|
628
|
-
}
|
|
629
|
-
|
|
630
666
|
public addParentSyncs(parentSyncs: { parentPath: string; sourceNodeId: string }[]) {
|
|
631
|
-
|
|
632
667
|
for (let obj of parentSyncs) {
|
|
633
|
-
let
|
|
634
|
-
let
|
|
668
|
+
let decoded = decodeParentFilter(obj.parentPath);
|
|
669
|
+
let range = decoded ? { start: decoded.start, end: decoded.end } : { start: 0, end: 1 };
|
|
670
|
+
let parentPath = hack_stripPackedPath(obj.parentPath);
|
|
671
|
+
|
|
635
672
|
let prevSynced = this.parentsSynced.get(parentPath);
|
|
636
673
|
if (prevSynced === true) continue;
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
if (!
|
|
640
|
-
|
|
674
|
+
|
|
675
|
+
let ranges: { start: number; end: number }[];
|
|
676
|
+
if (!prevSynced) {
|
|
677
|
+
ranges = [];
|
|
678
|
+
this.parentsSynced.set(parentPath, ranges);
|
|
641
679
|
} else {
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
680
|
+
ranges = prevSynced;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
ranges.push(range);
|
|
684
|
+
|
|
685
|
+
function rangesCoverFull(ranges: { start: number; end: number }[]): boolean {
|
|
686
|
+
let sorted = ranges.slice().sort((a, b) => a.start - b.start);
|
|
687
|
+
let covered = 0;
|
|
688
|
+
for (let range of sorted) {
|
|
689
|
+
if (range.start > covered) return false;
|
|
690
|
+
if (range.end > covered) covered = range.end;
|
|
649
691
|
}
|
|
692
|
+
return covered >= 1;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
if (rangesCoverFull(ranges)) {
|
|
696
|
+
this.parentsSynced.set(parentPath, true);
|
|
650
697
|
}
|
|
651
698
|
}
|
|
652
699
|
}
|
|
@@ -1044,6 +1091,7 @@ class AuthorityPathValueStorage {
|
|
|
1044
1091
|
let { spec, startTime, endTime } = config;
|
|
1045
1092
|
let result: PathValue[] = [];
|
|
1046
1093
|
for (let [path, values] of this.values) {
|
|
1094
|
+
if (PathRouter.isLocalPath(path)) continue;
|
|
1047
1095
|
if (PathRouter.matchesAuthoritySpec(spec, path)) {
|
|
1048
1096
|
for (let value of values) {
|
|
1049
1097
|
let time = value.time.time;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { delay } from "socket-function/src/batching";
|
|
2
2
|
import { DISK_FLUSH_INTERVAL } from "../diagnostics/logs/IndexedLogs/BufferIndexLogsOptimizationConstants";
|
|
3
3
|
import { authorityLookup } from "./AuthorityLookup";
|
|
4
|
-
import { AuthoritySpec, PathRouter } from "./PathRouter";
|
|
4
|
+
import { AuthoritySpec, PathRouter, debugSpec } from "./PathRouter";
|
|
5
5
|
import { PathValueControllerBase, pathValueCommitter } from "./PathValueController";
|
|
6
6
|
import { validStateComputer } from "./ValidStateComputer";
|
|
7
7
|
import { MAX_TIME_UNTIL_DISK_FLUSH, authorityStorage, pathValueArchives } from "./pathValueCore";
|
|
@@ -11,7 +11,7 @@ import { formatNumber, formatTime } from "socket-function/src/formatting/format"
|
|
|
11
11
|
const MAX_LOAD_RETRIES = 3;
|
|
12
12
|
export async function startupAuthority(spec: AuthoritySpec) {
|
|
13
13
|
let startTime = Date.now();
|
|
14
|
-
console.log(`Becoming authority for`, { spec });
|
|
14
|
+
console.log(`Becoming authority for`, { spec: debugSpec(spec) });
|
|
15
15
|
|
|
16
16
|
// NOTE: This will broadcast all the other servers about our spec, and so all other servers will start giving us live writes.
|
|
17
17
|
await authorityLookup.setOurSpec(spec);
|
|
@@ -20,23 +20,23 @@ export async function startupAuthority(spec: AuthoritySpec) {
|
|
|
20
20
|
let readEndTime = Number.MAX_SAFE_INTEGER;
|
|
21
21
|
|
|
22
22
|
let sources = await PathRouter.getAuthoritySources({ target: spec });
|
|
23
|
-
console.log(`Found sources when finding spec, count: ${sources.length}`, { spec, sources });
|
|
23
|
+
console.log(`Found sources when finding spec, count: ${sources.length}`, { us: debugSpec(spec), sources });
|
|
24
24
|
async function loadFromSourceBase(source: AuthoritySpec) {
|
|
25
|
-
console.log(blue(`Loading values from source ${source.nodeId}`), { nodeId: source.nodeId, spec: source, readStartTime, readEndTime });
|
|
25
|
+
console.log(blue(`Loading values from source ${source.nodeId}`), { nodeId: source.nodeId, spec: debugSpec(source), readStartTime, readEndTime });
|
|
26
26
|
let values = await PathValueControllerBase.getInitialValues({
|
|
27
27
|
nodeId: source.nodeId,
|
|
28
28
|
spec: source,
|
|
29
29
|
startTime: readStartTime,
|
|
30
30
|
endTime: readEndTime,
|
|
31
31
|
});
|
|
32
|
-
console.log(blue(`Finished loading values from source ${source.nodeId}, found ${formatNumber(values.length)} values`), { nodeId: source.nodeId, spec: source, values: values.length });
|
|
32
|
+
console.log(blue(`Finished loading values from source ${source.nodeId}, found ${formatNumber(values.length)} values`), { nodeId: source.nodeId, spec: debugSpec(source), values: values.length });
|
|
33
33
|
validStateComputer.ingestValuesAndValidStates({
|
|
34
34
|
pathValues: values,
|
|
35
35
|
parentSyncs: [],
|
|
36
36
|
initialTriggers: { values: new Set(), parentPaths: new Set() },
|
|
37
37
|
doNotArchive: true,
|
|
38
38
|
});
|
|
39
|
-
console.log(blue(`Finished ingesting values from source ${source.nodeId} (values: ${formatNumber(values.length)}`), { nodeId: source.nodeId, spec: source, values: values.length });
|
|
39
|
+
console.log(blue(`Finished ingesting values from source ${source.nodeId} (values: ${formatNumber(values.length)}`), { nodeId: source.nodeId, spec: debugSpec(source), values: values.length });
|
|
40
40
|
}
|
|
41
41
|
async function loadFromSource(source: AuthoritySpec, retries = 3) {
|
|
42
42
|
try {
|
|
@@ -60,15 +60,15 @@ export async function startupAuthority(spec: AuthoritySpec) {
|
|
|
60
60
|
let diskPromise = (async () => {
|
|
61
61
|
let snapshot = await pathValueArchives.loadValues(spec);
|
|
62
62
|
let flat = Object.values(snapshot.values).flat();
|
|
63
|
-
console.log(blue(`Loaded snapshot in memory, found ${formatNumber(flat.length)} values`), { spec, values: flat.length });
|
|
63
|
+
console.log(blue(`Loaded snapshot in memory, found ${formatNumber(flat.length)} values`), { spec: debugSpec(spec), values: flat.length });
|
|
64
64
|
// NOTE: If it's on disk, it can't be rejected, so there's no point in passing it through our validStateComputer (and passing it through is VERY slow).
|
|
65
65
|
authorityStorage.ingestValues(flat, { doNotArchive: true });
|
|
66
|
-
console.log(blue(`Finished ingesting values from snapshot, found ${formatNumber(flat.length)} values`), { spec, values: flat.length });
|
|
66
|
+
console.log(blue(`Finished ingesting values from snapshot, found ${formatNumber(flat.length)} values`), { spec: debugSpec(spec), values: flat.length });
|
|
67
67
|
})();
|
|
68
68
|
|
|
69
69
|
await Promise.all([...sourcePromises, diskPromise]);
|
|
70
70
|
|
|
71
71
|
await authorityLookup.setIsReady();
|
|
72
72
|
let duration = Date.now() - startTime;
|
|
73
|
-
console.log(green(`Finished becoming authority, took ${formatTime(duration)}`), { spec });
|
|
73
|
+
console.log(green(`Finished becoming authority, took ${formatTime(duration)}`), { spec: debugSpec(spec) });
|
|
74
74
|
}
|