querysub 0.405.0 → 0.407.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 (31) hide show
  1. package/bin/deploy-prefixes.js +7 -0
  2. package/package.json +2 -1
  3. package/src/-f-node-discovery/NodeDiscovery.ts +0 -2
  4. package/src/0-path-value-core/AuthorityLookup.ts +7 -2
  5. package/src/0-path-value-core/PathRouter.ts +170 -84
  6. package/src/0-path-value-core/PathRouterRouteOverride.ts +2 -2
  7. package/src/0-path-value-core/PathRouterServerAuthoritySpec.tsx +2 -8
  8. package/src/0-path-value-core/PathValueCommitter.ts +65 -30
  9. package/src/0-path-value-core/PathValueController.ts +2 -4
  10. package/src/0-path-value-core/PathWatcher.ts +7 -4
  11. package/src/0-path-value-core/ShardPrefixes.ts +4 -0
  12. package/src/0-path-value-core/hackedPackedPathParentFiltering.ts +12 -31
  13. package/src/0-path-value-core/pathValueArchives.ts +3 -3
  14. package/src/0-path-value-core/pathValueCore.ts +32 -24
  15. package/src/0-path-value-core/startupAuthority.ts +9 -9
  16. package/src/1-path-client/RemoteWatcher.ts +188 -170
  17. package/src/1-path-client/pathValueClientWatcher.ts +6 -11
  18. package/src/2-proxy/garbageCollection.ts +4 -3
  19. package/src/2-proxy/pathValueProxy.ts +2 -3
  20. package/src/3-path-functions/PathFunctionRunner.ts +3 -1
  21. package/src/3-path-functions/syncSchema.ts +6 -2
  22. package/src/4-deploy/deployGetFunctionsInner.ts +1 -1
  23. package/src/4-deploy/deployPrefixes.ts +14 -0
  24. package/src/4-deploy/edgeNodes.ts +1 -1
  25. package/src/diagnostics/SyncTestPage.tsx +19 -8
  26. package/src/functional/promiseCache.ts +5 -2
  27. package/src/path.ts +15 -2
  28. package/src/rangeMath.ts +41 -0
  29. package/tempnotes.txt +12 -26
  30. package/test.ts +8 -4
  31. package/src/diagnostics/logs/BrowserLargeFileCache.ts +0 -64
@@ -2,7 +2,7 @@
2
2
  import { SocketFunction } from "socket-function/SocketFunction";
3
3
  import { batchFunction, runInSerial, runInfinitePoll } from "socket-function/src/batching";
4
4
  import { cache } from "socket-function/src/caching";
5
- import { isNode } from "socket-function/src/misc";
5
+ import { isNode, sort } from "socket-function/src/misc";
6
6
  import { measureFnc, measureBlock } from "socket-function/src/profiling/measure";
7
7
  import { isOwnNodeId } from "../-f-node-discovery/NodeDiscovery";
8
8
  import { PathValueController } from "../0-path-value-core/PathValueController";
@@ -12,14 +12,15 @@ import { pathWatcher } from "../0-path-value-core/PathWatcher";
12
12
  import { ActionsHistory } from "../diagnostics/ActionsHistory";
13
13
  import { registerResource } from "../diagnostics/trackResources";
14
14
  import { logErrors } from "../errors";
15
- import { green, yellow } from "socket-function/src/formatting/logColors";
16
- import { getParentPathStr } from "../path";
15
+ import { blue, green, yellow } from "socket-function/src/formatting/logColors";
16
+ import { getParentPathStr, hack_stripPackedPath } from "../path";
17
17
  import { isEmpty } from "../misc";
18
18
  import { isDevDebugbreak } from "../config";
19
19
  import { auditLog, isDebugLogEnabled } from "../0-path-value-core/auditLogs";
20
20
  import { debugNodeThread } from "../-c-identity/IdentityController";
21
21
  import { authorityLookup } from "../0-path-value-core/AuthorityLookup";
22
22
  import { decodeParentFilter, encodeParentFilter } from "../0-path-value-core/hackedPackedPathParentFiltering";
23
+ import { removeRange, rangesOverlap } from "../rangeMath";
23
24
  setImmediate(() => import("../4-querysub/Querysub"));
24
25
 
25
26
  // 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.
@@ -35,19 +36,25 @@ export class RemoteWatcher {
35
36
  // path => nodeId
36
37
  // isOwnNodeId(nodeId) means we are watching it locally
37
38
  private remoteWatchPaths = registerResource("paths|remoteWatchPaths", new Map<string, string>());
38
- // inputPath =>
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;
43
- nodesId: Map<string, {
39
+
40
+ // cleanPath (hack_stripPackedPath(path)) =>
41
+ private remoteWatchParents2 = registerResource("paths|remoteWatchParents", new Map<string, {
42
+ ranges: {
43
+ // inclusive
44
44
  start: number;
45
+ // exclusive
45
46
  end: number;
46
- }>;
47
- // Watches which we suppressed, due to this parent sync. If this parent sync stops,
48
- // we will need to re-send the watches.
49
- suppressedWatches: Set<string>;
50
- fullNodeCount: number;
47
+ // encodeParentFilter({ path: cleanPath, startFraction: start, endFraction: end })
48
+ finalPath: string;
49
+ // All of the requests paths that request it (pre decodeParentFilter).
50
+ inputPaths: Set<string>;
51
+
52
+ // The authorityId satisifying this request
53
+ authorityId: string;
54
+
55
+ // All the paths that have getParentPathStr(path) === cleanPath, and PathRouter.getRouteChildKey is within start and end. We suppress these, as our parent watch implicitly watches them. HOWEVER, We have to track them So we can re-watch them if the parent watch is removed.
56
+ suppressedWatches: Set<string>;
57
+ }[];
51
58
  }>());
52
59
 
53
60
  private disconnectedPaths = registerResource("paths|disconnectedPaths", new Set<string>());
@@ -69,22 +76,27 @@ export class RemoteWatcher {
69
76
  }
70
77
  }
71
78
  let parentPaths: string[] = [];
72
- for (let [path, watchObj] of this.remoteWatchParents.entries()) {
73
- if (typeof watchObj.nodesId === "string") {
74
- if (watchObj.nodesId === authorityId) {
75
- this.remoteWatchParents.delete(path);
76
- parentPaths.push(path);
79
+
80
+ // Remove all ranges from remoteWatchParents2 that matches this, adding all inputPaths to parentPaths so we can try to rewatch them again
81
+ for (let [cleanPath, watchObj] of this.remoteWatchParents2.entries()) {
82
+ let removedRanges = watchObj.ranges.filter(x => x.authorityId === authorityId);
83
+ watchObj.ranges = watchObj.ranges.filter(x => x.authorityId !== authorityId);
84
+ if (watchObj.ranges.length === 0) {
85
+ this.remoteWatchParents2.delete(cleanPath);
86
+ }
87
+ for (let range of removedRanges) {
88
+ for (let inputPath of range.inputPaths) {
89
+ parentPaths.push(inputPath);
77
90
  }
78
- } else {
79
- if (watchObj.nodesId.has(authorityId)) {
80
- watchObj.nodesId.delete(authorityId);
81
- if (isEmpty(watchObj.nodesId)) {
82
- this.remoteWatchParents.delete(path);
83
- }
84
- parentPaths.push(path);
91
+ for (let suppressedPath of range.suppressedWatches) {
92
+ if (!this.remoteWatchPaths.has(suppressedPath)) continue;
93
+ // NOTE: We shouldn't need to remove the remote watch path, as it being suppressed means it shouldn't be watched. However, I guess we can because we definitely don't want to double-watch it.
94
+ this.remoteWatchPaths.delete(suppressedPath);
95
+ paths.push(suppressedPath);
85
96
  }
86
97
  }
87
98
  }
99
+
88
100
  if (paths.length === 0 && parentPaths.length === 0) return;
89
101
 
90
102
  for (let path of paths) {
@@ -125,9 +137,6 @@ export class RemoteWatcher {
125
137
  // and the code calling this function isn't looking for that specific issue.
126
138
  return this.remoteWatchPaths.has(path) || !PathRouter.getReadyAuthority(path);
127
139
  }
128
- public debugIsWatchingParentPath(path: string) {
129
- return this.remoteWatchParents.has(path) || !PathRouter.getReadyAuthority(path);
130
- }
131
140
 
132
141
  /** @deprecated If you want to watch something, call pathWatcher.watchPath instead. */
133
142
  public watchLatest(config: WatchConfig & { debugName?: string }) {
@@ -149,10 +158,16 @@ export class RemoteWatcher {
149
158
  return this.watchLatestBase(config);
150
159
  }
151
160
 
152
- /** Returns [] if we aren't remotely syncing the path. */
153
- public getMultiNodesForParent(path: string): Map<string, unknown> | undefined {
154
- let watchObj = this.remoteWatchParents.get(path);
155
- return watchObj?.nodesId;
161
+ // As in, is this the path that we're actually watching on the remote? We remap all of the requested paths to the final paths. So, this won't be something that the client application code would understand. However, this would be what the remote server thinks we're intending to watch.
162
+ public isFinalRemoteWatchPath(config: {
163
+ parentPath: string;
164
+ nodeId: string;
165
+ }) {
166
+ let { parentPath, nodeId } = config;
167
+ let cleanPath = hack_stripPackedPath(parentPath);
168
+ let watchObj = this.remoteWatchParents2.get(cleanPath);
169
+ if (!watchObj) return false;
170
+ return watchObj.ranges.some(x => x.authorityId === nodeId && x.finalPath === parentPath);
156
171
  }
157
172
 
158
173
  private watchUnwatchSerial = runInSerial(async (name: string, fnc: () => Promise<unknown>) => {
@@ -176,7 +191,6 @@ export class RemoteWatcher {
176
191
  forceWatch?: boolean;
177
192
  }) {
178
193
  let watchesPerAuthority = new Map<string, { paths: string[]; parentPaths: string[] }>();
179
-
180
194
  let unwatchParentsPerAuthority = new Map<string, Set<string>>();
181
195
 
182
196
  let newDisconnectPaths = 0;
@@ -236,128 +250,111 @@ export class RemoteWatcher {
236
250
  paths.paths.push(path);
237
251
  }
238
252
 
239
- for (let originalPath of config.parentPaths) {
253
+ for (let path of config.parentPaths) {
240
254
  // NOTE: We weren't ignoring local paths here for a while, but... I'm pretty sure we should, to save time.
241
- if (PathRouter.isLocalPath(originalPath)) continue;
242
- let watchObj = this.remoteWatchParents.get(originalPath);
243
- if (
244
- watchObj
245
- && watchObj.nodesId.size === watchObj.fullNodeCount
246
- && !config.tryReconnect
247
- ) continue;
248
-
249
- let { useFullPathHash, nodes } = PathRouter.getChildReadNodes(originalPath, {
250
- // Pass existing connected node ids as preferred
251
- preferredNodeIds: watchObj && Array.from(watchObj.nodesId.keys())
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
- }
255
+ if (PathRouter.isLocalPath(path)) continue;
264
256
 
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
- }
257
+ // IMPORTANT! We HAVE to watch only the range we want, as... if we happen to watch more than the node has, it'll try to get it from other nodes, which is definitely not what we want.
275
258
 
276
- watchObj = undefined;
277
- }
259
+ let decodedParentFilter = decodeParentFilter(path);
260
+ let cleanPath = decodedParentFilter?.path ?? path;
278
261
 
279
262
  let rangeStart = 0;
280
263
  let rangeEnd = 1;
281
-
282
- let nodesToAdd = new Map<string, { start: number; end: number }>();
283
264
  if (decodedParentFilter) {
284
265
  rangeStart = decodedParentFilter.start;
285
266
  rangeEnd = decodedParentFilter.end;
286
267
  }
287
268
 
288
- {
289
- let curStart = 0;
290
- for (let node of nodes) {
291
- let r = node.range;
292
- if (r.start > curStart) {
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.`);
294
- curStart = r.start;
295
- }
296
- let nextEnd = Math.min(r.end, rangeEnd);
297
- // Ignore values with no overlap (mostly trailing values)
298
- if (nextEnd <= curStart) continue;
269
+ let watchObj = this.remoteWatchParents2.get(cleanPath);
299
270
 
300
- nodesToAdd.set(node.nodeId, { start: curStart, end: nextEnd });
301
- curStart = nextEnd;
271
+ if (watchObj && !config.tryReconnect) {
272
+ // If all the ranges we want are already covered, then we can just use them
273
+ let requiredRanges: { start: number; end: number }[] = [{
274
+ start: rangeStart,
275
+ end: rangeEnd,
276
+ }];
277
+ for (let range of watchObj.ranges) {
278
+ removeRange(requiredRanges, range);
302
279
  }
303
- }
304
-
305
- let fullRangeCovered = true;
306
- {
307
- let lastValue = rangeStart;
308
- for (let node of nodes) {
309
- if (node.range.start > lastValue) {
310
- fullRangeCovered = false;
311
- break;
280
+ if (requiredRanges.length === 0) {
281
+ this.disconnectedParents.delete(path);
282
+ for (let range of watchObj.ranges) {
283
+ if (rangesOverlap(range, { start: rangeStart, end: rangeEnd })) {
284
+ range.inputPaths.add(path);
285
+ }
312
286
  }
313
- lastValue = node.range.end;
314
- }
315
- if (lastValue < rangeEnd) {
316
- fullRangeCovered = false;
287
+ continue;
317
288
  }
289
+
318
290
  }
319
- if (!fullRangeCovered) {
320
- if (!this.disconnectedParents.has(originalPath)) {
291
+
292
+ let { nodes } = PathRouter.getChildReadNodes(cleanPath, {
293
+ // Pass existing connected node ids as preferred
294
+ preferredNodeIds: watchObj?.ranges.map(x => x.authorityId) || []
295
+ });
296
+ // NOTE: getChildReadNodes either returns a full range, or nothing
297
+ if (nodes.length === 0) {
298
+ if (!this.disconnectedParents.has(path)) {
321
299
  newDisconnectParents++;
322
- this.disconnectedParents.add(originalPath);
300
+ this.disconnectedParents.add(path);
323
301
  }
324
- } else {
325
- this.disconnectedParents.delete(originalPath);
326
- foundParentPaths++;
302
+ continue;
327
303
  }
304
+ this.disconnectedParents.delete(path);
305
+ foundParentPaths++;
306
+
307
+
308
+ // NOTE: If the previous call to get childrenodes returns something entirely different, that's fine. If there's a disconnect, we would have removed the ranges already, so they're probably still valid. And we passed them as preferred node IDs, so they'll probably be chosen again. But if they weren't, it's fine. We can just add new ranges for any that we happen to be missing.
309
+ // - As in, we don't need to find the difference between the previous time, we just need to find what new watches do we need to add.
328
310
 
329
- // Unwatch nodes that have changed
330
- if (watchObj) {
331
- for (let nodeId of watchObj.nodesId.keys()) {
332
- if (!nodesToAdd.has(nodeId)) {
333
- let parentPaths = unwatchParentsPerAuthority.get(nodeId);
334
- if (!parentPaths) {
335
- parentPaths = new Set();
336
- unwatchParentsPerAuthority.set(nodeId, parentPaths);
337
- }
338
- parentPaths.add(watchedPath);
339
- watchObj.nodesId.delete(nodeId);
340
- }
341
- }
342
- }
343
311
 
344
312
  if (!watchObj) {
345
- watchObj = { nodesId: new Map(), fullNodeCount: 0, suppressedWatches: new Set(), watchedPath, useFullPathHash };
346
- this.remoteWatchParents.set(watchedPath, watchObj);
313
+ watchObj = { ranges: [] };
314
+ this.remoteWatchParents2.set(cleanPath, watchObj);
347
315
  }
348
- watchObj.fullNodeCount = nodes.length;
349
- for (let [nodeId, range] of nodesToAdd) {
350
- watchObj.nodesId.set(nodeId, range);
316
+
317
+ // Compute which parts of [rangeStart, rangeEnd] aren't yet covered by existing watches
318
+ let missingRanges: { start: number; end: number }[] = [{ start: rangeStart, end: rangeEnd }];
319
+ for (let range of watchObj.ranges) {
320
+ removeRange(missingRanges, range);
351
321
  }
352
322
 
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.
354
- for (let authorityId of nodesToAdd.keys()) {
355
- let paths = watchesPerAuthority.get(authorityId);
356
- if (!paths) {
357
- paths = { paths: [], parentPaths: [] };
358
- watchesPerAuthority.set(authorityId, paths);
323
+ // For each missing sub-range, clip each node's range to exactly that sub-range and add it.
324
+ // NOTE: Nodes has no internal overlaps, and missing ranges by definition are only values that we haven't added (and itself has no overlaps). And so adding the intersection of those will also have no overlaps and only add values that we haven't added.
325
+ for (let missing of missingRanges) {
326
+ for (let node of nodes) {
327
+ let clippedStart = Math.max(node.range.start, missing.start);
328
+ let clippedEnd = Math.min(node.range.end, missing.end);
329
+ if (clippedEnd - clippedStart <= 0) continue;
330
+ let finalPath = encodeParentFilter({ path: cleanPath, startFraction: clippedStart, endFraction: clippedEnd });
331
+ let existingRange = watchObj.ranges.find(x => x.finalPath === finalPath && x.authorityId === node.nodeId);
332
+ if (!existingRange) {
333
+ existingRange = {
334
+ start: clippedStart,
335
+ end: clippedEnd,
336
+ finalPath,
337
+ inputPaths: new Set(),
338
+ authorityId: node.nodeId,
339
+ suppressedWatches: new Set(),
340
+ };
341
+ watchObj.ranges.push(existingRange);
342
+ let watchesPer = watchesPerAuthority.get(node.nodeId);
343
+ if (!watchesPer) {
344
+ watchesPer = { paths: [], parentPaths: [] };
345
+ watchesPerAuthority.set(node.nodeId, watchesPer);
346
+ }
347
+ watchesPer.parentPaths.push(finalPath);
348
+ }
349
+ existingRange.inputPaths.add(path);
350
+ }
351
+ }
352
+
353
+ // Register path on pre-existing ranges that already cover parts of [rangeStart, rangeEnd]
354
+ for (let range of watchObj.ranges) {
355
+ if (rangesOverlap(range, { start: rangeStart, end: rangeEnd })) {
356
+ range.inputPaths.add(path);
359
357
  }
360
- paths.parentPaths.push(watchedPath);
361
358
  }
362
359
  }
363
360
  }, `watchLatest|getSingleReadNode`);
@@ -395,11 +392,16 @@ export class RemoteWatcher {
395
392
  }
396
393
 
397
394
  if (STOP_KEYS_DOUBLE_SENDS) {
395
+ // NOTE: We could already be watching the value watches, in which case watching the parent watches will cause us to double watch them. This is fine. We don't need to remove all double watches. We just want to remove some.
398
396
  paths = paths.filter(path => {
399
- let parentPath = getParentPathStr(path);
400
- let parentObj = this.remoteWatchParents.get(parentPath);
401
- if (!parentObj) return true;
402
- parentObj.suppressedWatches.add(path);
397
+ let cleanParent = getParentPathStr(path);
398
+ let watchObj = this.remoteWatchParents2.get(cleanParent);
399
+ if (!watchObj) return true;
400
+ let routeKey = PathRouter.getRouteChildKey(path);
401
+ // If we're watching it from any authority, don't watch it now.
402
+ let coveringRange = watchObj.ranges.find(x => x.start <= routeKey && routeKey < x.end);
403
+ if (!coveringRange) return true;
404
+ coveringRange.suppressedWatches.add(path);
403
405
  auditLog("remoteWatcher outer WATCH SUPPRESSED", { path, remoteNodeId: authorityId });
404
406
  return false;
405
407
  });
@@ -428,6 +430,7 @@ export class RemoteWatcher {
428
430
  const { Querysub } = await import("../4-querysub/Querysub");
429
431
  await this.watchUnwatchSerial("watchLatestRemote", async () => {
430
432
  let byAuthority = new Map<string, { paths: Set<string>; parentPaths: Set<string>; }>();
433
+ // Collect them and also verify that we're still watching the same authority as we were when we started the batched call
431
434
  for (let { authorityId, paths, parentPaths } of batched) {
432
435
  let pathsPerAuthority = byAuthority.get(authorityId);
433
436
  if (!pathsPerAuthority) {
@@ -440,9 +443,10 @@ export class RemoteWatcher {
440
443
  pathsPerAuthority.paths.add(path);
441
444
  }
442
445
  for (let path of parentPaths) {
443
- let watchObj = this.remoteWatchParents.get(path);
446
+ let cleanPath = hack_stripPackedPath(path);
447
+ let watchObj = this.remoteWatchParents2.get(cleanPath);
444
448
  if (!watchObj) continue;
445
- if (!watchObj.nodesId.has(authorityId)) continue;
449
+ if (!watchObj.ranges.some(x => x.finalPath === path && x.authorityId === authorityId)) continue;
446
450
  pathsPerAuthority.parentPaths.add(path);
447
451
  }
448
452
  }
@@ -470,6 +474,7 @@ export class RemoteWatcher {
470
474
  auditLog("Asking to watch parent path", { path, authorityId, targetNodeThreadId: debugNodeThread(authorityId) });
471
475
  }
472
476
  }
477
+
473
478
  logErrors(RemoteWatcher.REMOTE_WATCH_FUNCTION(config, authorityId));
474
479
  }
475
480
  });
@@ -525,19 +530,27 @@ export class RemoteWatcher {
525
530
  }
526
531
  let suppressedRewatches = new Set<string>();
527
532
  for (let path of config.parentPaths) {
528
- let watchObj = this.remoteWatchParents.get(path);
533
+ let cleanPath = hack_stripPackedPath(path);
534
+ let watchObj = this.remoteWatchParents2.get(cleanPath);
529
535
  if (!watchObj) continue;
530
- // Rewatch anything that we suppressed watching (which happens when STOP_KEYS_DOUBLE_SENDS is set)
531
- for (let suppressedWatch of watchObj.suppressedWatches) {
532
- // Only if we are still watching it (which most of the time, we won't be)
533
- if (!this.remoteWatchPaths.has(suppressedWatch)) continue;
534
- suppressedRewatches.add(suppressedWatch);
535
- auditLog("remoteWatcher inner SUPPRESSED rewatched", { path: suppressedWatch });
536
+ for (let range of watchObj.ranges) {
537
+ range.inputPaths.delete(path);
536
538
  }
537
- for (let authorityId of watchObj.nodesId.keys()) {
538
- innerParentPathRemove(path, authorityId);
539
+ let removedRanges = watchObj.ranges.filter(x => x.inputPaths.size === 0);
540
+ watchObj.ranges = watchObj.ranges.filter(x => x.inputPaths.size > 0);
541
+ if (watchObj.ranges.length === 0) {
542
+ this.remoteWatchParents2.delete(cleanPath);
543
+ }
544
+ for (let removedRange of removedRanges) {
545
+ innerParentPathRemove(removedRange.finalPath, removedRange.authorityId);
546
+ // Rewatch anything that we suppressed watching (which happens when STOP_KEYS_DOUBLE_SENDS is set)
547
+ for (let suppressedWatch of removedRange.suppressedWatches) {
548
+ // Only if we are still watching it (which most of the time, we won't be)
549
+ if (!this.remoteWatchPaths.has(suppressedWatch)) continue;
550
+ suppressedRewatches.add(suppressedWatch);
551
+ auditLog("remoteWatcher inner SUPPRESSED rewatched", { path: suppressedWatch });
552
+ }
539
553
  }
540
- this.remoteWatchParents.delete(path);
541
554
  }
542
555
  if (suppressedRewatches.size > 0) {
543
556
  this.internalWatchLatest({
@@ -573,26 +586,22 @@ export class RemoteWatcher {
573
586
  @measureFnc
574
587
  public getExistingWatchRemoteNodeId(path: string): string | undefined {
575
588
  let parentPath = getParentPathStr(path);
576
- let parentWatchObj = this.remoteWatchParents.get(parentPath);
589
+ let parentWatchObj = this.remoteWatchParents2.get(parentPath);
577
590
  if (parentWatchObj) {
578
- for (let [nodeId, obj] of parentWatchObj.nodesId) {
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
- }
589
- }
591
+ let route = PathRouter.getRouteChildKey(path);
592
+ let range = parentWatchObj.ranges.find(x => x.start <= route && route < x.end);
593
+ if (range) {
594
+ return range.authorityId;
590
595
  }
591
596
  // NOTE: I think it is a bug if we hit here. There shouldn't be a parent, but have us not match it?
592
- // But... I also don't see the harm in checking remoteWatchPaths?
597
+ // But... I also don't see the harm in checking remoteWatchPaths
593
598
  }
594
599
  return this.remoteWatchPaths.get(path);
595
600
  }
601
+ public getValueWatchRemoteNodeId(path: string): string | undefined {
602
+ return this.remoteWatchPaths.get(path);
603
+ }
604
+
596
605
  public getAllRemoteWatchedNodeIds() {
597
606
  return Array.from(new Set(this.remoteWatchPaths.values()));
598
607
  }
@@ -611,7 +620,6 @@ export class RemoteWatcher {
611
620
  // cases where an authority is in a broken state.
612
621
 
613
622
  let pathsToWatch = new Set<string>();
614
- let parentPathsToWatch = new Set<string>();
615
623
  for (let [path, nodeId] of this.remoteWatchPaths) {
616
624
  if (nodeId === authorityNodeId) {
617
625
  pathsToWatch.add(path);
@@ -621,19 +629,30 @@ export class RemoteWatcher {
621
629
  for (let path of pathsToWatch) {
622
630
  this.remoteWatchPaths.delete(path);
623
631
  }
624
- for (let [path, watchObj] of this.remoteWatchParents) {
625
- if (watchObj.nodesId.has(authorityNodeId)) {
626
- watchObj.nodesId.delete(authorityNodeId);
627
- parentPathsToWatch.add(path);
632
+ let parentRemotePathsToUnwatch = new Set<string>();
633
+ let parentPathsToRewatch = new Set<string>();
634
+ for (let [cleanPath, watchObj] of this.remoteWatchParents2) {
635
+ let ranges = watchObj.ranges.filter(x => x.authorityId === authorityNodeId);
636
+ watchObj.ranges = watchObj.ranges.filter(x => x.authorityId !== authorityNodeId);
637
+ if (watchObj.ranges.length === 0) {
638
+ this.remoteWatchParents2.delete(cleanPath);
639
+ }
640
+ for (let range of ranges) {
641
+ for (let inputPath of range.inputPaths) {
642
+ parentPathsToRewatch.add(inputPath);
643
+ }
644
+ parentRemotePathsToUnwatch.add(range.finalPath);
628
645
  }
629
646
  }
630
647
  let paths = Array.from(pathsToWatch);
631
- let parentPaths = Array.from(parentPathsToWatch);
632
648
 
633
- logErrors(RemoteWatcher.REMOTE_UNWATCH_FUNCTION({ paths, parentPaths }, authorityNodeId));
649
+ logErrors(RemoteWatcher.REMOTE_UNWATCH_FUNCTION({
650
+ paths,
651
+ parentPaths: Array.from(parentRemotePathsToUnwatch)
652
+ }, authorityNodeId));
634
653
  this.internalWatchLatest({
635
654
  paths: Array.from(pathsToWatch),
636
- parentPaths: Array.from(parentPathsToWatch),
655
+ parentPaths: Array.from(parentPathsToRewatch),
637
656
  debugName: "refreshAllWatches",
638
657
  });
639
658
  });
@@ -641,7 +660,7 @@ export class RemoteWatcher {
641
660
 
642
661
  public getWatchReason(path: string): "direct" | "parent" | undefined {
643
662
  if (this.remoteWatchPaths.has(path)) return "direct";
644
- if (this.remoteWatchParents.has(getParentPathStr(path))) return "parent";
663
+ if (this.remoteWatchParents2.has(getParentPathStr(path))) return "parent";
645
664
  return undefined;
646
665
  }
647
666
  }
@@ -651,5 +670,4 @@ export const remoteWatcher = new RemoteWatcher();
651
670
  void Promise.resolve().finally(() => {
652
671
  pathWatcher.setUnwatchCallback(config => remoteWatcher.unwatchLatest(config));
653
672
  pathWatcher.setWatchRemoteCallback(config => remoteWatcher.watchLatest(config));
654
- authorityStorage.setGetMultiNodesForParent(path => remoteWatcher.getMultiNodesForParent(path));
655
673
  });
@@ -14,29 +14,24 @@
14
14
  - For knowing when values change
15
15
  */
16
16
 
17
- import { SocketFunction } from "socket-function/SocketFunction";
18
- import { cache, lazy } from "socket-function/src/caching";
19
- import { binarySearchIndex, isNode, recursiveFreeze, sort } from "socket-function/src/misc";
17
+ import { lazy } from "socket-function/src/caching";
18
+ import { isNode } from "socket-function/src/misc";
20
19
  import { logErrors } from "../errors";
21
20
  import { getParentPathStr, getPathFromStr, hack_stripPackedPath } from "../path";
22
21
  import { measureBlock, measureFnc } from "socket-function/src/profiling/measure";
23
- import { pathValueCommitter, PathValueController } from "../0-path-value-core/PathValueController";
22
+ import { pathValueCommitter } from "../0-path-value-core/PathValueController";
24
23
  import { PathValue, Value, getNextTime, Time, ReadLock, MAX_CHANGE_AGE, authorityStorage, getCreatorId, WatchConfig } from "../0-path-value-core/pathValueCore";
25
24
  import { pathWatcher } from "../0-path-value-core/PathWatcher";
26
25
  import { blue, green, red } from "socket-function/src/formatting/logColors";
27
- import { MaybePromise } from "socket-function/src/types";
28
- import { batchFunction, batchFunctionNone, runInfinitePoll } from "socket-function/src/batching";
26
+ import { runInfinitePoll } from "socket-function/src/batching";
29
27
  import { registerResource } from "../diagnostics/trackResources";
30
- import debugbreak from "debugbreak";
31
- import { ActionsHistory } from "../diagnostics/ActionsHistory";
32
- import { getOwnNodeId, isOwnNodeId } from "../-f-node-discovery/NodeDiscovery";
28
+ import { getOwnNodeId } from "../-f-node-discovery/NodeDiscovery";
33
29
  import { PromiseObj } from "../promise";
34
30
  import { getOwnMachineId } from "../-a-auth/certs";
35
31
  import { remoteWatcher } from "./RemoteWatcher";
36
32
  import { heapTagObj } from "../diagnostics/heapTag";
37
33
  import { isDevDebugbreak } from "../config";
38
- import { auditLog } from "../0-path-value-core/auditLogs";
39
- import { decodeParentFilter, matchesParentRangeFilter } from "../0-path-value-core/hackedPackedPathParentFiltering";
34
+ import { matchesParentRangeFilter } from "../0-path-value-core/hackedPackedPathParentFiltering";
40
35
 
41
36
  const pathValueCtor = heapTagObj("PathValue");
42
37
 
@@ -19,6 +19,7 @@ import { getStorageDir, getSubFolder } from "../fs";
19
19
  import fs from "fs";
20
20
  import { sha256 } from "js-sha256";
21
21
  import { AuthoritySpec, PathRouter } from "../0-path-value-core/PathRouter";
22
+ import { pathValueSerializer } from "../-h-path-value-serialize/PathValueSerializer";
22
23
 
23
24
 
24
25
  export interface AliveChecker<T = unknown> {
@@ -34,7 +35,7 @@ export interface AliveChecker<T = unknown> {
34
35
  * NOTE: The value might only have a single heavily nested value, not being a valid object. If the object
35
36
  * lookups broken, probably just return false, to clean up the straggling values.
36
37
  */
37
- isAlive: (value: T, path: string[]) => boolean | unknown;
38
+ isAlive: (value: T, path: string[], valuePath: PathValue | undefined) => boolean | unknown;
38
39
 
39
40
  /** Alias for lockBasedDelete & unsafeLiveReads */
40
41
  lock?: boolean;
@@ -254,7 +255,7 @@ function sandboxedIsAlive(config: {
254
255
  proxy = proxy[part];
255
256
  }
256
257
  try {
257
- return !!atomicRaw(checker.isAlive(proxy, path));
258
+ return !!atomicRaw(checker.isAlive(proxy, path, undefined));
258
259
  } catch {
259
260
  config.errors.count++;
260
261
  return true;
@@ -308,7 +309,7 @@ function sandboxedGetLocks(config: {
308
309
 
309
310
  let evalResult = getWatchEvaluator()(() => {
310
311
  let value = proxyWatcher.getCallbackPathValue(pathStr);
311
- return !!atomic(checker.isAlive(value, path));
312
+ return !!atomic(checker.isAlive(pathValueSerializer.getPathValue(value), path, value));
312
313
  }, { getReadLocks: true });
313
314
 
314
315
  return evalResult.readLocks!;
@@ -1,10 +1,9 @@
1
1
  import { runInfinitePoll } from "socket-function/src/batching";
2
2
  import { cache, lazy } from "socket-function/src/caching";
3
3
  import { appendToPathStr, rootPathStr } from "../path";
4
- import { MAX_CHANGE_AGE } from "../0-path-value-core/pathValueCore";
5
4
  import { runCodeWithDatabase } from "./pathDatabaseProxyBase";
6
5
  import { canHaveChildren } from "socket-function/src/types";
7
- import { isNode } from "socket-function/src/misc";
6
+ import { isNode, timeInMinute } from "socket-function/src/misc";
8
7
 
9
8
  let noopProxy = createPathValueProxy({ getCallback: () => undefined, setCallback: () => { }, getSymbol: () => undefined, getKeys: () => [] });
10
9
  let gettingPath: { value: string } | undefined;
@@ -67,7 +66,7 @@ export function createPathValueProxy(
67
66
  let createdCount = 0;
68
67
  let createProxyCached = cache(createProxy);
69
68
  let proxyClearLoop = lazy(() => {
70
- runInfinitePoll(MAX_CHANGE_AGE, function clearProxyCache() {
69
+ runInfinitePoll(timeInMinute * 5, function clearProxyCache() {
71
70
  // TODO: Maybe allow proxies to stick around?
72
71
  //if (createdCount > 1000 * 100)
73
72
  {
@@ -37,8 +37,10 @@ export const functionSchema = rawSchema<{
37
37
  };
38
38
  };
39
39
  }>();
40
- void Promise.resolve().then(() => {
40
+ // NOTE: Looks like set immediate will run after even dynamic imports. So this should actually run as late as possible. If not, we might need to make this so the code that's looking for prefixes explicitly asks for the dynamic prefixes when it's ready.
41
+ setImmediate(() => {
41
42
  for (let moduleId of getAllDevelopmentModulesIds()) {
43
+ addRoutingPrefixForDeploy(getProxyPath(() => functionSchema()[getDomain()].PathFunctionRunner[moduleId].Sources));
42
44
  addRoutingPrefixForDeploy(getProxyPath(() => functionSchema()[getDomain()].PathFunctionRunner[moduleId].Calls));
43
45
  addRoutingPrefixForDeploy(getProxyPath(() => functionSchema()[getDomain()].PathFunctionRunner[moduleId].Results));
44
46
  }