querysub 0.402.0 → 0.404.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/.cursorrules +2 -0
  2. package/bin/audit-imports.js +4 -0
  3. package/package.json +7 -4
  4. package/spec.txt +77 -0
  5. package/src/-a-archives/archiveCache.ts +9 -4
  6. package/src/-a-archives/archivesBackBlaze.ts +1039 -1039
  7. package/src/-a-auth/certs.ts +0 -12
  8. package/src/-c-identity/IdentityController.ts +12 -3
  9. package/src/-f-node-discovery/NodeDiscovery.ts +32 -26
  10. package/src/-g-core-values/NodeCapabilities.ts +12 -2
  11. package/src/0-path-value-core/AuthorityLookup.ts +239 -0
  12. package/src/0-path-value-core/LockWatcher2.ts +150 -0
  13. package/src/0-path-value-core/PathRouter.ts +535 -0
  14. package/src/0-path-value-core/PathRouterRouteOverride.ts +72 -0
  15. package/src/0-path-value-core/PathRouterServerAuthoritySpec.tsx +65 -0
  16. package/src/0-path-value-core/PathValueCommitter.ts +222 -488
  17. package/src/0-path-value-core/PathValueController.ts +277 -239
  18. package/src/0-path-value-core/PathWatcher.ts +534 -0
  19. package/src/0-path-value-core/ShardPrefixes.ts +31 -0
  20. package/src/0-path-value-core/ValidStateComputer.ts +303 -0
  21. package/src/0-path-value-core/archiveLocks/ArchiveLocks.ts +1 -1
  22. package/src/0-path-value-core/archiveLocks/ArchiveLocks2.ts +80 -44
  23. package/src/0-path-value-core/archiveLocks/archiveSnapshots.ts +13 -16
  24. package/src/0-path-value-core/auditLogs.ts +2 -0
  25. package/src/0-path-value-core/hackedPackedPathParentFiltering.ts +97 -0
  26. package/src/0-path-value-core/pathValueArchives.ts +490 -492
  27. package/src/0-path-value-core/pathValueCore.ts +195 -1492
  28. package/src/0-path-value-core/startupAuthority.ts +74 -0
  29. package/src/1-path-client/RemoteWatcher.ts +100 -83
  30. package/src/1-path-client/pathValueClientWatcher.ts +808 -815
  31. package/src/2-proxy/PathValueProxyWatcher.ts +10 -8
  32. package/src/2-proxy/archiveMoveHarness.ts +182 -214
  33. package/src/2-proxy/garbageCollection.ts +9 -8
  34. package/src/2-proxy/schema2.ts +21 -1
  35. package/src/3-path-functions/PathFunctionHelpers.ts +206 -180
  36. package/src/3-path-functions/PathFunctionRunner.ts +943 -766
  37. package/src/3-path-functions/PathFunctionRunnerMain.ts +5 -3
  38. package/src/3-path-functions/pathFunctionLoader.ts +2 -2
  39. package/src/3-path-functions/syncSchema.ts +592 -521
  40. package/src/4-deploy/deployFunctions.ts +19 -4
  41. package/src/4-deploy/deployGetFunctionsInner.ts +8 -2
  42. package/src/4-deploy/deployMain.ts +51 -68
  43. package/src/4-deploy/edgeClientWatcher.tsx +1 -1
  44. package/src/4-deploy/edgeNodes.ts +2 -2
  45. package/src/4-dom/qreact.tsx +2 -4
  46. package/src/4-dom/qreactTest.tsx +7 -13
  47. package/src/4-querysub/Querysub.ts +21 -8
  48. package/src/4-querysub/QuerysubController.ts +45 -29
  49. package/src/4-querysub/permissions.ts +2 -2
  50. package/src/4-querysub/querysubPrediction.ts +80 -70
  51. package/src/4-querysub/schemaHelpers.ts +5 -1
  52. package/src/5-diagnostics/GenericFormat.tsx +14 -9
  53. package/src/archiveapps/archiveGCEntry.tsx +9 -2
  54. package/src/archiveapps/archiveJoinEntry.ts +87 -84
  55. package/src/archiveapps/archiveMergeEntry.tsx +2 -0
  56. package/src/bits.ts +19 -0
  57. package/src/config.ts +21 -3
  58. package/src/config2.ts +23 -48
  59. package/src/deployManager/components/DeployPage.tsx +7 -3
  60. package/src/deployManager/machineSchema.ts +4 -1
  61. package/src/diagnostics/ActionsHistory.ts +3 -8
  62. package/src/diagnostics/AuditLogPage.tsx +2 -3
  63. package/src/diagnostics/FunctionCallInfo.tsx +141 -0
  64. package/src/diagnostics/FunctionCallInfoState.ts +162 -0
  65. package/src/diagnostics/MachineThreadInfo.tsx +1 -1
  66. package/src/diagnostics/NodeViewer.tsx +37 -48
  67. package/src/diagnostics/SyncTestPage.tsx +241 -0
  68. package/src/diagnostics/auditImportViolations.ts +185 -0
  69. package/src/diagnostics/listenOnDebugger.ts +3 -3
  70. package/src/diagnostics/logs/IndexedLogs/BufferUnitSet.ts +10 -4
  71. package/src/diagnostics/logs/IndexedLogs/IndexedLogs.ts +2 -2
  72. package/src/diagnostics/logs/IndexedLogs/LogViewer3.tsx +24 -22
  73. package/src/diagnostics/logs/IndexedLogs/moveIndexLogsToPublic.ts +1 -1
  74. package/src/diagnostics/logs/diskLogGlobalContext.ts +1 -0
  75. package/src/diagnostics/logs/errorNotifications2/logWatcher.ts +1 -3
  76. package/src/diagnostics/logs/lifeCycleAnalysis/LifeCycleEntryEditor.tsx +39 -17
  77. package/src/diagnostics/logs/lifeCycleAnalysis/LifeCycleEntryReadMode.tsx +4 -6
  78. package/src/diagnostics/logs/lifeCycleAnalysis/LifeCycleInstanceTableView.tsx +36 -5
  79. package/src/diagnostics/logs/lifeCycleAnalysis/LifeCyclePage.tsx +19 -5
  80. package/src/diagnostics/logs/lifeCycleAnalysis/LifeCycleRenderer.tsx +15 -7
  81. package/src/diagnostics/logs/lifeCycleAnalysis/NestedLifeCycleInfo.tsx +28 -106
  82. package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycleMatching.ts +2 -0
  83. package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycleMisc.ts +0 -0
  84. package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycleSearch.tsx +18 -7
  85. package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycles.tsx +3 -0
  86. package/src/diagnostics/managementPages.tsx +10 -3
  87. package/src/diagnostics/misc-pages/ArchiveViewer.tsx +20 -26
  88. package/src/diagnostics/misc-pages/ArchiveViewerTree.tsx +6 -4
  89. package/src/diagnostics/misc-pages/ComponentSyncStats.tsx +2 -2
  90. package/src/diagnostics/misc-pages/LocalWatchViewer.tsx +7 -9
  91. package/src/diagnostics/misc-pages/SnapshotViewer.tsx +23 -12
  92. package/src/diagnostics/misc-pages/archiveViewerShared.tsx +1 -1
  93. package/src/diagnostics/pathAuditer.ts +486 -0
  94. package/src/diagnostics/pathAuditerCallback.ts +20 -0
  95. package/src/diagnostics/watchdog.ts +8 -1
  96. package/src/library-components/URLParam.ts +1 -1
  97. package/src/misc/hash.ts +1 -0
  98. package/src/path.ts +21 -7
  99. package/src/server.ts +54 -47
  100. package/src/user-implementation/loginEmail.tsx +1 -1
  101. package/tempnotes.txt +67 -0
  102. package/test.ts +288 -95
  103. package/src/0-path-value-core/NodePathAuthorities.ts +0 -1057
  104. package/src/0-path-value-core/PathController.ts +0 -1
  105. package/src/5-diagnostics/diskValueAudit.ts +0 -218
  106. package/src/5-diagnostics/memoryValueAudit.ts +0 -438
  107. 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,27 +1,31 @@
1
1
 
2
2
  import { SocketFunction } from "socket-function/SocketFunction";
3
- import { batchFunction, delay, runInSerial, runInfinitePoll } from "socket-function/src/batching";
3
+ import { batchFunction, runInSerial, runInfinitePoll } from "socket-function/src/batching";
4
4
  import { cache } from "socket-function/src/caching";
5
- import { isNode, nextId } from "socket-function/src/misc";
5
+ import { isNode } from "socket-function/src/misc";
6
6
  import { measureFnc, measureBlock } from "socket-function/src/profiling/measure";
7
- import { isOwnNodeId, getOwnNodeId } from "../-f-node-discovery/NodeDiscovery";
8
- import { LOCAL_DOMAIN_PATH, nodePathAuthority, pathValueAuthority2 } from "../0-path-value-core/NodePathAuthorities";
9
- import { PathValueController, pathValueCommitter } from "../0-path-value-core/PathValueController";
10
- import { WatchConfig, authorityStorage, decodeParentFilter, matchesParentRangeFilter, pathWatcher } from "../0-path-value-core/pathValueCore";
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 { ClientWatcher } from "./pathValueClientWatcher";
15
- import { blue, green, yellow } from "socket-function/src/formatting/logColors";
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
- import { auditLog } from "../0-path-value-core/auditLogs";
19
+ import { auditLog, isDebugLogEnabled } from "../0-path-value-core/auditLogs";
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"));
21
24
 
22
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.
23
26
  const STOP_KEYS_DOUBLE_SENDS = true;
24
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).
25
29
  export class RemoteWatcher {
26
30
  public static DEBUG = false;
27
31
 
@@ -31,7 +35,11 @@ export class RemoteWatcher {
31
35
  // path => nodeId
32
36
  // isOwnNodeId(nodeId) means we are watching it locally
33
37
  private remoteWatchPaths = registerResource("paths|remoteWatchPaths", new Map<string, string>());
38
+ // inputPath =>
34
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;
35
43
  nodesId: Map<string, {
36
44
  start: number;
37
45
  end: number;
@@ -115,12 +123,13 @@ export class RemoteWatcher {
115
123
  public debugIsWatchingPath(path: string) {
116
124
  // HACK: If there is no read node... pretend we are watching it. Because... for now, this is a known
117
125
  // and the code calling this function isn't looking for that specific issue.
118
- return this.remoteWatchPaths.has(path) || !pathValueAuthority2.getSingleReadNodeSync(path);
126
+ return this.remoteWatchPaths.has(path) || !PathRouter.getReadyAuthority(path);
119
127
  }
120
128
  public debugIsWatchingParentPath(path: string) {
121
- return this.remoteWatchParents.has(path) || !pathValueAuthority2.getSingleReadNodeSync(path);
129
+ return this.remoteWatchParents.has(path) || !PathRouter.getReadyAuthority(path);
122
130
  }
123
131
 
132
+ /** @deprecated If you want to watch something, call pathWatcher.watchPath instead. */
124
133
  public watchLatest(config: WatchConfig & { debugName?: string }) {
125
134
  logErrors(this.watchLatestPromise(config));
126
135
  }
@@ -130,10 +139,9 @@ export class RemoteWatcher {
130
139
  // 2) If it errors out, they can't do anything to fix the error
131
140
  // 3) We retry internally anyways
132
141
  private watchLatestPromise(config: WatchConfig & { debugName?: string }) {
133
- // NOTE: If none of the values are remote... early out. This has been profiled to save a bit of time,
134
- // 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.
135
143
  if (config.parentPaths.length === 0) {
136
- let isSelf = measureBlock(() => config.paths.every(x => pathValueAuthority2.isSelfAuthority(x)), "RemoteWatcher()|watchLatestPromise|isSelfCheck");
144
+ let isSelf = measureBlock(() => config.paths.every(x => PathRouter.isSelfAuthority(x)), "RemoteWatcher()|watchLatestPromise|isSelfCheck");
137
145
  if (isSelf) {
138
146
  return;
139
147
  }
@@ -154,7 +162,7 @@ export class RemoteWatcher {
154
162
  /** NOTE: We dedupe duplicate watches in watchLatest. */
155
163
  private async watchLatestBase(config: WatchConfig & { debugName?: string }) {
156
164
  await this.watchUnwatchSerial("watchLatestBase", async () => {
157
- await pathValueAuthority2.waitUntilRoutingIsReady();
165
+ await authorityLookup.startSyncing();
158
166
 
159
167
  this.internalWatchLatest(config);
160
168
  });
@@ -180,7 +188,7 @@ export class RemoteWatcher {
180
188
  measureBlock(() => {
181
189
  for (let path of config.paths) {
182
190
  // NOTE: We weren't ignoring local paths here for a while, but... I'm pretty sure we should, to save time.
183
- if (path.startsWith(LOCAL_DOMAIN_PATH)) continue;
191
+ if (PathRouter.isLocalPath(path)) continue;
184
192
 
185
193
  // IMPORTANT! We have to await, otherwise we might see the paths are watched, skip them
186
194
  // find another path which has an authority, then go to watch that... which causes
@@ -200,10 +208,11 @@ export class RemoteWatcher {
200
208
  continue;
201
209
  }
202
210
 
203
- let authorityId = pathValueAuthority2.getSingleReadNodeSync(path);
211
+ let authorityId = PathRouter.getReadyAuthority(path)?.nodeId;
204
212
  if (!authorityId) {
205
213
  if (totalMissingPaths === 0) {
206
- let authorities = pathValueAuthority2.debug_getAllAuthorities();
214
+ let authorities = authorityLookup.getTopologySync();
215
+ authorityId = PathRouter.getReadyAuthority(path)?.nodeId;
207
216
  console.warn(`Missing authority for path ${path}`, { authorities });
208
217
  }
209
218
  totalMissingPaths++;
@@ -227,29 +236,53 @@ export class RemoteWatcher {
227
236
  paths.paths.push(path);
228
237
  }
229
238
 
230
- for (let path of config.parentPaths) {
239
+ for (let originalPath of config.parentPaths) {
231
240
  // NOTE: We weren't ignoring local paths here for a while, but... I'm pretty sure we should, to save time.
232
- if (path.startsWith(LOCAL_DOMAIN_PATH)) continue;
233
- let watchObj = this.remoteWatchParents.get(path);
241
+ if (PathRouter.isLocalPath(originalPath)) continue;
242
+ let watchObj = this.remoteWatchParents.get(originalPath);
234
243
  if (
235
244
  watchObj
236
245
  && watchObj.nodesId.size === watchObj.fullNodeCount
237
246
  && !config.tryReconnect
238
247
  ) continue;
239
248
 
240
- let { nodes } = pathValueAuthority2.getChildReadNodes(path, {
249
+ let { useFullPathHash, nodes } = PathRouter.getChildReadNodes(originalPath, {
241
250
  // Pass existing connected node ids as preferred
242
- preferredNodeIds: watchObj?.nodesId
251
+ preferredNodeIds: watchObj && Array.from(watchObj.nodesId.keys())
243
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
+ }
244
278
 
245
279
  let rangeStart = 0;
246
280
  let rangeEnd = 1;
247
281
 
248
282
  let nodesToAdd = new Map<string, { start: number; end: number }>();
249
- let range = decodeParentFilter(path);
250
- if (range) {
251
- rangeStart = range.start;
252
- rangeEnd = range.end;
283
+ if (decodedParentFilter) {
284
+ rangeStart = decodedParentFilter.start;
285
+ rangeEnd = decodedParentFilter.end;
253
286
  }
254
287
 
255
288
  {
@@ -257,7 +290,7 @@ export class RemoteWatcher {
257
290
  for (let node of nodes) {
258
291
  let r = node.range;
259
292
  if (r.start > curStart) {
260
- console.warn(`There is a gap in the range of nodes for path ${path} values from from ${curStart} to ${r.start} cannot be found in any nodes.`);
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.`);
261
294
  curStart = r.start;
262
295
  }
263
296
  let nextEnd = Math.min(r.end, rangeEnd);
@@ -284,15 +317,16 @@ export class RemoteWatcher {
284
317
  }
285
318
  }
286
319
  if (!fullRangeCovered) {
287
- if (!this.disconnectedParents.has(path)) {
320
+ if (!this.disconnectedParents.has(originalPath)) {
288
321
  newDisconnectParents++;
289
- this.disconnectedParents.add(path);
322
+ this.disconnectedParents.add(originalPath);
290
323
  }
291
324
  } else {
292
- this.disconnectedParents.delete(path);
325
+ this.disconnectedParents.delete(originalPath);
293
326
  foundParentPaths++;
294
327
  }
295
328
 
329
+ // Unwatch nodes that have changed
296
330
  if (watchObj) {
297
331
  for (let nodeId of watchObj.nodesId.keys()) {
298
332
  if (!nodesToAdd.has(nodeId)) {
@@ -301,28 +335,29 @@ export class RemoteWatcher {
301
335
  parentPaths = new Set();
302
336
  unwatchParentsPerAuthority.set(nodeId, parentPaths);
303
337
  }
304
- parentPaths.add(path);
338
+ parentPaths.add(watchedPath);
305
339
  watchObj.nodesId.delete(nodeId);
306
340
  }
307
341
  }
308
342
  }
309
343
 
310
344
  if (!watchObj) {
311
- watchObj = { nodesId: new Map(), fullNodeCount: 0, suppressedWatches: new Set() };
312
- this.remoteWatchParents.set(path, watchObj);
345
+ watchObj = { nodesId: new Map(), fullNodeCount: 0, suppressedWatches: new Set(), watchedPath, useFullPathHash };
346
+ this.remoteWatchParents.set(watchedPath, watchObj);
313
347
  }
314
348
  watchObj.fullNodeCount = nodes.length;
315
349
  for (let [nodeId, range] of nodesToAdd) {
316
350
  watchObj.nodesId.set(nodeId, range);
317
351
  }
318
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.
319
354
  for (let authorityId of nodesToAdd.keys()) {
320
355
  let paths = watchesPerAuthority.get(authorityId);
321
356
  if (!paths) {
322
357
  paths = { paths: [], parentPaths: [] };
323
358
  watchesPerAuthority.set(authorityId, paths);
324
359
  }
325
- paths.parentPaths.push(path);
360
+ paths.parentPaths.push(watchedPath);
326
361
  }
327
362
  }
328
363
  }, `watchLatest|getSingleReadNode`);
@@ -350,22 +385,12 @@ export class RemoteWatcher {
350
385
  // Only remote watch remotes!
351
386
  if (isOwnNodeId(authorityId)) continue;
352
387
 
353
- if (RemoteWatcher.DEBUG) {
354
- 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(" | ") || "")}`);
355
- if (parentPaths.length > 0) {
356
- console.log(`\t parentPaths:`);
357
- for (let path of parentPaths) {
358
- console.log(`\t ${path}`);
359
- }
360
- }
361
- }
362
-
363
388
  if (isDevDebugbreak()) {
364
389
  for (let path of paths) {
365
390
  auditLog("remoteWatcher outer WATCH", { path, remoteNodeId: authorityId });
366
391
  }
367
392
  for (let path of parentPaths) {
368
- auditLog("remoteWatcher outer WATCH PARENT", { path, remoteNodeId: authorityId });
393
+ auditLog("remoteWatcher outer PARENT WATCH", { path, remoteNodeId: authorityId });
369
394
  }
370
395
  }
371
396
 
@@ -397,31 +422,10 @@ export class RemoteWatcher {
397
422
  }
398
423
  }
399
424
 
400
- public getChildPatchWatchChecker(config: {
401
- parentPath: string;
402
- nodeId: string;
403
- }): {
404
- isWatchedByNodeId(childPath: string): boolean;
405
- } {
406
- let remoteNodes = this.remoteWatchParents.get(config.parentPath);
407
- if (!remoteNodes) return { isWatchedByNodeId: () => false };
408
- const range = remoteNodes.nodesId.get(config.nodeId);
409
- if (!range) return { isWatchedByNodeId: () => false };
410
- return {
411
- isWatchedByNodeId(childPath) {
412
- return matchesParentRangeFilter({
413
- parentPath: config.parentPath,
414
- fullPath: childPath,
415
- start: range.start,
416
- end: range.end,
417
- });
418
- },
419
- };
420
- }
421
-
422
425
  private watchLatestRemote = batchFunction(
423
426
  { delay: 10, throttleWindow: isNode() ? 500 : undefined },
424
427
  async (batched: (WatchConfig & { authorityId: string; debugName?: string })[]) => {
428
+ const { Querysub } = await import("../4-querysub/Querysub");
425
429
  await this.watchUnwatchSerial("watchLatestRemote", async () => {
426
430
  let byAuthority = new Map<string, { paths: Set<string>; parentPaths: Set<string>; }>();
427
431
  for (let { authorityId, paths, parentPaths } of batched) {
@@ -456,15 +460,23 @@ export class RemoteWatcher {
456
460
  let config: WatchConfig = {
457
461
  paths: Array.from(paths),
458
462
  parentPaths: Array.from(parentPaths),
463
+ fullHistory: Querysub.SEND_FULL_HISTORY_ON_INITIAL_SYNC,
459
464
  };
465
+ if (isDebugLogEnabled()) {
466
+ for (let path of paths) {
467
+ auditLog("Asking to watch path", { path, authorityId, targetNodeThreadId: debugNodeThread(authorityId) });
468
+ }
469
+ for (let path of parentPaths) {
470
+ auditLog("Asking to watch parent path", { path, authorityId, targetNodeThreadId: debugNodeThread(authorityId) });
471
+ }
472
+ }
460
473
  logErrors(RemoteWatcher.REMOTE_WATCH_FUNCTION(config, authorityId));
461
474
  }
462
475
  });
463
476
  }
464
477
  );
465
478
 
466
- /** NOTE: We do not count the number of watches, so... only unwatch if everything is done watching a path! */
467
-
479
+ /** @deprecated Call pathWatcher.unwatchPath instead. */
468
480
  public unwatchLatest(config: WatchConfig) {
469
481
  // IMPORTANT! This serial is important, otherwise there is a race condition between starting to unwatch,
470
482
  // and unwatching, which can result in marking a path as unwatched when it is watched.
@@ -564,13 +576,16 @@ export class RemoteWatcher {
564
576
  let parentWatchObj = this.remoteWatchParents.get(parentPath);
565
577
  if (parentWatchObj) {
566
578
  for (let [nodeId, obj] of parentWatchObj.nodesId) {
567
- if (matchesParentRangeFilter({
568
- parentPath,
569
- fullPath: path,
570
- start: obj.start,
571
- end: obj.end,
572
- })) {
573
- return nodeId;
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
+ }
574
589
  }
575
590
  }
576
591
  // NOTE: I think it is a bug if we hit here. There shouldn't be a parent, but have us not match it?
@@ -582,8 +597,10 @@ export class RemoteWatcher {
582
597
  return Array.from(new Set(this.remoteWatchPaths.values()));
583
598
  }
584
599
 
600
+
601
+
585
602
  public async refreshAllWatches(authorityNodeId: string) {
586
- await pathValueAuthority2.waitUntilRoutingIsReady();
603
+ await authorityLookup.startSyncing();
587
604
  await this.watchUnwatchSerial("refreshAllWatches", async () => {
588
605
  // Unwatch all paths, reset all remoteWatchPaths, and then watch all paths again SYNCHRONOUSLY,
589
606
  // so there is no time we aren't watching the paths.
@@ -631,8 +648,8 @@ export class RemoteWatcher {
631
648
 
632
649
  export const remoteWatcher = new RemoteWatcher();
633
650
  (globalThis as any).remoteWatcher = remoteWatcher;
634
- setImmediate(() => {
635
- pathWatcher.watchUnwatched(config => remoteWatcher.unwatchLatest(config));
651
+ void Promise.resolve().finally(() => {
652
+ pathWatcher.setUnwatchCallback(config => remoteWatcher.unwatchLatest(config));
653
+ pathWatcher.setWatchRemoteCallback(config => remoteWatcher.watchLatest(config));
636
654
  authorityStorage.setGetMultiNodesForParent(path => remoteWatcher.getMultiNodesForParent(path));
637
- pathValueCommitter.setGetRemoteWatchNodeId(path => remoteWatcher.getExistingWatchRemoteNodeId(path));
638
655
  });