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.
Files changed (108) hide show
  1. package/.cursorrules +2 -0
  2. package/bin/audit-imports.js +4 -0
  3. package/bin/join.js +1 -1
  4. package/package.json +7 -4
  5. package/spec.txt +77 -0
  6. package/src/-a-archives/archiveCache.ts +9 -4
  7. package/src/-a-archives/archivesBackBlaze.ts +1039 -1039
  8. package/src/-a-auth/certs.ts +0 -12
  9. package/src/-c-identity/IdentityController.ts +12 -3
  10. package/src/-f-node-discovery/NodeDiscovery.ts +32 -26
  11. package/src/-g-core-values/NodeCapabilities.ts +12 -2
  12. package/src/0-path-value-core/AuthorityLookup.ts +239 -0
  13. package/src/0-path-value-core/LockWatcher2.ts +150 -0
  14. package/src/0-path-value-core/PathRouter.ts +543 -0
  15. package/src/0-path-value-core/PathRouterRouteOverride.ts +72 -0
  16. package/src/0-path-value-core/PathRouterServerAuthoritySpec.tsx +73 -0
  17. package/src/0-path-value-core/PathValueCommitter.ts +222 -488
  18. package/src/0-path-value-core/PathValueController.ts +277 -239
  19. package/src/0-path-value-core/PathWatcher.ts +534 -0
  20. package/src/0-path-value-core/ShardPrefixes.ts +31 -0
  21. package/src/0-path-value-core/ValidStateComputer.ts +303 -0
  22. package/src/0-path-value-core/archiveLocks/ArchiveLocks.ts +1 -1
  23. package/src/0-path-value-core/archiveLocks/ArchiveLocks2.ts +80 -44
  24. package/src/0-path-value-core/archiveLocks/archiveSnapshots.ts +13 -16
  25. package/src/0-path-value-core/auditLogs.ts +2 -0
  26. package/src/0-path-value-core/hackedPackedPathParentFiltering.ts +97 -0
  27. package/src/0-path-value-core/pathValueArchives.ts +491 -492
  28. package/src/0-path-value-core/pathValueCore.ts +195 -1496
  29. package/src/0-path-value-core/startupAuthority.ts +74 -0
  30. package/src/1-path-client/RemoteWatcher.ts +90 -82
  31. package/src/1-path-client/pathValueClientWatcher.ts +808 -815
  32. package/src/2-proxy/PathValueProxyWatcher.ts +10 -8
  33. package/src/2-proxy/archiveMoveHarness.ts +182 -214
  34. package/src/2-proxy/garbageCollection.ts +9 -8
  35. package/src/2-proxy/schema2.ts +21 -1
  36. package/src/3-path-functions/PathFunctionHelpers.ts +206 -180
  37. package/src/3-path-functions/PathFunctionRunner.ts +943 -766
  38. package/src/3-path-functions/PathFunctionRunnerMain.ts +5 -3
  39. package/src/3-path-functions/pathFunctionLoader.ts +2 -2
  40. package/src/3-path-functions/syncSchema.ts +596 -521
  41. package/src/4-deploy/deployFunctions.ts +19 -4
  42. package/src/4-deploy/deployGetFunctionsInner.ts +8 -2
  43. package/src/4-deploy/deployMain.ts +51 -68
  44. package/src/4-deploy/edgeClientWatcher.tsx +6 -1
  45. package/src/4-deploy/edgeNodes.ts +2 -2
  46. package/src/4-dom/qreact.tsx +2 -4
  47. package/src/4-dom/qreactTest.tsx +7 -13
  48. package/src/4-querysub/Querysub.ts +21 -8
  49. package/src/4-querysub/QuerysubController.ts +45 -29
  50. package/src/4-querysub/permissions.ts +2 -2
  51. package/src/4-querysub/querysubPrediction.ts +80 -70
  52. package/src/4-querysub/schemaHelpers.ts +5 -1
  53. package/src/5-diagnostics/GenericFormat.tsx +14 -9
  54. package/src/archiveapps/archiveGCEntry.tsx +9 -2
  55. package/src/archiveapps/archiveJoinEntry.ts +96 -84
  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 +34 -16
  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 +65 -0
  102. package/test.ts +298 -97
  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/archiveMergeEntry.tsx +0 -48
  108. 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, 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
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) || !pathValueAuthority2.getSingleReadNodeSync(path);
126
+ return this.remoteWatchPaths.has(path) || !PathRouter.getReadyAuthority(path);
120
127
  }
121
128
  public debugIsWatchingParentPath(path: string) {
122
- return this.remoteWatchParents.has(path) || !pathValueAuthority2.getSingleReadNodeSync(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 => pathValueAuthority2.isSelfAuthority(x)), "RemoteWatcher()|watchLatestPromise|isSelfCheck");
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 pathValueAuthority2.waitUntilRoutingIsReady();
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 (path.startsWith(LOCAL_DOMAIN_PATH)) continue;
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 = pathValueAuthority2.getSingleReadNodeSync(path);
211
+ let authorityId = PathRouter.getReadyAuthority(path)?.nodeId;
205
212
  if (!authorityId) {
206
213
  if (totalMissingPaths === 0) {
207
- let authorities = pathValueAuthority2.debug_getAllAuthorities();
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 path of config.parentPaths) {
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 (path.startsWith(LOCAL_DOMAIN_PATH)) continue;
234
- let watchObj = this.remoteWatchParents.get(path);
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 } = pathValueAuthority2.getChildReadNodes(path, {
249
+ let { useFullPathHash, nodes } = PathRouter.getChildReadNodes(originalPath, {
242
250
  // Pass existing connected node ids as preferred
243
- preferredNodeIds: watchObj?.nodesId
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
- let range = decodeParentFilter(path);
251
- if (range) {
252
- rangeStart = range.start;
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 ${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.`);
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(path)) {
320
+ if (!this.disconnectedParents.has(originalPath)) {
289
321
  newDisconnectParents++;
290
- this.disconnectedParents.add(path);
322
+ this.disconnectedParents.add(originalPath);
291
323
  }
292
324
  } else {
293
- this.disconnectedParents.delete(path);
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(path);
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(path, watchObj);
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(path);
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 PARENT", { path, remoteNodeId: authorityId });
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
- /** NOTE: We do not count the number of watches, so... only unwatch if everything is done watching a path! */
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 (matchesParentRangeFilter({
577
- parentPath,
578
- fullPath: path,
579
- start: obj.start,
580
- end: obj.end,
581
- })) {
582
- 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
+ }
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 pathValueAuthority2.waitUntilRoutingIsReady();
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
- setImmediate(() => {
644
- 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));
645
654
  authorityStorage.setGetMultiNodesForParent(path => remoteWatcher.getMultiNodesForParent(path));
646
- pathValueCommitter.setGetRemoteWatchNodeId(path => remoteWatcher.getExistingWatchRemoteNodeId(path));
647
655
  });