querysub 0.403.0 → 0.405.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/.cursorrules +2 -0
- package/bin/audit-imports.js +4 -0
- package/bin/join.js +1 -1
- package/package.json +7 -4
- package/spec.txt +77 -0
- package/src/-a-archives/archiveCache.ts +9 -4
- package/src/-a-archives/archivesBackBlaze.ts +1039 -1039
- package/src/-a-auth/certs.ts +0 -12
- package/src/-c-identity/IdentityController.ts +12 -3
- package/src/-f-node-discovery/NodeDiscovery.ts +32 -26
- package/src/-g-core-values/NodeCapabilities.ts +12 -2
- package/src/0-path-value-core/AuthorityLookup.ts +239 -0
- package/src/0-path-value-core/LockWatcher2.ts +150 -0
- package/src/0-path-value-core/PathRouter.ts +543 -0
- package/src/0-path-value-core/PathRouterRouteOverride.ts +72 -0
- package/src/0-path-value-core/PathRouterServerAuthoritySpec.tsx +73 -0
- package/src/0-path-value-core/PathValueCommitter.ts +222 -488
- package/src/0-path-value-core/PathValueController.ts +277 -239
- package/src/0-path-value-core/PathWatcher.ts +534 -0
- package/src/0-path-value-core/ShardPrefixes.ts +31 -0
- package/src/0-path-value-core/ValidStateComputer.ts +303 -0
- package/src/0-path-value-core/archiveLocks/ArchiveLocks.ts +1 -1
- package/src/0-path-value-core/archiveLocks/ArchiveLocks2.ts +80 -44
- package/src/0-path-value-core/archiveLocks/archiveSnapshots.ts +13 -16
- package/src/0-path-value-core/auditLogs.ts +2 -0
- package/src/0-path-value-core/hackedPackedPathParentFiltering.ts +97 -0
- package/src/0-path-value-core/pathValueArchives.ts +491 -492
- package/src/0-path-value-core/pathValueCore.ts +195 -1496
- package/src/0-path-value-core/startupAuthority.ts +74 -0
- package/src/1-path-client/RemoteWatcher.ts +90 -82
- package/src/1-path-client/pathValueClientWatcher.ts +808 -815
- package/src/2-proxy/PathValueProxyWatcher.ts +10 -8
- package/src/2-proxy/archiveMoveHarness.ts +182 -214
- package/src/2-proxy/garbageCollection.ts +9 -8
- package/src/2-proxy/schema2.ts +21 -1
- package/src/3-path-functions/PathFunctionHelpers.ts +206 -180
- package/src/3-path-functions/PathFunctionRunner.ts +943 -766
- package/src/3-path-functions/PathFunctionRunnerMain.ts +5 -3
- package/src/3-path-functions/pathFunctionLoader.ts +2 -2
- package/src/3-path-functions/syncSchema.ts +596 -521
- package/src/4-deploy/deployFunctions.ts +19 -4
- package/src/4-deploy/deployGetFunctionsInner.ts +8 -2
- package/src/4-deploy/deployMain.ts +51 -68
- package/src/4-deploy/edgeClientWatcher.tsx +6 -1
- package/src/4-deploy/edgeNodes.ts +2 -2
- package/src/4-dom/qreact.tsx +2 -4
- package/src/4-dom/qreactTest.tsx +7 -13
- package/src/4-querysub/Querysub.ts +21 -8
- package/src/4-querysub/QuerysubController.ts +45 -29
- package/src/4-querysub/permissions.ts +2 -2
- package/src/4-querysub/querysubPrediction.ts +80 -70
- package/src/4-querysub/schemaHelpers.ts +5 -1
- package/src/5-diagnostics/GenericFormat.tsx +14 -9
- package/src/archiveapps/archiveGCEntry.tsx +9 -2
- package/src/archiveapps/archiveJoinEntry.ts +96 -84
- package/src/bits.ts +19 -0
- package/src/config.ts +21 -3
- package/src/config2.ts +23 -48
- package/src/deployManager/components/DeployPage.tsx +7 -3
- package/src/deployManager/machineSchema.ts +4 -1
- package/src/diagnostics/ActionsHistory.ts +3 -8
- package/src/diagnostics/AuditLogPage.tsx +2 -3
- package/src/diagnostics/FunctionCallInfo.tsx +141 -0
- package/src/diagnostics/FunctionCallInfoState.ts +162 -0
- package/src/diagnostics/MachineThreadInfo.tsx +1 -1
- package/src/diagnostics/NodeViewer.tsx +37 -48
- package/src/diagnostics/SyncTestPage.tsx +241 -0
- package/src/diagnostics/auditImportViolations.ts +185 -0
- package/src/diagnostics/listenOnDebugger.ts +3 -3
- package/src/diagnostics/logs/IndexedLogs/BufferUnitSet.ts +10 -4
- package/src/diagnostics/logs/IndexedLogs/IndexedLogs.ts +2 -2
- package/src/diagnostics/logs/IndexedLogs/LogViewer3.tsx +24 -22
- package/src/diagnostics/logs/IndexedLogs/moveIndexLogsToPublic.ts +1 -1
- package/src/diagnostics/logs/diskLogGlobalContext.ts +1 -0
- package/src/diagnostics/logs/errorNotifications2/logWatcher.ts +1 -3
- package/src/diagnostics/logs/lifeCycleAnalysis/LifeCycleEntryEditor.tsx +34 -16
- package/src/diagnostics/logs/lifeCycleAnalysis/LifeCycleEntryReadMode.tsx +4 -6
- package/src/diagnostics/logs/lifeCycleAnalysis/LifeCycleInstanceTableView.tsx +36 -5
- package/src/diagnostics/logs/lifeCycleAnalysis/LifeCyclePage.tsx +19 -5
- package/src/diagnostics/logs/lifeCycleAnalysis/LifeCycleRenderer.tsx +15 -7
- package/src/diagnostics/logs/lifeCycleAnalysis/NestedLifeCycleInfo.tsx +28 -106
- package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycleMatching.ts +2 -0
- package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycleMisc.ts +0 -0
- package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycleSearch.tsx +18 -7
- package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycles.tsx +3 -0
- package/src/diagnostics/managementPages.tsx +10 -3
- package/src/diagnostics/misc-pages/ArchiveViewer.tsx +20 -26
- package/src/diagnostics/misc-pages/ArchiveViewerTree.tsx +6 -4
- package/src/diagnostics/misc-pages/ComponentSyncStats.tsx +2 -2
- package/src/diagnostics/misc-pages/LocalWatchViewer.tsx +7 -9
- package/src/diagnostics/misc-pages/SnapshotViewer.tsx +23 -12
- package/src/diagnostics/misc-pages/archiveViewerShared.tsx +1 -1
- package/src/diagnostics/pathAuditer.ts +486 -0
- package/src/diagnostics/pathAuditerCallback.ts +20 -0
- package/src/diagnostics/watchdog.ts +8 -1
- package/src/library-components/URLParam.ts +1 -1
- package/src/misc/hash.ts +1 -0
- package/src/path.ts +21 -7
- package/src/server.ts +54 -47
- package/src/user-implementation/loginEmail.tsx +1 -1
- package/tempnotes.txt +65 -0
- package/test.ts +298 -97
- package/src/0-path-value-core/NodePathAuthorities.ts +0 -1057
- package/src/0-path-value-core/PathController.ts +0 -1
- package/src/5-diagnostics/diskValueAudit.ts +0 -218
- package/src/5-diagnostics/memoryValueAudit.ts +0 -438
- package/src/archiveapps/archiveMergeEntry.tsx +0 -48
- package/src/archiveapps/lockTest.ts +0 -127
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { delay } from "socket-function/src/batching";
|
|
2
|
+
import { DISK_FLUSH_INTERVAL } from "../diagnostics/logs/IndexedLogs/BufferIndexLogsOptimizationConstants";
|
|
3
|
+
import { authorityLookup } from "./AuthorityLookup";
|
|
4
|
+
import { AuthoritySpec, PathRouter } from "./PathRouter";
|
|
5
|
+
import { PathValueControllerBase, pathValueCommitter } from "./PathValueController";
|
|
6
|
+
import { validStateComputer } from "./ValidStateComputer";
|
|
7
|
+
import { MAX_TIME_UNTIL_DISK_FLUSH, authorityStorage, pathValueArchives } from "./pathValueCore";
|
|
8
|
+
import { blue, green } from "socket-function/src/formatting/logColors";
|
|
9
|
+
import { formatNumber, formatTime } from "socket-function/src/formatting/format";
|
|
10
|
+
|
|
11
|
+
const MAX_LOAD_RETRIES = 3;
|
|
12
|
+
export async function startupAuthority(spec: AuthoritySpec) {
|
|
13
|
+
let startTime = Date.now();
|
|
14
|
+
console.log(`Becoming authority for`, { spec });
|
|
15
|
+
|
|
16
|
+
// NOTE: This will broadcast all the other servers about our spec, and so all other servers will start giving us live writes.
|
|
17
|
+
await authorityLookup.setOurSpec(spec);
|
|
18
|
+
|
|
19
|
+
let readStartTime = Date.now() - MAX_TIME_UNTIL_DISK_FLUSH;
|
|
20
|
+
let readEndTime = Number.MAX_SAFE_INTEGER;
|
|
21
|
+
|
|
22
|
+
let sources = await PathRouter.getAuthoritySources({ target: spec });
|
|
23
|
+
console.log(`Found sources when finding spec, count: ${sources.length}`, { spec, sources });
|
|
24
|
+
async function loadFromSourceBase(source: AuthoritySpec) {
|
|
25
|
+
console.log(blue(`Loading values from source ${source.nodeId}`), { nodeId: source.nodeId, spec: source, readStartTime, readEndTime });
|
|
26
|
+
let values = await PathValueControllerBase.getInitialValues({
|
|
27
|
+
nodeId: source.nodeId,
|
|
28
|
+
spec: source,
|
|
29
|
+
startTime: readStartTime,
|
|
30
|
+
endTime: readEndTime,
|
|
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 });
|
|
33
|
+
validStateComputer.ingestValuesAndValidStates({
|
|
34
|
+
pathValues: values,
|
|
35
|
+
parentSyncs: [],
|
|
36
|
+
initialTriggers: { values: new Set(), parentPaths: new Set() },
|
|
37
|
+
doNotArchive: true,
|
|
38
|
+
});
|
|
39
|
+
console.log(blue(`Finished ingesting values from source ${source.nodeId} (values: ${formatNumber(values.length)}`), { nodeId: source.nodeId, spec: source, values: values.length });
|
|
40
|
+
}
|
|
41
|
+
async function loadFromSource(source: AuthoritySpec, retries = 3) {
|
|
42
|
+
try {
|
|
43
|
+
await loadFromSourceBase(source);
|
|
44
|
+
return;
|
|
45
|
+
} catch (error: any) {
|
|
46
|
+
console.error(`Failed to load from source ${source.nodeId}`, { error: error.stack });
|
|
47
|
+
await delay(1000);
|
|
48
|
+
if (retries > 0) {
|
|
49
|
+
let sources = await PathRouter.getAuthoritySources({ target: source });
|
|
50
|
+
await Promise.all(sources.map(x => loadFromSource(x, retries - 1)));
|
|
51
|
+
} else {
|
|
52
|
+
console.error(`Failed to load from source ${source.nodeId} after ${MAX_LOAD_RETRIES} retries`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let sourcePromises = sources.map(x => loadFromSource(x));
|
|
58
|
+
|
|
59
|
+
// Oh, we need to load snapshots as well... of course
|
|
60
|
+
let diskPromise = (async () => {
|
|
61
|
+
let snapshot = await pathValueArchives.loadValues(spec);
|
|
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 });
|
|
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
|
+
authorityStorage.ingestValues(flat, { doNotArchive: true });
|
|
66
|
+
console.log(blue(`Finished ingesting values from snapshot, found ${formatNumber(flat.length)} values`), { spec, values: flat.length });
|
|
67
|
+
})();
|
|
68
|
+
|
|
69
|
+
await Promise.all([...sourcePromises, diskPromise]);
|
|
70
|
+
|
|
71
|
+
await authorityLookup.setIsReady();
|
|
72
|
+
let duration = Date.now() - startTime;
|
|
73
|
+
console.log(green(`Finished becoming authority, took ${formatTime(duration)}`), { spec });
|
|
74
|
+
}
|
|
@@ -1,28 +1,31 @@
|
|
|
1
1
|
|
|
2
2
|
import { SocketFunction } from "socket-function/SocketFunction";
|
|
3
|
-
import { batchFunction,
|
|
3
|
+
import { batchFunction, runInSerial, runInfinitePoll } from "socket-function/src/batching";
|
|
4
4
|
import { cache } from "socket-function/src/caching";
|
|
5
|
-
import { isNode
|
|
5
|
+
import { isNode } from "socket-function/src/misc";
|
|
6
6
|
import { measureFnc, measureBlock } from "socket-function/src/profiling/measure";
|
|
7
|
-
import { isOwnNodeId
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { WatchConfig, authorityStorage
|
|
7
|
+
import { isOwnNodeId } from "../-f-node-discovery/NodeDiscovery";
|
|
8
|
+
import { PathValueController } from "../0-path-value-core/PathValueController";
|
|
9
|
+
import { PathRouter } from "../0-path-value-core/PathRouter";
|
|
10
|
+
import { WatchConfig, authorityStorage } from "../0-path-value-core/pathValueCore";
|
|
11
|
+
import { pathWatcher } from "../0-path-value-core/PathWatcher";
|
|
11
12
|
import { ActionsHistory } from "../diagnostics/ActionsHistory";
|
|
12
13
|
import { registerResource } from "../diagnostics/trackResources";
|
|
13
14
|
import { logErrors } from "../errors";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import { appendToPathStr, getParentPathStr, getPathDepth, getPathIndexAssert, getPathSuffix, hack_getPackedPathSuffix, hack_setPackedPathSuffix } from "../path";
|
|
15
|
+
import { green, yellow } from "socket-function/src/formatting/logColors";
|
|
16
|
+
import { getParentPathStr } from "../path";
|
|
17
17
|
import { isEmpty } from "../misc";
|
|
18
|
-
import debugbreak from "debugbreak";
|
|
19
18
|
import { isDevDebugbreak } from "../config";
|
|
20
19
|
import { auditLog, isDebugLogEnabled } from "../0-path-value-core/auditLogs";
|
|
21
20
|
import { debugNodeThread } from "../-c-identity/IdentityController";
|
|
21
|
+
import { authorityLookup } from "../0-path-value-core/AuthorityLookup";
|
|
22
|
+
import { decodeParentFilter, encodeParentFilter } from "../0-path-value-core/hackedPackedPathParentFiltering";
|
|
23
|
+
setImmediate(() => import("../4-querysub/Querysub"));
|
|
22
24
|
|
|
23
25
|
// NOTE: In some cases this can half our the PathValues sent. AND, it can reduce our sync requests by N, and as those are uploads those are event more expensive. However, it also complicates other code, requiring not just checking if a value is synced, but also the parent path. We should try to make this stable, but worst case scenario, we can set this to false, and it will make our synchronization a lot easier to verify.
|
|
24
26
|
const STOP_KEYS_DOUBLE_SENDS = true;
|
|
25
27
|
|
|
28
|
+
// NOTE: If a parent watch is broken up between multiple nodes, we generally watch everything on all those nodes and then filter when we receive the data. This isn't efficient, and we should probably change it. However, in practice, it's probably fine, as it's unlikely for the watches to be broken up to a much finer granularity than the network (it would require very strange partially overlapping sharding cases which no reasonable sharding setup would ever satisfy).
|
|
26
29
|
export class RemoteWatcher {
|
|
27
30
|
public static DEBUG = false;
|
|
28
31
|
|
|
@@ -32,7 +35,11 @@ export class RemoteWatcher {
|
|
|
32
35
|
// path => nodeId
|
|
33
36
|
// isOwnNodeId(nodeId) means we are watching it locally
|
|
34
37
|
private remoteWatchPaths = registerResource("paths|remoteWatchPaths", new Map<string, string>());
|
|
38
|
+
// inputPath =>
|
|
35
39
|
private remoteWatchParents = registerResource("paths|remoteWatchParents", new Map<string, {
|
|
40
|
+
useFullPathHash: boolean;
|
|
41
|
+
// Might vary from the input path if useFullPathHash is true (and the path is a hacked path, and so which has a range restriction).
|
|
42
|
+
watchedPath: string;
|
|
36
43
|
nodesId: Map<string, {
|
|
37
44
|
start: number;
|
|
38
45
|
end: number;
|
|
@@ -116,12 +123,13 @@ export class RemoteWatcher {
|
|
|
116
123
|
public debugIsWatchingPath(path: string) {
|
|
117
124
|
// HACK: If there is no read node... pretend we are watching it. Because... for now, this is a known
|
|
118
125
|
// and the code calling this function isn't looking for that specific issue.
|
|
119
|
-
return this.remoteWatchPaths.has(path) || !
|
|
126
|
+
return this.remoteWatchPaths.has(path) || !PathRouter.getReadyAuthority(path);
|
|
120
127
|
}
|
|
121
128
|
public debugIsWatchingParentPath(path: string) {
|
|
122
|
-
return this.remoteWatchParents.has(path) || !
|
|
129
|
+
return this.remoteWatchParents.has(path) || !PathRouter.getReadyAuthority(path);
|
|
123
130
|
}
|
|
124
131
|
|
|
132
|
+
/** @deprecated If you want to watch something, call pathWatcher.watchPath instead. */
|
|
125
133
|
public watchLatest(config: WatchConfig & { debugName?: string }) {
|
|
126
134
|
logErrors(this.watchLatestPromise(config));
|
|
127
135
|
}
|
|
@@ -131,10 +139,9 @@ export class RemoteWatcher {
|
|
|
131
139
|
// 2) If it errors out, they can't do anything to fix the error
|
|
132
140
|
// 3) We retry internally anyways
|
|
133
141
|
private watchLatestPromise(config: WatchConfig & { debugName?: string }) {
|
|
134
|
-
// NOTE: If none of the values are remote... early out. This has been profiled to save a bit of time,
|
|
135
|
-
// mostly due to avoiding the async call.
|
|
142
|
+
// NOTE: If none of the values are remote... early out. This has been profiled to save a bit of time, mostly due to avoiding the async call.
|
|
136
143
|
if (config.parentPaths.length === 0) {
|
|
137
|
-
let isSelf = measureBlock(() => config.paths.every(x =>
|
|
144
|
+
let isSelf = measureBlock(() => config.paths.every(x => PathRouter.isSelfAuthority(x)), "RemoteWatcher()|watchLatestPromise|isSelfCheck");
|
|
138
145
|
if (isSelf) {
|
|
139
146
|
return;
|
|
140
147
|
}
|
|
@@ -155,7 +162,7 @@ export class RemoteWatcher {
|
|
|
155
162
|
/** NOTE: We dedupe duplicate watches in watchLatest. */
|
|
156
163
|
private async watchLatestBase(config: WatchConfig & { debugName?: string }) {
|
|
157
164
|
await this.watchUnwatchSerial("watchLatestBase", async () => {
|
|
158
|
-
await
|
|
165
|
+
await authorityLookup.startSyncing();
|
|
159
166
|
|
|
160
167
|
this.internalWatchLatest(config);
|
|
161
168
|
});
|
|
@@ -181,7 +188,7 @@ export class RemoteWatcher {
|
|
|
181
188
|
measureBlock(() => {
|
|
182
189
|
for (let path of config.paths) {
|
|
183
190
|
// NOTE: We weren't ignoring local paths here for a while, but... I'm pretty sure we should, to save time.
|
|
184
|
-
if (
|
|
191
|
+
if (PathRouter.isLocalPath(path)) continue;
|
|
185
192
|
|
|
186
193
|
// IMPORTANT! We have to await, otherwise we might see the paths are watched, skip them
|
|
187
194
|
// find another path which has an authority, then go to watch that... which causes
|
|
@@ -201,10 +208,11 @@ export class RemoteWatcher {
|
|
|
201
208
|
continue;
|
|
202
209
|
}
|
|
203
210
|
|
|
204
|
-
let authorityId =
|
|
211
|
+
let authorityId = PathRouter.getReadyAuthority(path)?.nodeId;
|
|
205
212
|
if (!authorityId) {
|
|
206
213
|
if (totalMissingPaths === 0) {
|
|
207
|
-
let authorities =
|
|
214
|
+
let authorities = authorityLookup.getTopologySync();
|
|
215
|
+
authorityId = PathRouter.getReadyAuthority(path)?.nodeId;
|
|
208
216
|
console.warn(`Missing authority for path ${path}`, { authorities });
|
|
209
217
|
}
|
|
210
218
|
totalMissingPaths++;
|
|
@@ -228,29 +236,53 @@ export class RemoteWatcher {
|
|
|
228
236
|
paths.paths.push(path);
|
|
229
237
|
}
|
|
230
238
|
|
|
231
|
-
for (let
|
|
239
|
+
for (let originalPath of config.parentPaths) {
|
|
232
240
|
// NOTE: We weren't ignoring local paths here for a while, but... I'm pretty sure we should, to save time.
|
|
233
|
-
if (
|
|
234
|
-
let watchObj = this.remoteWatchParents.get(
|
|
241
|
+
if (PathRouter.isLocalPath(originalPath)) continue;
|
|
242
|
+
let watchObj = this.remoteWatchParents.get(originalPath);
|
|
235
243
|
if (
|
|
236
244
|
watchObj
|
|
237
245
|
&& watchObj.nodesId.size === watchObj.fullNodeCount
|
|
238
246
|
&& !config.tryReconnect
|
|
239
247
|
) continue;
|
|
240
248
|
|
|
241
|
-
let { nodes } =
|
|
249
|
+
let { useFullPathHash, nodes } = PathRouter.getChildReadNodes(originalPath, {
|
|
242
250
|
// Pass existing connected node ids as preferred
|
|
243
|
-
preferredNodeIds: watchObj
|
|
251
|
+
preferredNodeIds: watchObj && Array.from(watchObj.nodesId.keys())
|
|
244
252
|
});
|
|
253
|
+
let watchedPath = originalPath;
|
|
254
|
+
useFullPathHash = !!useFullPathHash;
|
|
255
|
+
let decodedParentFilter = decodeParentFilter(originalPath);
|
|
256
|
+
if (useFullPathHash && decodedParentFilter) {
|
|
257
|
+
watchedPath = encodeParentFilter({
|
|
258
|
+
path: decodedParentFilter.path,
|
|
259
|
+
startFraction: decodedParentFilter.start,
|
|
260
|
+
endFraction: decodedParentFilter.end,
|
|
261
|
+
useFullPathHash: true,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Unwatch the old watchedPath
|
|
266
|
+
if (watchObj && watchObj.watchedPath !== watchedPath) {
|
|
267
|
+
for (let nodeId of watchObj.nodesId.keys()) {
|
|
268
|
+
let parentPaths = unwatchParentsPerAuthority.get(nodeId);
|
|
269
|
+
if (!parentPaths) {
|
|
270
|
+
parentPaths = new Set();
|
|
271
|
+
unwatchParentsPerAuthority.set(nodeId, parentPaths);
|
|
272
|
+
}
|
|
273
|
+
parentPaths.add(watchObj.watchedPath);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
watchObj = undefined;
|
|
277
|
+
}
|
|
245
278
|
|
|
246
279
|
let rangeStart = 0;
|
|
247
280
|
let rangeEnd = 1;
|
|
248
281
|
|
|
249
282
|
let nodesToAdd = new Map<string, { start: number; end: number }>();
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
rangeEnd = range.end;
|
|
283
|
+
if (decodedParentFilter) {
|
|
284
|
+
rangeStart = decodedParentFilter.start;
|
|
285
|
+
rangeEnd = decodedParentFilter.end;
|
|
254
286
|
}
|
|
255
287
|
|
|
256
288
|
{
|
|
@@ -258,7 +290,7 @@ export class RemoteWatcher {
|
|
|
258
290
|
for (let node of nodes) {
|
|
259
291
|
let r = node.range;
|
|
260
292
|
if (r.start > curStart) {
|
|
261
|
-
console.warn(`There is a gap in the range of nodes for path ${
|
|
293
|
+
console.warn(`There is a gap in the range of nodes for path ${originalPath} values from from ${curStart} to ${r.start} cannot be found in any nodes.`);
|
|
262
294
|
curStart = r.start;
|
|
263
295
|
}
|
|
264
296
|
let nextEnd = Math.min(r.end, rangeEnd);
|
|
@@ -285,15 +317,16 @@ export class RemoteWatcher {
|
|
|
285
317
|
}
|
|
286
318
|
}
|
|
287
319
|
if (!fullRangeCovered) {
|
|
288
|
-
if (!this.disconnectedParents.has(
|
|
320
|
+
if (!this.disconnectedParents.has(originalPath)) {
|
|
289
321
|
newDisconnectParents++;
|
|
290
|
-
this.disconnectedParents.add(
|
|
322
|
+
this.disconnectedParents.add(originalPath);
|
|
291
323
|
}
|
|
292
324
|
} else {
|
|
293
|
-
this.disconnectedParents.delete(
|
|
325
|
+
this.disconnectedParents.delete(originalPath);
|
|
294
326
|
foundParentPaths++;
|
|
295
327
|
}
|
|
296
328
|
|
|
329
|
+
// Unwatch nodes that have changed
|
|
297
330
|
if (watchObj) {
|
|
298
331
|
for (let nodeId of watchObj.nodesId.keys()) {
|
|
299
332
|
if (!nodesToAdd.has(nodeId)) {
|
|
@@ -302,28 +335,29 @@ export class RemoteWatcher {
|
|
|
302
335
|
parentPaths = new Set();
|
|
303
336
|
unwatchParentsPerAuthority.set(nodeId, parentPaths);
|
|
304
337
|
}
|
|
305
|
-
parentPaths.add(
|
|
338
|
+
parentPaths.add(watchedPath);
|
|
306
339
|
watchObj.nodesId.delete(nodeId);
|
|
307
340
|
}
|
|
308
341
|
}
|
|
309
342
|
}
|
|
310
343
|
|
|
311
344
|
if (!watchObj) {
|
|
312
|
-
watchObj = { nodesId: new Map(), fullNodeCount: 0, suppressedWatches: new Set() };
|
|
313
|
-
this.remoteWatchParents.set(
|
|
345
|
+
watchObj = { nodesId: new Map(), fullNodeCount: 0, suppressedWatches: new Set(), watchedPath, useFullPathHash };
|
|
346
|
+
this.remoteWatchParents.set(watchedPath, watchObj);
|
|
314
347
|
}
|
|
315
348
|
watchObj.fullNodeCount = nodes.length;
|
|
316
349
|
for (let [nodeId, range] of nodesToAdd) {
|
|
317
350
|
watchObj.nodesId.set(nodeId, range);
|
|
318
351
|
}
|
|
319
352
|
|
|
353
|
+
// NOTE: We watch every node with the full request. If our nodes are sharded well, this should be fine. They won't have that much data to give us, and we'll ignore it when we receive it if it's more than we want.
|
|
320
354
|
for (let authorityId of nodesToAdd.keys()) {
|
|
321
355
|
let paths = watchesPerAuthority.get(authorityId);
|
|
322
356
|
if (!paths) {
|
|
323
357
|
paths = { paths: [], parentPaths: [] };
|
|
324
358
|
watchesPerAuthority.set(authorityId, paths);
|
|
325
359
|
}
|
|
326
|
-
paths.parentPaths.push(
|
|
360
|
+
paths.parentPaths.push(watchedPath);
|
|
327
361
|
}
|
|
328
362
|
}
|
|
329
363
|
}, `watchLatest|getSingleReadNode`);
|
|
@@ -351,22 +385,12 @@ export class RemoteWatcher {
|
|
|
351
385
|
// Only remote watch remotes!
|
|
352
386
|
if (isOwnNodeId(authorityId)) continue;
|
|
353
387
|
|
|
354
|
-
if (RemoteWatcher.DEBUG) {
|
|
355
|
-
console.log(`${blue("Watching")} ${paths.length} paths and ${parentPaths.length} parent paths on ${authorityId}\n\t authority: ${blue(nodePathAuthority.getAuthorityPaths(authorityId)?.map(x => nodePathAuthority.getArchiveDirectory(x)).join(" | ") || "")}`);
|
|
356
|
-
if (parentPaths.length > 0) {
|
|
357
|
-
console.log(`\t parentPaths:`);
|
|
358
|
-
for (let path of parentPaths) {
|
|
359
|
-
console.log(`\t ${path}`);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
|
|
364
388
|
if (isDevDebugbreak()) {
|
|
365
389
|
for (let path of paths) {
|
|
366
390
|
auditLog("remoteWatcher outer WATCH", { path, remoteNodeId: authorityId });
|
|
367
391
|
}
|
|
368
392
|
for (let path of parentPaths) {
|
|
369
|
-
auditLog("remoteWatcher outer WATCH
|
|
393
|
+
auditLog("remoteWatcher outer PARENT WATCH", { path, remoteNodeId: authorityId });
|
|
370
394
|
}
|
|
371
395
|
}
|
|
372
396
|
|
|
@@ -398,31 +422,10 @@ export class RemoteWatcher {
|
|
|
398
422
|
}
|
|
399
423
|
}
|
|
400
424
|
|
|
401
|
-
public getChildPatchWatchChecker(config: {
|
|
402
|
-
parentPath: string;
|
|
403
|
-
nodeId: string;
|
|
404
|
-
}): {
|
|
405
|
-
isWatchedByNodeId(childPath: string): boolean;
|
|
406
|
-
} {
|
|
407
|
-
let remoteNodes = this.remoteWatchParents.get(config.parentPath);
|
|
408
|
-
if (!remoteNodes) return { isWatchedByNodeId: () => false };
|
|
409
|
-
const range = remoteNodes.nodesId.get(config.nodeId);
|
|
410
|
-
if (!range) return { isWatchedByNodeId: () => false };
|
|
411
|
-
return {
|
|
412
|
-
isWatchedByNodeId(childPath) {
|
|
413
|
-
return matchesParentRangeFilter({
|
|
414
|
-
parentPath: config.parentPath,
|
|
415
|
-
fullPath: childPath,
|
|
416
|
-
start: range.start,
|
|
417
|
-
end: range.end,
|
|
418
|
-
});
|
|
419
|
-
},
|
|
420
|
-
};
|
|
421
|
-
}
|
|
422
|
-
|
|
423
425
|
private watchLatestRemote = batchFunction(
|
|
424
426
|
{ delay: 10, throttleWindow: isNode() ? 500 : undefined },
|
|
425
427
|
async (batched: (WatchConfig & { authorityId: string; debugName?: string })[]) => {
|
|
428
|
+
const { Querysub } = await import("../4-querysub/Querysub");
|
|
426
429
|
await this.watchUnwatchSerial("watchLatestRemote", async () => {
|
|
427
430
|
let byAuthority = new Map<string, { paths: Set<string>; parentPaths: Set<string>; }>();
|
|
428
431
|
for (let { authorityId, paths, parentPaths } of batched) {
|
|
@@ -457,6 +460,7 @@ export class RemoteWatcher {
|
|
|
457
460
|
let config: WatchConfig = {
|
|
458
461
|
paths: Array.from(paths),
|
|
459
462
|
parentPaths: Array.from(parentPaths),
|
|
463
|
+
fullHistory: Querysub.SEND_FULL_HISTORY_ON_INITIAL_SYNC,
|
|
460
464
|
};
|
|
461
465
|
if (isDebugLogEnabled()) {
|
|
462
466
|
for (let path of paths) {
|
|
@@ -472,8 +476,7 @@ export class RemoteWatcher {
|
|
|
472
476
|
}
|
|
473
477
|
);
|
|
474
478
|
|
|
475
|
-
/**
|
|
476
|
-
|
|
479
|
+
/** @deprecated Call pathWatcher.unwatchPath instead. */
|
|
477
480
|
public unwatchLatest(config: WatchConfig) {
|
|
478
481
|
// IMPORTANT! This serial is important, otherwise there is a race condition between starting to unwatch,
|
|
479
482
|
// and unwatching, which can result in marking a path as unwatched when it is watched.
|
|
@@ -573,13 +576,16 @@ export class RemoteWatcher {
|
|
|
573
576
|
let parentWatchObj = this.remoteWatchParents.get(parentPath);
|
|
574
577
|
if (parentWatchObj) {
|
|
575
578
|
for (let [nodeId, obj] of parentWatchObj.nodesId) {
|
|
576
|
-
if (
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
}
|
|
582
|
-
|
|
579
|
+
if (parentWatchObj.useFullPathHash) {
|
|
580
|
+
let route = PathRouter.getSingleKeyRoute(path);
|
|
581
|
+
if (obj.start <= route && route < obj.end) {
|
|
582
|
+
return nodeId;
|
|
583
|
+
}
|
|
584
|
+
} else {
|
|
585
|
+
let route = PathRouter.getRouteChildKey(path);
|
|
586
|
+
if (obj.start <= route && route < obj.end) {
|
|
587
|
+
return nodeId;
|
|
588
|
+
}
|
|
583
589
|
}
|
|
584
590
|
}
|
|
585
591
|
// NOTE: I think it is a bug if we hit here. There shouldn't be a parent, but have us not match it?
|
|
@@ -591,8 +597,10 @@ export class RemoteWatcher {
|
|
|
591
597
|
return Array.from(new Set(this.remoteWatchPaths.values()));
|
|
592
598
|
}
|
|
593
599
|
|
|
600
|
+
|
|
601
|
+
|
|
594
602
|
public async refreshAllWatches(authorityNodeId: string) {
|
|
595
|
-
await
|
|
603
|
+
await authorityLookup.startSyncing();
|
|
596
604
|
await this.watchUnwatchSerial("refreshAllWatches", async () => {
|
|
597
605
|
// Unwatch all paths, reset all remoteWatchPaths, and then watch all paths again SYNCHRONOUSLY,
|
|
598
606
|
// so there is no time we aren't watching the paths.
|
|
@@ -640,8 +648,8 @@ export class RemoteWatcher {
|
|
|
640
648
|
|
|
641
649
|
export const remoteWatcher = new RemoteWatcher();
|
|
642
650
|
(globalThis as any).remoteWatcher = remoteWatcher;
|
|
643
|
-
|
|
644
|
-
pathWatcher.
|
|
651
|
+
void Promise.resolve().finally(() => {
|
|
652
|
+
pathWatcher.setUnwatchCallback(config => remoteWatcher.unwatchLatest(config));
|
|
653
|
+
pathWatcher.setWatchRemoteCallback(config => remoteWatcher.watchLatest(config));
|
|
645
654
|
authorityStorage.setGetMultiNodesForParent(path => remoteWatcher.getMultiNodesForParent(path));
|
|
646
|
-
pathValueCommitter.setGetRemoteWatchNodeId(path => remoteWatcher.getExistingWatchRemoteNodeId(path));
|
|
647
655
|
});
|