querysub 0.406.0 → 0.408.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 (55) hide show
  1. package/bin/audit-disk-values.js +7 -0
  2. package/bin/deploy-prefixes.js +7 -0
  3. package/package.json +5 -3
  4. package/src/-a-archives/archiveCache.ts +12 -9
  5. package/src/-a-auth/certs.ts +1 -1
  6. package/src/-c-identity/IdentityController.ts +9 -1
  7. package/src/-f-node-discovery/NodeDiscovery.ts +63 -10
  8. package/src/0-path-value-core/AuthorityLookup.ts +14 -4
  9. package/src/0-path-value-core/PathRouter.ts +247 -117
  10. package/src/0-path-value-core/PathRouterRouteOverride.ts +1 -1
  11. package/src/0-path-value-core/PathRouterServerAuthoritySpec.tsx +4 -2
  12. package/src/0-path-value-core/PathValueCommitter.ts +68 -31
  13. package/src/0-path-value-core/PathValueController.ts +77 -8
  14. package/src/0-path-value-core/PathWatcher.ts +46 -4
  15. package/src/0-path-value-core/ShardPrefixes.ts +6 -0
  16. package/src/0-path-value-core/ValidStateComputer.ts +20 -8
  17. package/src/0-path-value-core/hackedPackedPathParentFiltering.ts +18 -55
  18. package/src/0-path-value-core/pathValueArchives.ts +19 -8
  19. package/src/0-path-value-core/pathValueCore.ts +75 -27
  20. package/src/0-path-value-core/startupAuthority.ts +9 -9
  21. package/src/1-path-client/RemoteWatcher.ts +217 -178
  22. package/src/1-path-client/pathValueClientWatcher.ts +6 -11
  23. package/src/2-proxy/pathValueProxy.ts +2 -3
  24. package/src/3-path-functions/PathFunctionRunner.ts +3 -1
  25. package/src/3-path-functions/syncSchema.ts +6 -2
  26. package/src/4-deploy/deployGetFunctionsInner.ts +1 -1
  27. package/src/4-deploy/deployPrefixes.ts +14 -0
  28. package/src/4-deploy/edgeNodes.ts +1 -1
  29. package/src/4-querysub/Querysub.ts +17 -5
  30. package/src/4-querysub/QuerysubController.ts +21 -10
  31. package/src/4-querysub/predictionQueue.tsx +3 -0
  32. package/src/4-querysub/querysubPrediction.ts +27 -20
  33. package/src/5-diagnostics/nodeMetadata.ts +17 -0
  34. package/src/diagnostics/NodeConnectionsPage.tsx +167 -0
  35. package/src/diagnostics/NodeViewer.tsx +11 -15
  36. package/src/diagnostics/PathDistributionInfo.tsx +102 -0
  37. package/src/diagnostics/SyncTestPage.tsx +19 -8
  38. package/src/diagnostics/auditDiskValues.ts +221 -0
  39. package/src/diagnostics/auditDiskValuesEntry.ts +43 -0
  40. package/src/diagnostics/logs/IndexedLogs/LogViewer3.tsx +5 -1
  41. package/src/diagnostics/logs/TimeRangeSelector.tsx +3 -3
  42. package/src/diagnostics/logs/lifeCycleAnalysis/LifeCycleRenderer.tsx +2 -0
  43. package/src/diagnostics/managementPages.tsx +10 -1
  44. package/src/diagnostics/misc-pages/ArchiveViewer.tsx +3 -2
  45. package/src/diagnostics/pathAuditer.ts +21 -0
  46. package/src/path.ts +9 -2
  47. package/src/rangeMath.ts +41 -0
  48. package/tempnotes.txt +5 -58
  49. package/test.ts +13 -295
  50. package/src/diagnostics/benchmark.ts +0 -139
  51. package/src/diagnostics/runSaturationTest.ts +0 -416
  52. package/src/diagnostics/satSchema.ts +0 -64
  53. package/src/test/mongoSatTest.tsx +0 -55
  54. package/src/test/satTest.ts +0 -193
  55. package/src/test/test.tsx +0 -552
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Always local, as we want to always use the local code? Might not be needed anymore?
4
+ process.argv.push("--local");
5
+
6
+ require("typenode");
7
+ require("../src/diagnostics/auditDiskValuesEntry");
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Always local, as we want to always use the local code? Might not be needed anymore?
4
+ process.argv.push("--local");
5
+
6
+ require("typenode");
7
+ require("../src/4-deploy/deployPrefixes");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "querysub",
3
- "version": "0.406.0",
3
+ "version": "0.408.0",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "note1": "note on node-forge fork, see https://github.com/digitalbazaar/forge/issues/744 for details",
@@ -23,6 +23,7 @@
23
23
  },
24
24
  "bin": {
25
25
  "deploy": "./bin/deploy.js",
26
+ "deploy-prefixes": "./bin/deploy-prefixes.js",
26
27
  "server": "./bin/server.js",
27
28
  "server-public": "./bin/server-public.js",
28
29
  "function": "./bin/function.js",
@@ -39,7 +40,8 @@
39
40
  "addsuperuser": "./bin/addsuperuser.js",
40
41
  "error-watch": "./bin/error-watch.js",
41
42
  "error-watch-public": "./bin/error-watch-public.js",
42
- "audit-imports": "./bin/audit-imports.js"
43
+ "audit-imports": "./bin/audit-imports.js",
44
+ "audit-disk-values": "./bin/audit-disk-values.js"
43
45
  },
44
46
  "dependencies": {
45
47
  "@types/fs-ext": "^2.0.3",
@@ -60,7 +62,7 @@
60
62
  "pako": "^2.1.0",
61
63
  "peggy": "^5.0.6",
62
64
  "querysub": "^0.357.0",
63
- "socket-function": "^1.1.11",
65
+ "socket-function": "^1.1.18",
64
66
  "terser": "^5.31.0",
65
67
  "typesafecss": "^0.28.0",
66
68
  "yaml": "^2.5.0",
@@ -82,15 +82,18 @@ const getDiskMetricsBase = async () => {
82
82
  usedCacheBytes += info.size;
83
83
  usedCacheFiles++;
84
84
  } else {
85
- // TEMP files, and... any files?
86
- // If it's too old, delete it
87
- let stat = await fs.promises.stat(cacheArchives2 + file);
88
- let threshold = Date.now() - TEMP_THRESHOLD;
89
- if (stat.mtimeMs < threshold) {
90
- try {
91
- await fs.promises.unlink(cacheArchives2 + file);
92
- } catch { }
93
- }
85
+ try {
86
+ // TEMP files, and... any files?
87
+ // If it's too old, delete it
88
+ let stat = await fs.promises.stat(cacheArchives2 + file);
89
+ let threshold = Date.now() - TEMP_THRESHOLD;
90
+ if (stat.mtimeMs < threshold) {
91
+ try {
92
+ await fs.promises.unlink(cacheArchives2 + file);
93
+ } catch { }
94
+ }
95
+ // If we can't stat it, someone else deleted it, so that's fine...
96
+ } catch { }
94
97
  }
95
98
  }
96
99
  let processFileParallel = runInParallel({ parallelCount: 32 }, processFile);
@@ -480,7 +480,7 @@ export function decodeNodeId(nodeId: string, allowMissingThreadId?: "allowMissin
480
480
  export function decodeNodeIdAssert(nodeId: string, allowMissingThreadId?: "allowMissingThreadId"): NodeIdParts {
481
481
  let result = decodeNodeId(nodeId, allowMissingThreadId);
482
482
  if (!result) {
483
- throw new Error(`Invalid nodeId: ${nodeId}`);
483
+ throw new Error(`Invalid nodeId: ${JSON.stringify(nodeId)}`);
484
484
  }
485
485
  return result;
486
486
  }
@@ -18,7 +18,7 @@ import { formatTime } from "socket-function/src/formatting/format";
18
18
  import { waitForFirstTimeSync } from "socket-function/time/trueTimeShim";
19
19
  import { red } from "socket-function/src/formatting/logColors";
20
20
  import { isNode } from "typesafecss";
21
- import { areNodeIdsEqual, getOwnThreadId } from "../-f-node-discovery/NodeDiscovery";
21
+ import { areNodeIdsEqual, getOwnNodeId, getOwnThreadId } from "../-f-node-discovery/NodeDiscovery";
22
22
 
23
23
  let callerInfo = new Map<CallerContext, {
24
24
  reconnectNodeId: string | undefined;
@@ -109,6 +109,7 @@ export const IdentityController_getOwnPubKeyShort = lazy((): number => {
109
109
  return getShortNumber(pubKey);
110
110
  });
111
111
 
112
+
112
113
  export interface ChangeIdentityPayload {
113
114
  time: number;
114
115
  cert: string;
@@ -116,6 +117,7 @@ export interface ChangeIdentityPayload {
116
117
  serverId: string;
117
118
  mountedPort: number | undefined;
118
119
  debugEntryPoint: string | undefined;
120
+ clientIsNode: boolean;
119
121
  }
120
122
  class IdentityControllerBase {
121
123
  // IMPORTANT! We HAVE to call changeIdentity NOT JUST because we can't use peer certificates in the browser, BUT, also
@@ -133,6 +135,11 @@ class IdentityControllerBase {
133
135
  throw new Error(`Signed payload too old, ${payload.time} < ${signedThreshold} from ${caller.localNodeId} (${caller.nodeId})`);
134
136
  }
135
137
 
138
+ if (payload.clientIsNode && payload.serverId !== getOwnNodeId()) {
139
+ // This is extremely common when we reuse ports, which we do frequently for the edge nodes.
140
+ throw new Error(`You tried to contact another server. We are ${getOwnNodeId()}, you tried to contact ${payload.serverId}.`);
141
+ }
142
+
136
143
  // Verify the signature is meant for us, otherwise any other site can hijack the login!
137
144
  // (We don't have to worry about other servers on the same domain, as all servers
138
145
  // on the same domain should be the same!)
@@ -221,6 +228,7 @@ const changeIdentityOnce = cacheWeak(async function changeIdentityOnce(connectio
221
228
  certIssuer: issuer.cert.toString(),
222
229
  mountedPort: getNodeIdLocation(SocketFunction.mountedNodeId)?.port,
223
230
  debugEntryPoint: isNode() ? process.argv[1] : "browser",
231
+ clientIsNode: isNode(),
224
232
  };
225
233
  let signature = sign(threadKeyCert, payload);
226
234
  await timeoutToError(
@@ -3,11 +3,11 @@ import { getArchives } from "../-a-archives/archives";
3
3
  import { getDomain, isDevDebugbreak, isNoNetwork, isPublic } from "../config";
4
4
  import { measureBlock } from "socket-function/src/profiling/measure";
5
5
  import { isNode, sha256Hash, throttleFunction, timeInMinute, timeInSecond } from "socket-function/src/misc";
6
- import { errorToUndefinedSilent, ignoreErrors, logErrors, timeoutToUndefinedSilent } from "../errors";
6
+ import { errorToUndefinedSilent, ignoreErrors, logErrors, timeoutToError, timeoutToUndefinedSilent } from "../errors";
7
7
  import { ensureWeAreTrusted, requiresNetworkTrustHook } from "../-d-trust/NetworkTrust2";
8
8
  import { delay, runInfinitePoll, runInfinitePollCallAtStart } from "socket-function/src/batching";
9
- import { getNodeId, getNodeIdFromLocation, getNodeIdLocation } from "socket-function/src/nodeCache";
10
- import { lazy } from "socket-function/src/caching";
9
+ import { getCallFactory, getCreateCallFactory, getNodeId, getNodeIdFromLocation, getNodeIdLocation } from "socket-function/src/nodeCache";
10
+ import { cache, lazy } from "socket-function/src/caching";
11
11
  import { shuffle } from "../misc/random";
12
12
  import { blue, green, magenta, red, yellow } from "socket-function/src/formatting/logColors";
13
13
  import { PromiseObj } from "../promise";
@@ -46,8 +46,6 @@ let DISK_AUDIT_RATE = timeInMinute * 15;
46
46
  // probably is less than that). Which is around 2.5 cents on digital ocean IF we go over
47
47
  // our 1TB/month allowance.
48
48
  let API_AUDIT_RATE = timeInSecond * 30;
49
- // BUT, for now, poll less often... because I think it is lagging our 2 core potato digital ocean server.
50
- API_AUDIT_RATE = timeInMinute * 5;
51
49
  let API_AUDIT_COUNT = 12;
52
50
 
53
51
 
@@ -196,8 +194,12 @@ function addNodeIdBase(nodeId: string) {
196
194
  allNodeIds2.add(nodeId);
197
195
  onNodesChanged();
198
196
  }
199
- function setNodeIds(nodeIds: string[]) {
197
+ async function setNodeIds(nodeIds: string[]) {
200
198
  nodeIds = nodeIds.filter(x => x !== SPECIAL_NODE_ID_FOR_UNMOUNTED_NODE);
199
+ if (isNode()) {
200
+ await Promise.allSettled(nodeIds.map(checkWrongServerNodeId));
201
+ nodeIds = nodeIds.filter(nodeId => !wrongServerNodeIds.has(nodeId));
202
+ }
201
203
 
202
204
  console.info("setNodeIds", { nodeIds });
203
205
  let newNodeIds = nodeIds.filter(nodeId => !allNodeIds2.has(nodeId));
@@ -295,25 +297,74 @@ export async function triggerNodeChange() {
295
297
  }));
296
298
  }
297
299
 
300
+ // If we can connect on the same port, but it has a different thread ID, it means the old thread ID is gone. We're never going to go back to an old thread ID, and we can't have two threads on the same port.
301
+ let wrongServerNodeIds = new Set<string>();
302
+ let checkWrongServerNodeId = cache(async (nodeId: string) => {
303
+ if (wrongServerNodeIds.has(nodeId)) return;
304
+ let callFactory = await timeoutToUndefinedSilent(timeInSecond * 5, Promise.resolve(getCreateCallFactory(nodeId)));
305
+ if (!callFactory) {
306
+ if (SocketFunction.logMessages) {
307
+ console.log(`Did not find call factory for ${nodeId}`);
308
+ }
309
+ // Clear it right away, so we can check for it being alive quickly.
310
+ checkWrongServerNodeId.clear(nodeId);
311
+ return;
312
+ }
313
+ if (callFactory) {
314
+ // Not great, but... this should work well enough.
315
+ for (let i = 0; i < 10; i++) {
316
+ if (callFactory.receivedInitializeState) break;
317
+ await delay(500);
318
+ }
319
+ if (!callFactory.receivedInitializeState && SocketFunction.logMessages) {
320
+ console.log(`Did not receive initialize state from ${nodeId}`);
321
+ }
322
+ } else {
323
+ if (SocketFunction.logMessages) {
324
+ console.log(`Did not find call factory for ${nodeId}`);
325
+ }
326
+ }
327
+ if (callFactory && callFactory.realNodeId && callFactory.realNodeId !== nodeId) {
328
+ if (SocketFunction.logMessages) {
329
+ console.log(red(`Found dead thread, disconnecting node and deleting from archives ${nodeId}`));
330
+ }
331
+ wrongServerNodeIds.add(nodeId);
332
+ callFactory?.disconnect();
333
+ // Dead threads never come back, so this should be safe to do.
334
+ await archives().del(nodeId);
335
+ // Return, so we don't clear this.
336
+ return;
337
+ } else {
338
+ if (SocketFunction.logMessages) {
339
+ console.log(green(`Found live thread, node ${nodeId}, real node id ${callFactory?.realNodeId}`));
340
+ }
341
+ }
342
+
343
+ setTimeout(() => {
344
+ checkWrongServerNodeId.clear(nodeId);
345
+ }, timeInMinute * 5);
346
+ });
347
+
298
348
  async function syncArchives() {
299
349
  if (isServer()) {
300
350
  // Make sure we are present
301
351
  await writeHeartbeat();
302
352
  let nodeIds = await archives().find("");
303
353
  console.log(green(`Syncing node ids from archives`), { nodeIds });
304
- setNodeIds(nodeIds);
354
+ console.log(green(`Synced node ids from archives`), { nodeIds });
355
+ await setNodeIds(nodeIds);
305
356
  } else {
306
357
  if (isNoNetwork() || !isNode()) {
307
358
  // NOTE: If no network, our trust source might be different, so we can't talk to regular nodes,
308
359
  // and instead have to only talk to HTTP nodes
309
- setNodeIds([getBrowserUrlNode()]);
360
+ await setNodeIds([getBrowserUrlNode()]);
310
361
  } else {
311
362
  // If on the network, NetworkTrust2 should sync the trusted machines from backblaze, so we should be
312
363
  // able to talk to any nodes.
313
364
  // - If they user is using --client they only want to talk to querysub nodes. There might be multiple,
314
365
  // which cloudflare will proxy, HOWEVER, it is more efficient to directly access the node list, which
315
366
  // will be better for load balancing and updating on failure than the cloudflare proxying... probably.
316
- setNodeIds(await NodeDiscoveryController.nodes[getBrowserUrlNode()].getAllNodeIds());
367
+ await setNodeIds(await NodeDiscoveryController.nodes[getBrowserUrlNode()].getAllNodeIds());
317
368
  }
318
369
  }
319
370
  }
@@ -332,7 +383,7 @@ async function runHeartbeatAuditLoop() {
332
383
  let deadTime = Date.now() - DEAD_THRESHOLD;
333
384
  let nodeIds = await archives().find("");
334
385
  // We spent the money checking the node list, so we might as well update it
335
- setNodeIds(nodeIds);
386
+ await setNodeIds(nodeIds);
336
387
 
337
388
  let pendingDeadCount = 0;
338
389
 
@@ -565,6 +616,7 @@ class NodeDiscoveryControllerBase {
565
616
  public async addNode(nodeId: string) {
566
617
  console.log(magenta(`Received addNode`), { nodeId });
567
618
  addNodeId(nodeId);
619
+ return true;
568
620
  }
569
621
  public async resyncNodes(reason: string) {
570
622
  let caller = SocketFunction.getCaller();
@@ -589,6 +641,7 @@ const NodeDiscoveryController = SocketFunction.register(
589
641
  "NodeDiscoveryController-7991037e-fd9e-4085-b1db-52035487e72c",
590
642
  new NodeDiscoveryControllerBase(),
591
643
  () => ({
644
+ getOwnNodeId: { noClientHooks: true, noDefaultHooks: true },
592
645
  addNode: { hooks: [requiresNetworkTrustHook] },
593
646
  resyncNodes: { hooks: [requiresNetworkTrustHook] },
594
647
  getAllNodesHash: { hooks: [requiresNetworkTrustHook] },
@@ -5,7 +5,7 @@ import { archiveJSONT } from "../-a-archives/archivesJSONT";
5
5
  import { getDomain, isPublic } from "../config";
6
6
  import { cache, lazy } from "socket-function/src/caching";
7
7
  import { SocketFunction } from "socket-function/SocketFunction";
8
- import { runInSerial, runInfinitePollCallAtStart } from "socket-function/src/batching";
8
+ import { delay, runInSerial, runInfinitePollCallAtStart } from "socket-function/src/batching";
9
9
  import { getAllNodeIds, getOwnNodeId, isOwnNodeId, onNodeBroadcasted, syncNodesNow, watchDeltaNodeIds, watchNodeIds } from "../-f-node-discovery/NodeDiscovery";
10
10
  import { IdentityController_getCurrentReconnectNodeIdAssert } from "../-c-identity/IdentityController";
11
11
  import { requiresNetworkTrustHook } from "../-d-trust/NetworkTrust2";
@@ -16,6 +16,7 @@ import { AuthoritySpec } from "./PathRouter";
16
16
  import { formatTime } from "socket-function/src/formatting/format";
17
17
  import { getAllAuthoritySpec, getEmptyAuthoritySpec } from "./PathRouterServerAuthoritySpec";
18
18
 
19
+ setImmediate(() => import("../3-path-functions/syncSchema"));
19
20
 
20
21
  let NETWORK_POLL_INTERVAL = timeInMinute * 5;
21
22
  let CALL_TIMEOUT = isPublic() ? timeInSecond * 20 : timeInSecond * 3;
@@ -46,11 +47,15 @@ class AuthorityLookup {
46
47
  }
47
48
 
48
49
  public getTopologySync() {
49
- if (!this.didInitialSync) throw new Error("Cannot call getTopologySync without calling syncAllNow at some point first.");
50
+ if (!this.didInitialSync) {
51
+ require("debugbreak")(2);
52
+ debugger;
53
+ throw new Error("Cannot call getTopologySync without awaiting syncAllNow or startSyncing.");
54
+ }
50
55
  return Array.from(this.topology.nodes.values()).filter(x => x.isReady);
51
56
  }
52
57
  public getAuthoritySpecForNodeId(nodeId: string): AuthoritySpec | undefined {
53
- if (!this.didInitialSync) throw new Error("Cannot call getAuthoritySpecForNodeId without calling syncAllNow at some point first.");
58
+ if (!this.didInitialSync) throw new Error("Cannot call getAuthoritySpecForNodeId without awaiting syncAllNow or startSyncing.");
54
59
  return this.topology.nodes.get(nodeId)?.authoritySpec;
55
60
  }
56
61
 
@@ -201,9 +206,14 @@ class AuthorityLookup {
201
206
  if (isClient()) {
202
207
  // Doesn't matter what the node ID is, we should really only be connecting to the browser node ID, Which will have all the data for the current domain.
203
208
  // - Get all node IDs should restrict our nodes to just the browser node ID. If we ever change this, then either it's redundant nodes and they all have all the same data, or we need to figure out what data they have, And as their proxies, it probably won't be their actual authority data. So that will require new API functions, etc.
209
+ await new Promise(r => setImmediate(r));
210
+ await delay(1);
211
+ let { getPrefixesForDeploy } = await import("../3-path-functions/syncSchema");
204
212
  this.updatePaths(nodeId, {
205
- ...getAllAuthoritySpec(),
206
213
  nodeId: nodeId,
214
+ prefixes: await getPrefixesForDeploy(),
215
+ routeStart: 0,
216
+ routeEnd: 1,
207
217
  }, true);
208
218
  return;
209
219
  }