querysub 0.508.0 → 0.509.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 (48) hide show
  1. package/bin/function-public.js +2 -0
  2. package/bin/server.js +0 -2
  3. package/package.json +3 -4
  4. package/spec.txt +5 -0
  5. package/src/-f-node-discovery/NodeDiscovery.ts +1 -1
  6. package/src/-g-core-values/NodeCapabilities.ts +4 -0
  7. package/src/-g-core-values/scheduledShutdown.ts +48 -0
  8. package/src/0-path-value-core/AuthorityLookup.ts +50 -9
  9. package/src/0-path-value-core/PathRouter.ts +10 -31
  10. package/src/0-path-value-core/PathRouterRouteOverride.ts +14 -98
  11. package/src/0-path-value-core/PathRouterServerAuthoritySpec.tsx +1 -4
  12. package/src/0-path-value-core/PathValueCommitter.ts +1 -6
  13. package/src/0-path-value-core/PathValueController.ts +5 -7
  14. package/src/0-path-value-core/PathWatcher.ts +8 -11
  15. package/src/0-path-value-core/hackedPackedPathParentFiltering.ts +8 -22
  16. package/src/0-path-value-core/pathValueCore.ts +4 -5
  17. package/src/1-path-client/RemoteWatcher.ts +47 -34
  18. package/src/2-proxy/archiveMoveHarness.ts +2 -1
  19. package/src/3-path-functions/PathFunctionHelpers.ts +11 -10
  20. package/src/3-path-functions/PathFunctionRunner.ts +165 -24
  21. package/src/3-path-functions/functionReplay.ts +2 -0
  22. package/src/3-path-functions/syncSchema.ts +0 -2
  23. package/src/4-deploy/edgeBootstrap.ts +10 -0
  24. package/src/4-deploy/edgeClientWatcher.tsx +4 -6
  25. package/src/4-deploy/edgeNodes.ts +52 -12
  26. package/src/4-querysub/FunctionRunnerTracking.ts +290 -0
  27. package/src/4-querysub/Querysub.ts +11 -16
  28. package/src/4-querysub/QuerysubController.ts +13 -11
  29. package/src/4-querysub/permissions.ts +3 -0
  30. package/src/4-querysub/querysubPrediction.ts +2 -2
  31. package/src/config.ts +1 -3
  32. package/src/deployManager/components/ServiceDetailPage.tsx +127 -73
  33. package/src/deployManager/components/ServicesListPage.tsx +3 -3
  34. package/src/deployManager/machineApplyMainCode.ts +139 -153
  35. package/src/deployManager/machineController.ts +12 -61
  36. package/src/deployManager/machineSchema.ts +84 -3
  37. package/src/deployManager/setupMachineMain.ts +2 -1
  38. package/src/diagnostics/grossStats/GrossStatsController.ts +3 -2
  39. package/src/diagnostics/logs/errorTickets/autoFixer.ts +3 -2
  40. package/src/diagnostics/logs/logGitHashes.ts +1 -1
  41. package/src/diagnostics/managementPages.tsx +2 -0
  42. package/src/diagnostics/misc-pages/AuthoritySpecPage.tsx +36 -28
  43. package/src/diagnostics/misc-pages/FunctionCaptureAnalyzePage.tsx +1 -1
  44. package/src/library-components/NetworkSelector.tsx +77 -0
  45. package/src/misc.ts +19 -0
  46. package/src/user-implementation/userData.ts +1 -1
  47. package/src/zipThreaded.ts +3 -2
  48. package/bin/server-dev.js +0 -11
@@ -9,9 +9,7 @@ import { pathValueSerializer } from "../-h-path-value-serialize/PathValueSeriali
9
9
  import { pathValueCommitter } from "./PathValueCommitter";
10
10
  import { auditLog, isDebugLogEnabled } from "./auditLogs";
11
11
  import { debugNodeId, debugNodeThread } from "../-c-identity/IdentityController";
12
- import { getSlowdown, isDiskAudit, getDomain, DEFAULT_NETWORK } from "../config";
13
- import { getPathNetwork } from "./PathRouterRouteOverride";
14
- import { decodeParentFilter } from "./hackedPackedPathParentFiltering";
12
+ import { getSlowdown, isDiskAudit, getDomain } from "../config";
15
13
  import { decodeNodeId } from "sliftutils/misc/https/certs";
16
14
  import { areNodeIdsEqual, isOwnNodeId } from "../-f-node-discovery/NodeDiscovery";
17
15
  import { getNodeIdIP } from "socket-function/src/nodeCache";
@@ -235,10 +233,10 @@ export class PathValueControllerBase {
235
233
  if (isDebugLogEnabled()) {
236
234
  let sourceNodeId = debugNodeId(callerId);
237
235
  for (let value of config.paths) {
238
- auditLog("WATCH PATH", { path: value, network: getPathNetwork(value), sourceNodeId, sourceNodeThreadId: debugNodeThread(callerId) });
236
+ auditLog("WATCH PATH", { path: value, sourceNodeId, sourceNodeThreadId: debugNodeThread(callerId) });
239
237
  }
240
238
  for (let value of config.parentPaths) {
241
- auditLog("WATCH PARENT PATH", { path: value, network: decodeParentFilter(value)?.network || DEFAULT_NETWORK, sourceNodeId });
239
+ auditLog("WATCH PARENT PATH", { path: value, sourceNodeId });
242
240
  }
243
241
  }
244
242
  pathWatcher.watchPath({
@@ -254,10 +252,10 @@ export class PathValueControllerBase {
254
252
  if (isDebugLogEnabled()) {
255
253
  let sourceNodeId = debugNodeId(SocketFunction.getCaller().nodeId);
256
254
  for (let value of config.paths) {
257
- auditLog("UNWATCHING PATH", { path: value, network: getPathNetwork(value), sourceNodeId, sourceNodeThreadId: debugNodeThread(callerId) });
255
+ auditLog("UNWATCHING PATH", { path: value, sourceNodeId, sourceNodeThreadId: debugNodeThread(callerId) });
258
256
  }
259
257
  for (let value of config.parentPaths) {
260
- auditLog("UNWATCHING PARENT PATH", { path: value, network: decodeParentFilter(value)?.network || DEFAULT_NETWORK, sourceNodeId });
258
+ auditLog("UNWATCHING PARENT PATH", { path: value, sourceNodeId });
261
259
  }
262
260
  }
263
261
  pathWatcher.unwatchPath({ paths: config.paths, parentPaths: config.parentPaths, callback: callerId, reason: "PathValueController.unwatchLatest" });
@@ -11,9 +11,7 @@ import { PathValueControllerBase } from "./PathValueController";
11
11
  import { PathRouter } from "./PathRouter";
12
12
  import { auditLog } from "./auditLogs";
13
13
  import { WatchConfig, authorityStorage, PathValue, NodeId, compareTime, isCoreQuiet, MAX_CHANGE_AGE, createMissingEpochValue } from "./pathValueCore";
14
- import { decodeParentFilter, matchesParentRangeFilter } from "./hackedPackedPathParentFiltering";
15
- import { getPathNetwork } from "./PathRouterRouteOverride";
16
- import { DEFAULT_NETWORK } from "../config";
14
+ import { matchesParentRangeFilter } from "./hackedPackedPathParentFiltering";
17
15
  import { isClient } from "../config2";
18
16
  import { delay } from "socket-function/src/batching";
19
17
  import { isClientNodeId } from "socket-function/src/nodeCache";
@@ -172,22 +170,21 @@ class PathWatcher {
172
170
  incrementWatcherSequence();
173
171
  if (isOwnNodeId(config.nodeId)) {
174
172
  for (let path of newPathsWatched) {
175
- auditLog("new local WATCH VALUE", { path, network: getPathNetwork(path) });
173
+ auditLog("new local WATCH VALUE", { path });
176
174
  }
177
175
  for (let path of newParentsWatched) {
178
- auditLog("new local WATCH PARENT", { path, network: decodeParentFilter(path)?.network || DEFAULT_NETWORK });
176
+ auditLog("new local WATCH PARENT", { path });
179
177
  }
180
178
  } else {
181
179
  for (let path of newPathsWatched) {
182
180
  auditLog("new non-local WATCH VALUE", {
183
181
  path,
184
- network: getPathNetwork(path),
185
182
  watcher: config.nodeId,
186
183
  sourceNodeThreadId: debugNodeThread(config.nodeId),
187
184
  });
188
185
  }
189
186
  for (let path of newParentsWatched) {
190
- auditLog("new non-local WATCH PARENT", { path, network: decodeParentFilter(path)?.network || DEFAULT_NETWORK, watcher: config.nodeId });
187
+ auditLog("new non-local WATCH PARENT", { path, watcher: config.nodeId });
191
188
  }
192
189
  }
193
190
  console.info(`New PathValue watches`, {
@@ -247,9 +244,9 @@ class PathWatcher {
247
244
  obj.watchers.delete(callback);
248
245
 
249
246
  if (isOwnNodeId(callback)) {
250
- auditLog("local UNWATCH PARENT", { path, network: decodeParentFilter(path)?.network || DEFAULT_NETWORK, reason, remainingWatchers: obj.watchers.size });
247
+ auditLog("local UNWATCH PARENT", { path, reason, remainingWatchers: obj.watchers.size });
251
248
  } else {
252
- auditLog("non-local UNWATCH PARENT", { path, network: decodeParentFilter(path)?.network || DEFAULT_NETWORK, watcher: callback, remoteNodeId: debugNodeId(callback), remoteNodeThreadId: debugNodeThread(callback), reason, remainingWatchers: obj.watchers.size });
249
+ auditLog("non-local UNWATCH PARENT", { path, watcher: callback, remoteNodeId: debugNodeId(callback), remoteNodeThreadId: debugNodeThread(callback), reason, remainingWatchers: obj.watchers.size });
253
250
  }
254
251
 
255
252
  if (obj.watchers.size === 0) {
@@ -284,9 +281,9 @@ class PathWatcher {
284
281
  watchers.watchers.delete(callback);
285
282
 
286
283
  if (isOwnNodeId(callback)) {
287
- auditLog("local UNWATCH VALUE", { path, network: getPathNetwork(path), reason });
284
+ auditLog("local UNWATCH VALUE", { path, reason });
288
285
  } else {
289
- auditLog("non-local UNWATCH VALUE", { path, network: getPathNetwork(path), watcher: callback, reason });
286
+ auditLog("non-local UNWATCH VALUE", { path, watcher: callback, reason });
290
287
  }
291
288
 
292
289
  if (watchers.watchers.size === 0) {
@@ -3,8 +3,6 @@ import { getPathDepth, getPathIndexAssert, hack_getPackedPathSuffix, hack_setPac
3
3
  import { AuthoritySpec, PathRouter } from "./PathRouter";
4
4
  import { cache } from "socket-function/src/caching";
5
5
  import { authorityLookup } from "./AuthorityLookup";
6
- import { assertValidNetwork, getPathNetwork } from "./PathRouterRouteOverride";
7
- import { DEFAULT_NETWORK } from "../config";
8
6
 
9
7
 
10
8
  let getSpecForChildPath = (path: string) => authorityLookup.getOurSpec();
@@ -18,14 +16,9 @@ export function matchesParentRangeFilter(config: {
18
16
  packedPath: string;
19
17
  }) {
20
18
  // If it equals the non packed path, then it must not be packed, and so it must match.
21
- if (config.parentPath === config.packedPath) {
22
- return getPathNetwork(config.fullPath) === DEFAULT_NETWORK;
23
- }
19
+ if (config.parentPath === config.packedPath) return true;
24
20
  let filter = decodeParentFilter(config.packedPath);
25
- if (!filter) {
26
- return getPathNetwork(config.fullPath) === DEFAULT_NETWORK;
27
- }
28
- if (getPathNetwork(config.fullPath) !== (filter.network || DEFAULT_NETWORK)) return false;
21
+ if (!filter) return true;
29
22
  if (filter.start <= 0 && filter.end >= 1) return true;
30
23
  let route = PathRouter.getRouteFull({ path: config.fullPath, spec: getSpecForChildPath(config.fullPath) });
31
24
  return filter.start <= route && route < filter.end;
@@ -33,14 +26,12 @@ export function matchesParentRangeFilter(config: {
33
26
 
34
27
  export const filterChildPathsBase = measureWrap(
35
28
  function filterChildPathsBase(parentPath: string, packedSuffix: string, paths: Set<string>): Set<string> {
36
- let [startFractionStr, endFractionStr, network] = packedSuffix.split("|");
29
+ let [startFractionStr, endFractionStr] = packedSuffix.split("|");
37
30
  let startFraction = Number(startFractionStr);
38
31
  let endFraction = Number(endFractionStr);
39
- let filterNetwork = network || DEFAULT_NETWORK;
40
32
 
41
33
  let filtered = new Set<string>();
42
34
  for (let path of paths) {
43
- if (getPathNetwork(path) !== filterNetwork) continue;
44
35
  // TODO: We can make this significantly more efficient as once we know if it's a prefix or not, we know the underlying function call every time. However, that starts to get complicated, and I don't think this is going to be a bottleneck.
45
36
  let route = PathRouter.getRouteFull({ path, spec: getSpecForChildPath(path) });
46
37
  if (startFraction <= route && route < endFraction) {
@@ -56,19 +47,14 @@ export function encodeParentFilter(config: {
56
47
  path: string;
57
48
  startFraction: number;
58
49
  endFraction: number;
59
- network?: string;
60
50
  }) {
61
- let suffix = `${config.startFraction}|${config.endFraction}`;
62
- if (config.network) {
63
- assertValidNetwork(config.network);
64
- suffix += `|${config.network}`;
65
- }
66
- return hack_setPackedPathSuffix(config.path, suffix);
51
+ return hack_setPackedPathSuffix(config.path, `${config.startFraction}|${config.endFraction}`);
67
52
  }
68
- export function decodeParentFilter(path: string): { path: string; start: number, end: number; network?: string } | undefined {
53
+ export function decodeParentFilter(path: string): { path: string; start: number, end: number } | undefined {
69
54
  let packedSuffix = hack_getPackedPathSuffix(path);
70
55
  if (!packedSuffix) return undefined;
71
- let [startStr, endStr, network] = packedSuffix.split("|");
56
+ let [startStr, endStr] = packedSuffix.split("|");
72
57
  let unpackedPath = hack_stripPackedPath(path);
73
- return { path: unpackedPath, start: Number(startStr), end: Number(endStr), network: network || undefined };
58
+ return { path: unpackedPath, start: Number(startStr), end: Number(endStr) };
59
+
74
60
  }
@@ -24,8 +24,7 @@ import { fastHash } from "../misc/hash";
24
24
  import { authorityLookup } from "./AuthorityLookup";
25
25
  import { onPathInteracted } from "../diagnostics/pathAuditerCallback";
26
26
  import { decodeParentFilter, filterChildPathsBase } from "./hackedPackedPathParentFiltering";
27
- import { getPathNetwork } from "./PathRouterRouteOverride";
28
- import { isDiskAudit, DEFAULT_NETWORK } from "../config";
27
+ import { isDiskAudit } from "../config";
29
28
  import { removeRange } from "../rangeMath";
30
29
  import { remoteWatcher } from "../1-path-client/RemoteWatcher";
31
30
  import { setFlag } from "socket-function/require/compileFlags";
@@ -570,14 +569,14 @@ class AuthorityPathValueStorage {
570
569
  if (this.DEBUG_UNWATCH) {
571
570
  console.log(blue(`Unsyncing path at ${Date.now()}`), path);
572
571
  }
573
- auditLog("DESTROY PATH", { path, network: getPathNetwork(path) });
572
+ auditLog("DESTROY PATH", { path });
574
573
 
575
574
  this.isSyncedCache.delete(path);
576
575
  this.removePathFromStorage(path, "unwatched");
577
576
  }
578
577
  public markParentPathAsUnwatched(path: string) {
579
578
  if (this.parentsSynced.has(path)) {
580
- console.info(`Unwatching path that is a parent synced path (fine, but might be an issue)`, { path, network: decodeParentFilter(path)?.network || DEFAULT_NETWORK });
579
+ console.info(`Unwatching path that is a parent synced path (fine, but might be an issue)`, { path });
581
580
  }
582
581
  // NOTE: I don't think we have to handle the case where we are the authority,
583
582
  // because we don't delete any actual data here.
@@ -696,7 +695,7 @@ class AuthorityPathValueStorage {
696
695
  public addParentSyncs(parentSyncs: { parentPath: string; sourceNodeId: string }[]) {
697
696
  for (let obj of parentSyncs) {
698
697
  if (isDebugLogEnabled()) {
699
- auditLog("RECEIVED PARENT PATH", { parentPath: obj.parentPath, network: decodeParentFilter(obj.parentPath)?.network || DEFAULT_NETWORK, remoteNodeId: debugNodeId(obj.sourceNodeId), remoteNodeThreadId: debugNodeThread(obj.sourceNodeId) });
698
+ auditLog("RECEIVED PARENT PATH", { parentPath: obj.parentPath, remoteNodeId: debugNodeId(obj.sourceNodeId), remoteNodeThreadId: debugNodeThread(obj.sourceNodeId) });
700
699
  }
701
700
 
702
701
  let decoded = decodeParentFilter(obj.parentPath);
@@ -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, sort } from "socket-function/src/misc";
5
+ import { isNode, sort, timeInMinute, timeInSecond } from "socket-function/src/misc";
6
6
  import { measureFnc, measureBlock } from "socket-function/src/profiling/measure";
7
7
  import { getOwnNodeId, isOwnNodeId } from "../-f-node-discovery/NodeDiscovery";
8
8
  import { PathValueController } from "../0-path-value-core/PathValueController";
@@ -20,8 +20,6 @@ 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, registerGetSpecForChildPath } from "../0-path-value-core/hackedPackedPathParentFiltering";
23
- import { getPathNetwork } from "../0-path-value-core/PathRouterRouteOverride";
24
- import { DEFAULT_NETWORK } from "../config";
25
23
  import { removeRange, rangesOverlap } from "../rangeMath";
26
24
  import { evaluateValidStates } from "../config2";
27
25
 
@@ -30,6 +28,10 @@ setImmediate(() => import("../4-querysub/Querysub"));
30
28
  // 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.
31
29
  const STOP_KEYS_DOUBLE_SENDS = true;
32
30
 
31
+ // How close to an authority's scheduled shutdown we start treating it like a disconnect (rehoming all the paths we watch on it).
32
+ const SHUTDOWN_REHOME_WINDOW = timeInMinute * 5;
33
+ const SHUTDOWN_REHOME_POLL_INTERVAL = timeInSecond * 30;
34
+
33
35
  // 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).
34
36
  export class RemoteWatcher {
35
37
  public static DEBUG = false;
@@ -48,10 +50,8 @@ export class RemoteWatcher {
48
50
  start: number;
49
51
  // exclusive
50
52
  end: number;
51
- // encodeParentFilter({ path: cleanPath, startFraction: start, endFraction: end, network })
53
+ // encodeParentFilter({ path: cleanPath, startFraction: start, endFraction: end })
52
54
  finalPath: string;
53
- // undefined means the default network
54
- network?: string;
55
55
  // All of the requests paths that request it (pre decodeParentFilter).
56
56
  inputPaths: Set<string>;
57
57
 
@@ -126,6 +126,32 @@ export class RemoteWatcher {
126
126
  // the chances the server will be useless is high, so... lag is probably fine in exchange for faster
127
127
  // reconnection.
128
128
  private reconnectLoop = runInfinitePoll(10000, () => this.tryToReconnectNow());
129
+
130
+ // Once an authority is this close to its scheduled shutdown, we act as if it disconnected, and find new homes for all the paths we watch on it (PathRouter avoids shutting down nodes, so the rewatch picks a different authority when one exists).
131
+ private rehomedShutdownNodes = new Set<string>();
132
+ private shutdownRehomeLoop = runInfinitePoll(SHUTDOWN_REHOME_POLL_INTERVAL, () => this.rehomeShuttingDownAuthorities());
133
+ private rehomeShuttingDownAuthorities() {
134
+ let authorityIds = new Set<string>(this.remoteWatchPaths.values());
135
+ for (let watchObj of this.remoteWatchParents2.values()) {
136
+ for (let range of watchObj.ranges) {
137
+ authorityIds.add(range.authorityId);
138
+ }
139
+ }
140
+ for (let authorityId of authorityIds) {
141
+ if (this.rehomedShutdownNodes.has(authorityId)) continue;
142
+ let shutdownTime = authorityLookup.getScheduledShutdownTime(authorityId);
143
+ if (shutdownTime === undefined || shutdownTime - Date.now() > SHUTDOWN_REHOME_WINDOW) continue;
144
+ this.rehomedShutdownNodes.add(authorityId);
145
+ console.log(yellow(`Authority ${authorityId} is scheduled to shut down at ${new Date(shutdownTime).toLocaleString()}, rehoming all paths watched on it`));
146
+ logErrors(this.refreshAllWatches(authorityId));
147
+ }
148
+ // Nodes we no longer watch don't need an entry (otherwise this grows forever, as every switchover shuts down a node)
149
+ for (let nodeId of Array.from(this.rehomedShutdownNodes)) {
150
+ if (!authorityIds.has(nodeId)) {
151
+ this.rehomedShutdownNodes.delete(nodeId);
152
+ }
153
+ }
154
+ }
129
155
  private async tryToReconnectNow() {
130
156
  if (!this.disconnectedPaths.size && !this.disconnectedParents.size) return;
131
157
 
@@ -143,10 +169,8 @@ export class RemoteWatcher {
143
169
  let parentPath = getParentPathStr(path);
144
170
  let watchObj = this.remoteWatchParents2.get(parentPath);
145
171
  if (!watchObj) return undefined;
146
- let pathNetwork = getPathNetwork(path);
147
172
  // NOTE: PathRouter.getChildReadNodes PROMISES That all of the nodes will hash in the same way, and if they all hash in the same way, and they have different ranges, then every path will uniquely map to a single value.
148
173
  return watchObj.ranges.find(x => {
149
- if ((x.network || DEFAULT_NETWORK) !== pathNetwork) return false;
150
174
  let route = PathRouter.getRouteFull({ path, spec: x.authoritySpec });
151
175
  return x.start <= route && route < x.end;
152
176
  });
@@ -155,10 +179,8 @@ export class RemoteWatcher {
155
179
  let parentPath = getParentPathStr(path);
156
180
  let watchObj = this.remoteWatchParents2.get(parentPath);
157
181
  if (!watchObj) return undefined;
158
- let pathNetwork = getPathNetwork(path);
159
182
  // NOTE: PathRouter.getChildReadNodes PROMISES That all of the nodes will hash in the same way, and if they all hash in the same way, and they have different ranges, then every path will uniquely map to a single value.
160
183
  for (let range of watchObj.ranges) {
161
- if ((range.network || DEFAULT_NETWORK) !== pathNetwork) continue;
162
184
  let route = PathRouter.getRouteFull({ path, spec: range.authoritySpec });
163
185
  if (range.start <= route && route < range.end) {
164
186
  return route;
@@ -266,17 +288,17 @@ export class RemoteWatcher {
266
288
  if (totalMissingPaths === 0) {
267
289
  let authorities = authorityLookup.getTopologySync();
268
290
  authorityId = PathRouter.getReadyAuthority(path)?.nodeId;
269
- console.warn(`Missing authority for path ${path} (network ${getPathNetwork(path)})`, { network: getPathNetwork(path), authorities });
291
+ console.warn(`Missing authority for path ${path}`, { authorities });
270
292
  }
271
293
  totalMissingPaths++;
272
294
  if (!this.disconnectedPaths.has(path)) {
273
295
  newDisconnectPaths++;
274
296
  this.disconnectedPaths.add(path);
275
- auditLog("NO AUTHORITY FOR PATH", { path, network: getPathNetwork(path) });
297
+ auditLog("NO AUTHORITY FOR PATH", { path });
276
298
  }
277
299
  continue;
278
300
  }
279
- auditLog("FOUND AUTHORITY FOR PATH", { path, network: getPathNetwork(path), authorityId });
301
+ auditLog("FOUND AUTHORITY FOR PATH", { path, authorityId });
280
302
  this.disconnectedPaths.delete(path);
281
303
  foundPaths++;
282
304
 
@@ -297,7 +319,6 @@ export class RemoteWatcher {
297
319
 
298
320
  let decodedParentFilter = decodeParentFilter(path);
299
321
  let cleanPath = decodedParentFilter?.path ?? path;
300
- let network = decodedParentFilter?.network;
301
322
 
302
323
  let rangeStart = 0;
303
324
  let rangeEnd = 1;
@@ -306,8 +327,6 @@ export class RemoteWatcher {
306
327
  rangeEnd = decodedParentFilter.end;
307
328
  }
308
329
 
309
- const sameNetwork = (rangeNetwork: string | undefined) => (rangeNetwork || DEFAULT_NETWORK) === (network || DEFAULT_NETWORK);
310
-
311
330
  let watchObj = this.remoteWatchParents2.get(cleanPath);
312
331
 
313
332
  if (watchObj && !config.tryReconnect) {
@@ -317,13 +336,11 @@ export class RemoteWatcher {
317
336
  end: rangeEnd,
318
337
  }];
319
338
  for (let range of watchObj.ranges) {
320
- if (!sameNetwork(range.network)) continue;
321
339
  removeRange(requiredRanges, range);
322
340
  }
323
341
  if (requiredRanges.length === 0) {
324
342
  this.disconnectedParents.delete(path);
325
343
  for (let range of watchObj.ranges) {
326
- if (!sameNetwork(range.network)) continue;
327
344
  if (rangesOverlap(range, { start: rangeStart, end: rangeEnd })) {
328
345
  range.inputPaths.add(path);
329
346
  }
@@ -332,8 +349,7 @@ export class RemoteWatcher {
332
349
  }
333
350
  }
334
351
 
335
- let readNodesPath = network && encodeParentFilter({ path: cleanPath, startFraction: 0, endFraction: 1, network }) || cleanPath;
336
- let { nodes } = PathRouter.getChildReadNodes(readNodesPath, {
352
+ let { nodes } = PathRouter.getChildReadNodes(cleanPath, {
337
353
  // Pass existing connected node ids as preferred
338
354
  preferredNodeIds: watchObj?.ranges.map(x => x.authorityId) || []
339
355
  });
@@ -361,7 +377,6 @@ export class RemoteWatcher {
361
377
  // Compute which parts of [rangeStart, rangeEnd] aren't yet covered by existing watches
362
378
  let missingRanges: { start: number; end: number }[] = [{ start: rangeStart, end: rangeEnd }];
363
379
  for (let range of watchObj.ranges) {
364
- if (!sameNetwork(range.network)) continue;
365
380
  removeRange(missingRanges, range);
366
381
  }
367
382
 
@@ -374,14 +389,13 @@ export class RemoteWatcher {
374
389
  let clippedStart = Math.max(node.range.start, missing.start);
375
390
  let clippedEnd = Math.min(node.range.end, missing.end);
376
391
 
377
- let finalPath = encodeParentFilter({ path: cleanPath, startFraction: clippedStart, endFraction: clippedEnd, network });
392
+ let finalPath = encodeParentFilter({ path: cleanPath, startFraction: clippedStart, endFraction: clippedEnd });
378
393
  let existingRange = watchObj.ranges.find(x => x.finalPath === finalPath && x.authorityId === node.nodeId);
379
394
  if (!existingRange) {
380
395
  existingRange = {
381
396
  start: clippedStart,
382
397
  end: clippedEnd,
383
398
  finalPath,
384
- network,
385
399
  inputPaths: new Set(),
386
400
  authorityId: node.nodeId,
387
401
  suppressedWatches: new Set(),
@@ -401,7 +415,6 @@ export class RemoteWatcher {
401
415
 
402
416
  // Register path on pre-existing ranges that already cover parts of [rangeStart, rangeEnd]
403
417
  for (let range of watchObj.ranges) {
404
- if (!sameNetwork(range.network)) continue;
405
418
  if (rangesOverlap(range, { start: rangeStart, end: rangeEnd })) {
406
419
  range.inputPaths.add(path);
407
420
  }
@@ -413,7 +426,7 @@ export class RemoteWatcher {
413
426
  console.warn(`Some paths have no authority. We will search for an authority periodically until we find an authority. New missing ${newDisconnectPaths} paths and ${newDisconnectParents} parent paths, total missing ${this.disconnectedPaths.size} paths and ${this.disconnectedParents.size} parent paths.`);
414
427
  let first10 = Array.from(this.disconnectedPaths).slice(0, 10);
415
428
  for (let path of first10) {
416
- console.log(`\t(network ${getPathNetwork(path)}) ${path}`);
429
+ console.log(`\t${path}`);
417
430
  }
418
431
  if (this.disconnectedPaths.size > 10) {
419
432
  console.log(`\t... and ${this.disconnectedPaths.size - 10} more paths`);
@@ -433,10 +446,10 @@ export class RemoteWatcher {
433
446
  if (isOwnNodeId(authorityId)) continue;
434
447
 
435
448
  for (let path of paths) {
436
- auditLog("remoteWatcher outer WATCH", { path, network: getPathNetwork(path), remoteNodeId: authorityId });
449
+ auditLog("remoteWatcher outer WATCH", { path, remoteNodeId: authorityId });
437
450
  }
438
451
  for (let path of parentPaths) {
439
- auditLog("remoteWatcher outer PARENT WATCH", { path, network: decodeParentFilter(path)?.network || DEFAULT_NETWORK, remoteNodeId: authorityId });
452
+ auditLog("remoteWatcher outer PARENT WATCH", { path, remoteNodeId: authorityId });
440
453
  }
441
454
 
442
455
  if (STOP_KEYS_DOUBLE_SENDS) {
@@ -445,7 +458,7 @@ export class RemoteWatcher {
445
458
  let range = this.getRemoteWatchParentRange(path);
446
459
  if (range) {
447
460
  range.suppressedWatches.add(path);
448
- auditLog("remoteWatcher outer WATCH SUPPRESSED", { path, network: getPathNetwork(path), remoteNodeId: authorityId });
461
+ auditLog("remoteWatcher outer WATCH SUPPRESSED", { path, remoteNodeId: authorityId });
449
462
  return false;
450
463
  }
451
464
  return true;
@@ -497,10 +510,10 @@ export class RemoteWatcher {
497
510
  }
498
511
  for (let [authorityId, { paths, parentPaths }] of byAuthority) {
499
512
  for (let path of paths) {
500
- auditLog("remoteWatcher inner WATCH", { path, network: getPathNetwork(path), remoteNodeId: authorityId });
513
+ auditLog("remoteWatcher inner WATCH", { path, remoteNodeId: authorityId });
501
514
  }
502
515
  for (let path of parentPaths) {
503
- auditLog("remoteWatcher inner WATCH PARENT", { path, network: decodeParentFilter(path)?.network || DEFAULT_NETWORK, remoteNodeId: authorityId });
516
+ auditLog("remoteWatcher inner WATCH PARENT", { path, remoteNodeId: authorityId });
504
517
  }
505
518
  // NOTE: We log watches here because we want to log batched watches
506
519
  ActionsHistory.OnNewWatches({ newPathsWatched: paths, newParentsWatched: parentPaths, debugName: batched[0].debugName, authorityId });
@@ -511,10 +524,10 @@ export class RemoteWatcher {
511
524
  };
512
525
  if (isDebugLogEnabled()) {
513
526
  for (let path of paths) {
514
- auditLog("Asking to watch path", { path, network: getPathNetwork(path), authorityId, targetNodeThreadId: debugNodeThread(authorityId) });
527
+ auditLog("Asking to watch path", { path, authorityId, targetNodeThreadId: debugNodeThread(authorityId) });
515
528
  }
516
529
  for (let path of parentPaths) {
517
- auditLog("Asking to watch parent path", { path, network: decodeParentFilter(path)?.network || DEFAULT_NETWORK, authorityId, targetNodeThreadId: debugNodeThread(authorityId) });
530
+ auditLog("Asking to watch parent path", { path, authorityId, targetNodeThreadId: debugNodeThread(authorityId) });
518
531
  }
519
532
  }
520
533
  logErrors(RemoteWatcher.REMOTE_WATCH_FUNCTION(config, authorityId));
@@ -604,10 +617,10 @@ export class RemoteWatcher {
604
617
  }
605
618
  for (let [authorityIdBase, { paths, parentPaths }] of unwatchesPerAuthority.entries()) {
606
619
  for (let path of paths) {
607
- auditLog("remoteWatcher inner UNWATCH", { path, network: getPathNetwork(path), remoteNodeId: authorityIdBase });
620
+ auditLog("remoteWatcher inner UNWATCH", { path, remoteNodeId: authorityIdBase });
608
621
  }
609
622
  for (let path of parentPaths) {
610
- auditLog("remoteWatcher inner UNWATCH PARENT", { path, network: decodeParentFilter(path)?.network || DEFAULT_NETWORK, remoteNodeId: authorityIdBase });
623
+ auditLog("remoteWatcher inner UNWATCH PARENT", { path, remoteNodeId: authorityIdBase });
611
624
  }
612
625
  if (!isOwnNodeId(authorityIdBase)) {
613
626
  logErrors(RemoteWatcher.REMOTE_UNWATCH_FUNCTION({ paths, parentPaths }, authorityIdBase));
@@ -178,7 +178,8 @@ export async function runArchiveMover(config: {
178
178
  excludedValues.push(value);
179
179
  }
180
180
  }
181
- if (includedValues.length === 0) {
181
+ // If we are excluding it because we're filtering it, then there's no reason to rewrite the file. HOWEVER, If the file is empty, which can happen if it's corrupted, then we should move the file. Otherwise, corrupted files never get moved by anything.
182
+ if (includedValues.length === 0 && valuesObj.values.length > 0) {
182
183
  continue;
183
184
  }
184
185
  if (excludedValues.length > 0) {
@@ -18,8 +18,14 @@ import { interceptCalls } from "../-0-hooks/hooks";
18
18
  import { createRoutingOverrideKey } from "../0-path-value-core/PathRouterRouteOverride";
19
19
  import { PathRouter } from "../0-path-value-core/PathRouter";
20
20
  import { getPathFromProxy } from "../2-proxy/pathValueProxy";
21
- import { getDomain, getPrimaryNetwork, isPublic, DEFAULT_NETWORK } from "../config";
21
+ import { getDomain, isPublic } from "../config";
22
22
  import { isServer } from "../config2";
23
+
24
+ // Deferred, as syncSchema.ts imports us at module init, and FunctionRunnerTracking imports syncSchema.
25
+ let functionRunnerTracking: typeof import("../4-querysub/FunctionRunnerTracking") | undefined;
26
+ setImmediate(async () => {
27
+ functionRunnerTracking = await import("../4-querysub/FunctionRunnerTracking");
28
+ });
23
29
  import { getTimeUnique } from "socket-function/src/bits";
24
30
 
25
31
  // NOTE: We could deploy single functions, but... we will almost always be updating all functions at
@@ -160,14 +166,11 @@ export function writeFunctionCall(config: {
160
166
  // which gives them about a minute to guess it...
161
167
  let callId = now + "_" + secureRandom();
162
168
 
163
- // Clients don't know their network. The QuerysubController decides the network for their calls (and rewrites the callId to match).
164
- let network: string | undefined;
165
- if (isNode()) {
166
- network = getPrimaryNetwork();
167
- if (network === DEFAULT_NETWORK) {
168
- network = undefined;
169
- }
169
+ // The CALLER has to pick the network (instead of the receiving server), because the caller needs to watch the call's result path (which the network is part of) ahead of time — that is how predictions work.
170
+ if (!functionRunnerTracking) {
171
+ throw new Error(`Function runner tracking is not loaded yet, so a network cannot be picked for the call ${moduleId}.${functionId}`);
170
172
  }
173
+ let network = functionRunnerTracking.getSelectedNetwork();
171
174
 
172
175
  // We HAVE to override the routing id, otherwise PathFunctionRunner secondary sharding won't work
173
176
  callId = getCallIdOverride({
@@ -175,12 +178,10 @@ export function writeFunctionCall(config: {
175
178
  functionId,
176
179
  callId,
177
180
  args,
178
- network,
179
181
  }) || createRoutingOverrideKey({
180
182
  originalKey: callId,
181
183
  routeKey: callId,
182
184
  remappedPrefix: getFunctionRunnerPrefix(moduleId),
183
- network,
184
185
  });
185
186
 
186
187
  let argsEncoded = encodeArgs(args);