querysub 0.461.0 → 0.463.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 (95) hide show
  1. package/package.json +1 -1
  2. package/src/-d-trust/NetworkTrust2.ts +1 -1
  3. package/src/0-path-value-core/LockWatcher2.ts +2 -5
  4. package/src/0-path-value-core/PathRouter.ts +2 -3
  5. package/src/0-path-value-core/PathRouterConstants.ts +4 -0
  6. package/src/0-path-value-core/PathValueCommitter.ts +0 -1
  7. package/src/0-path-value-core/PathValueController.ts +1 -1
  8. package/src/0-path-value-core/PathWatcher.ts +14 -5
  9. package/src/0-path-value-core/ValidStateComputer.ts +234 -86
  10. package/src/0-path-value-core/pathValueCore.ts +57 -78
  11. package/src/1-path-client/RemoteWatcher.ts +4 -3
  12. package/src/1-path-client/pathValueClientWatcher.ts +28 -2
  13. package/src/2-proxy/PathValueProxyWatcher.ts +29 -24
  14. package/src/2-proxy/TransactionDelayer.ts +44 -22
  15. package/src/2-proxy/archiveMoveHarness.ts +2 -2
  16. package/src/3-path-functions/PathFunctionRunner.ts +30 -22
  17. package/src/3-path-functions/PathFunctionRunnerMain.ts +1 -3
  18. package/src/4-deploy/deployFunctions.ts +1 -1
  19. package/src/4-deploy/deployMain.ts +1 -1
  20. package/src/4-deploy/edgeClientWatcher.tsx +1 -1
  21. package/src/4-deploy/edgeNodes.ts +1 -1
  22. package/src/4-dom/qreactTest.tsx +1 -1
  23. package/src/4-querysub/Querysub.ts +8 -9
  24. package/src/4-querysub/QuerysubController.ts +19 -1
  25. package/src/4-querysub/permissions.ts +1 -1
  26. package/src/4-querysub/predictionQueue.tsx +1 -1
  27. package/src/4-querysub/querysubPrediction.ts +25 -12
  28. package/src/5-diagnostics/GenericFormat.tsx +1 -1
  29. package/src/5-diagnostics/qreactDebug.tsx +2 -2
  30. package/src/archiveapps/archiveGCEntry.tsx +1 -1
  31. package/src/archiveapps/archiveJoinEntry.ts +3 -3
  32. package/src/config.ts +5 -1
  33. package/src/config2.ts +9 -7
  34. package/src/deployManager/components/CommitModal.tsx +1 -1
  35. package/src/deployManager/components/DeployProgressView.tsx +1 -1
  36. package/src/deployManager/components/MachineDetailPage.tsx +1 -1
  37. package/src/deployManager/components/MachinesListPage.tsx +1 -1
  38. package/src/deployManager/components/ServiceDetailPage.tsx +1 -1
  39. package/src/deployManager/components/ServicesListPage.tsx +1 -1
  40. package/src/deployManager/components/Tools.tsx +1 -1
  41. package/src/deployManager/machineApplyMainCode.ts +3 -3
  42. package/src/deployManager/machineController.ts +1 -1
  43. package/src/deployManager/machineSchema.ts +1 -1
  44. package/src/deployManager/setupMachineMain.ts +1 -1
  45. package/src/diagnostics/FunctionCallInfoState.ts +6 -3
  46. package/src/diagnostics/MachineThreadInfo.tsx +1 -1
  47. package/src/diagnostics/NodeViewer.tsx +2 -1
  48. package/src/diagnostics/StatWarning.tsx +56 -0
  49. package/src/diagnostics/StatsHeader.tsx +248 -0
  50. package/src/diagnostics/StatsOverrides.ts +50 -0
  51. package/src/diagnostics/SyncTestPage.tsx +3 -3
  52. package/src/diagnostics/TimeDebug.tsx +1 -1
  53. package/src/diagnostics/debugger/mcp-server.ts +1 -1
  54. package/src/diagnostics/grossStats/GrossStatsPage.tsx +1 -1
  55. package/src/diagnostics/logs/IndexedLogs/LogViewer3.tsx +1 -1
  56. package/src/diagnostics/logs/IndexedLogs/MCPIndexedLogsEntry.ts +1 -1
  57. package/src/diagnostics/logs/TimeRangeSelector.tsx +1 -1
  58. package/src/diagnostics/logs/errorNotifications2/ErrorNotificationPage.tsx +1 -1
  59. package/src/diagnostics/logs/errorNotifications2/ErrorWarning.tsx +0 -1
  60. package/src/diagnostics/logs/errorNotifications2/errorNotifications.ts +1 -1
  61. package/src/diagnostics/logs/errorNotifications2/errorWatchEntry.ts +1 -1
  62. package/src/diagnostics/logs/errorNotifications2/errorWatcher.ts +1 -1
  63. package/src/diagnostics/logs/lifeCycleAnalysis/LifeCycleEntryEditor.tsx +1 -1
  64. package/src/diagnostics/logs/lifeCycleAnalysis/LifeCycleEntryReadMode.tsx +1 -1
  65. package/src/diagnostics/logs/lifeCycleAnalysis/LifeCyclePage.tsx +1 -1
  66. package/src/diagnostics/logs/lifeCycleAnalysis/LifeCycleRenderer.tsx +1 -1
  67. package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycleSearch.tsx +1 -1
  68. package/src/diagnostics/managementPages.tsx +4 -9
  69. package/src/diagnostics/misc-pages/ArchiveViewer.tsx +1 -1
  70. package/src/diagnostics/misc-pages/ArchiveViewerTree.tsx +1 -1
  71. package/src/diagnostics/misc-pages/DNSPage.tsx +1 -1
  72. package/src/diagnostics/pathAuditer.ts +6 -6
  73. package/src/diagnostics/statsDefinitions.tsx +253 -0
  74. package/src/functional/throttleRender.ts +1 -1
  75. package/src/library-components/AspectSizedComponent.tsx +1 -1
  76. package/src/library-components/Button.tsx +1 -1
  77. package/src/library-components/ButtonSelector.tsx +1 -1
  78. package/src/library-components/DropdownCustom.tsx +1 -1
  79. package/src/library-components/DropdownSelector.tsx +1 -1
  80. package/src/library-components/Histogram.tsx +1 -1
  81. package/src/library-components/InlinePopup.tsx +1 -1
  82. package/src/library-components/LazyComponent.tsx +5 -1
  83. package/src/library-components/StickyBottomScroll.tsx +1 -1
  84. package/src/library-components/TypedConfigEditor.tsx +1 -1
  85. package/src/library-components/URLParam.ts +5 -9
  86. package/src/library-components/drag.ts +1 -1
  87. package/src/misc/formatJSX.tsx +1 -1
  88. package/src/user-implementation/userData.ts +1 -1
  89. package/test2.ts +1 -1
  90. package/testEntry2.ts +1 -1
  91. package/valid.md +205 -0
  92. package/src/deployManager/LaunchTrackingHeader.tsx +0 -65
  93. package/src/diagnostics/FunctionCallInfo.tsx +0 -141
  94. package/src/diagnostics/PathDistributionInfo.tsx +0 -110
  95. package/src/diagnostics/ValuePathWarning.tsx +0 -68
@@ -3,7 +3,7 @@ import { blue, green, red } from "socket-function/src/formatting/logColors";
3
3
  import { measureBlock, measureWrap } from "socket-function/src/profiling/measure";
4
4
  import { FileInfo, ArchiveTransaction } from "../0-path-value-core/archiveLocks/ArchiveLocks";
5
5
  import { DecodedValuePath, pathValueArchives, PathValueArchives } from "../0-path-value-core/pathValueArchives";
6
- import { PathValue, VALUE_GC_THRESHOLD, FILE_VALUE_COUNT_LIMIT, FILE_SIZE_LIMIT, compareTime } from "../0-path-value-core/pathValueCore";
6
+ import { PathValue, FILE_VALUE_COUNT_LIMIT, FILE_SIZE_LIMIT, compareTime, ARCHIVE_STABLE_THRESHOLD } from "../0-path-value-core/pathValueCore";
7
7
  import { getSingleSizeEstimate } from "../5-diagnostics/shared";
8
8
  import { logNodeStats } from "../-0-hooks/hooks";
9
9
  import debugbreak from "debugbreak";
@@ -67,7 +67,7 @@ export async function runArchiveMover(config: {
67
67
  }>;
68
68
  }) {
69
69
  let authority = config.authority ?? await getOurAuthoritySpec(true);
70
- let minAge = config.readLiveData ? Number.NEGATIVE_INFINITY : VALUE_GC_THRESHOLD;
70
+ let minAge = config.readLiveData ? Number.NEGATIVE_INFINITY : ARCHIVE_STABLE_THRESHOLD;
71
71
  let minThreshold = Date.now() - minAge;
72
72
  // NOTE: If the value count is too high, parsing in the ArchiveViewer might crash the browser.
73
73
  const maxFileValueCount = config.maxFileValueCount ?? FILE_VALUE_COUNT_LIMIT;
@@ -9,7 +9,7 @@ import { getPathFromStr, getPathIndex } from "../path";
9
9
  import { rawSchema } from "../2-proxy/pathDatabaseProxyBase";
10
10
  import { getProxyPath } from "../2-proxy/pathValueProxy";
11
11
  import { atomicObjectRead, atomicObjectWrite, doProxyOptions, isProxyBlockedByOrder, isSynced, proxyWatcher } from "../2-proxy/PathValueProxyWatcher";
12
- import { authorityStorage, compareTime, debugTime, MAX_ACCEPTED_CHANGE_AGE, parseDebugTime, PathValue, Time } from "../0-path-value-core/pathValueCore";
12
+ import { authorityStorage, compareTime, debugTime, epochTime, MAX_ACCEPTED_CHANGE_AGE, parseDebugTime, PathValue, Time } from "../0-path-value-core/pathValueCore";
13
13
  import { getModuleFromSpec } from "./pathFunctionLoader";
14
14
  import debugbreak from "debugbreak";
15
15
  import { parseArgs } from "./PathFunctionHelpers";
@@ -163,6 +163,10 @@ function getDebugName(call: CallSpec, functionSpec: FunctionSpec | undefined, co
163
163
  }
164
164
  let pathName = getPathFromStr(functionSpec.exportPathStr).slice(1).join(".");
165
165
  let mainPart = `${call.DomainName}:${functionSpec.FilePath}|${pathName}|${debugTime(call.runAtTime)}`;
166
+ let caller = proxyWatcher.getTriggeredWatcherMaybeUndefined();
167
+ if (caller && caller.nextWriteTime) {
168
+ mainPart += ` (@ ${debugTime(caller.nextWriteTime)})`;
169
+ }
166
170
  if (colored) {
167
171
  return `${magenta(mainPart)}`;
168
172
  } else {
@@ -355,7 +359,10 @@ export class PathFunctionRunner {
355
359
  // but the result is by definition not, so... it is one of the values we check!
356
360
  if (!isSynced(moduleData.Results[callId])) continue;
357
361
 
358
- if (runningCalls.has(callId)) continue;
362
+ if (runningCalls.has(callId)) {
363
+ console.log(`Skipping run of already running call ${callId} at ${debugTime(callData.runAtTime)}`);
364
+ continue;
365
+ }
359
366
  runningCalls.add(callId);
360
367
 
361
368
  let limitCount = queueLimitCounts.get(callId) || 0;
@@ -460,7 +467,7 @@ export class PathFunctionRunner {
460
467
  let fraction = getRoutingOverridePart(callPath.CallId)?.route;
461
468
  if (fraction === undefined) {
462
469
  fraction = PathRouter.getSingleKeyRoute(callPath.CallId);
463
- console.error(`No routing override found for callId, falling back to direct hash`, { callId: callPath.CallId, fraction, domainName: callPath.DomainName, moduleId: callPath.ModuleId, functionId: callPath.FunctionId, callerMachineId: callPath.callerMachineId, callerIP: callPath.callerIP });
470
+ console.error(`No routing override found for callId (missing keyOverride fnc for ${callPath.ModuleId}.${callPath.FunctionId}), falling back to direct hash (this WILL cause rejections if this function is called a lot, ADD a keyOverride function in the definition)`, { callId: callPath.CallId, fraction, domainName: callPath.DomainName, moduleId: callPath.ModuleId, functionId: callPath.FunctionId, callerMachineId: callPath.callerMachineId, callerIP: callPath.callerIP });
464
471
  }
465
472
  // It isn't secondary if it is primary
466
473
  if (shardRange.startFraction <= fraction && fraction < shardRange.endFraction) return 0;
@@ -479,23 +486,21 @@ export class PathFunctionRunner {
479
486
  );
480
487
  };
481
488
 
482
- private callsRemainingInTick = cache((wait: true) => {
483
- // NOTE: This gives us 100K calls per second, which is more than we can run anyways.
484
- // We just need to wait a bit, otherwise the IO loop can never run, and so our finished calls
485
- // can never actually finish, until eventually they are too late to finish and get rejected!
486
- let tickDone = delay(10);
487
- void tickDone.finally(() => {
488
- this.callsRemainingInTick.clear(true);
489
- });
490
- return { calls: 1000, tickDone };
491
- });
492
- private async letIOClear(): Promise<void> {
493
- while (true) {
494
- let callsObj = this.callsRemainingInTick(true);
495
- callsObj.calls--;
496
- if (callsObj.calls >= 0) return;
497
- await callsObj.tickDone;
489
+ private pendingTickWaiters: { runAtTime: Time; resolve: () => void; }[] | undefined;
490
+ private async letIOClear(runAtTime: Time): Promise<void> {
491
+ if (!this.pendingTickWaiters) {
492
+ let waiters: { runAtTime: Time; resolve: () => void; }[] = [];
493
+ this.pendingTickWaiters = waiters;
494
+ void delay(100).then(() => {
495
+ this.pendingTickWaiters = undefined;
496
+ waiters.sort((a, b) => compareTime(a.runAtTime, b.runAtTime));
497
+ for (let w of waiters) w.resolve();
498
+ });
498
499
  }
500
+ let waiters = this.pendingTickWaiters;
501
+ await new Promise<void>(resolve => {
502
+ waiters.push({ runAtTime, resolve });
503
+ });
499
504
  }
500
505
 
501
506
  private callStats = new Map<string, CallStats>();
@@ -558,7 +563,7 @@ export class PathFunctionRunner {
558
563
  gitRef: functionSpec.gitRef,
559
564
  });
560
565
 
561
- await this.letIOClear();
566
+ await this.letIOClear(callSpec.runAtTime);
562
567
 
563
568
  let secondaryDelay = this.getSecondaryShardDelay(callSpec);
564
569
  if (secondaryDelay) {
@@ -589,7 +594,7 @@ export class PathFunctionRunner {
589
594
 
590
595
  PathFunctionRunner.RUN_START_COUNT++;
591
596
  if (PathFunctionRunner.DEBUG_CALLS) {
592
- console.log(`STARTING ${debugNameColored}`);
597
+ console.log(`STARTING ${debugNameColored} at realtime ${Date.now()}`);
593
598
  }
594
599
  let startTime = Date.now();
595
600
  let runCount = 0;
@@ -644,6 +649,8 @@ export class PathFunctionRunner {
644
649
 
645
650
  await proxyWatcher.commitFunction({
646
651
  canWrite: true,
652
+ // IMPORTANT! If we wait, it actually means that we could get the rejection before we finish the run call function. So the rejection will be suppressed because we'll think we'll still inside of a call. So that's bad. So we can't wait for the commit.
653
+ noWaitForCommit: true,
647
654
  debugName: debugName,
648
655
  source: debugName,
649
656
  runAtTime: callSpec.runAtTime,
@@ -819,7 +826,7 @@ export class PathFunctionRunner {
819
826
  }
820
827
 
821
828
  if (PathFunctionRunner.DEBUG_CALLS) {
822
- console.log(`FINISHED${nooped ? " (skipped)" : ""} ${debugNameColored}, writes: ${finalWrites?.length}, took ${blue(formatTime(Date.now() - startTime))}`);
829
+ console.log(`FINISHED${nooped ? " (skipped)" : ""} ${debugNameColored}, writes: ${finalWrites?.length}, took ${blue(formatTime(Date.now() - startTime))} ran at ${debugTime(finalWrites?.[0]?.time || epochTime)}`);
823
830
  }
824
831
 
825
832
  let wallTime = Date.now() - startTime;
@@ -873,6 +880,7 @@ export class PathFunctionRunner {
873
880
 
874
881
  await proxyWatcher.commitFunction({
875
882
  canWrite: true,
883
+ noWaitForCommit: true,
876
884
  // NOTE: We don't set runAtTime, because the rejection might be due to the call being too old
877
885
  debugName: `error (${getDebugName(callSpec, functionSpec)})`,
878
886
  eventWrite: true,
@@ -24,7 +24,7 @@ import { IndexedLogs } from "../diagnostics/logs/IndexedLogs/IndexedLogs";
24
24
 
25
25
  async function main() {
26
26
  const { PermissionsCheck } = await import("../4-querysub/permissions");
27
- const { Querysub } = await import("../4-querysub/QuerysubController");
27
+ const { Querysub } = await import("../4-querysub/Querysub");
28
28
  Error.stackTraceLimit = 20;
29
29
 
30
30
  //ActionsHistory.LOG_ACTION_HISTORY = "runner";
@@ -34,8 +34,6 @@ async function main() {
34
34
  // ClientWatcher.DEBUG_TRIGGERS = "heavy";
35
35
  // authorityStorage.DEBUG_UNWATCH = true;
36
36
 
37
- Querysub.SEND_FULL_HISTORY_ON_INITIAL_SYNC = true;
38
-
39
37
  PathFunctionRunner.DEBUG_CALLS = true;
40
38
  // debugCoreMode();
41
39
 
@@ -1,6 +1,6 @@
1
1
  import { magenta } from "socket-function/src/formatting/logColors";
2
2
  import { FunctionSpec, preloadFunctions } from "../3-path-functions/PathFunctionRunner";
3
- import { Querysub } from "../4-querysub/QuerysubController";
3
+ import { Querysub } from "../4-querysub/Querysub";
4
4
  import { errorify, timeoutToError } from "../errors";
5
5
  import { runPromise } from "../functional/runCommand";
6
6
  import path from "path";
@@ -7,7 +7,7 @@ import { blue, red } from "socket-function/src/formatting/logColors";
7
7
  import { timeInHour } from "socket-function/src/misc";
8
8
  import { shutdown } from "../diagnostics/periodic";
9
9
  import { deployFunctions, deployGetFunctions } from "./deployFunctions";
10
- import { Querysub } from "../4-querysub/QuerysubController";
10
+ import { Querysub } from "../4-querysub/Querysub";
11
11
  import { setShardPrefixes } from "../0-path-value-core/ShardPrefixes";
12
12
 
13
13
  let yargObj = yargs(process.argv)
@@ -1,5 +1,5 @@
1
1
  import { SocketFunction } from "socket-function/SocketFunction";
2
- import { Querysub } from "../4-querysub/QuerysubController";
2
+ import { Querysub } from "../4-querysub/Querysub";
3
3
  import { deploySchema } from "./deploySchema";
4
4
  import { getDomain, noSyncing } from "../config";
5
5
  import { throttleFunction, timeInMinute, timeInSecond } from "socket-function/src/misc";
@@ -20,7 +20,7 @@ import { blue, magenta } from "socket-function/src/formatting/logColors";
20
20
  import { errorToUndefined } from "../errors";
21
21
  import { deploySchema } from "./deploySchema";
22
22
  import { atomic, proxyWatcher } from "../2-proxy/PathValueProxyWatcher";
23
- import { Querysub } from "../4-querysub/QuerysubController";
23
+ import { Querysub } from "../4-querysub/Querysub";
24
24
  import { onEdgeNodesChanged } from "./edgeBootstrap";
25
25
  import { startEdgeNotifier } from "./edgeClientWatcher";
26
26
  import { getGitRefLive, getGitURLLive } from "./git";
@@ -8,7 +8,7 @@ import { isDeploy } from "../4-deploy/deployCheck";
8
8
  import { logErrors } from "../errors";
9
9
  import { watchFilesAndTriggerHotReloading } from "socket-function/hot/HotReloadController";
10
10
  import { qreact } from "./qreact";
11
- import { Querysub } from "../4-querysub/QuerysubController";
11
+ import { Querysub } from "../4-querysub/Querysub";
12
12
  import { PermissionsCheck } from "../4-querysub/permissions";
13
13
  import { delay } from "socket-function/src/batching";
14
14
  import { formatTime } from "socket-function/src/formatting/format";
@@ -18,7 +18,7 @@ import { RequireController, setRequireBootRequire } from "socket-function/requir
18
18
  import { cache, cacheLimited, lazy } from "socket-function/src/caching";
19
19
  import { getOwnMachineId, getOwnThreadId, getThreadKeyCert, verifyMachineIdForPublicKey } from "../-a-auth/certs";
20
20
  import { getHostedIP, getSNICerts, publishMachineARecords } from "../-e-certs/EdgeCertController";
21
- import { debugCoreMode, registerGetCompressNetwork, authorityStorage } from "../0-path-value-core/pathValueCore";
21
+ import { debugCoreMode, registerGetCompressNetwork, authorityStorage, enableDebugRejections } from "../0-path-value-core/pathValueCore";
22
22
  import { clientWatcher, ClientWatcher } from "../1-path-client/pathValueClientWatcher";
23
23
  import { SyncWatcher, proxyWatcher, specialObjectWriteValue, isSynced, PathValueProxyWatcher, atomic, doAtomicWrites, noAtomicSchema, undeleteFromLookup, registerSchemaPrefix, WatcherOptions, doProxyOptions } from "../2-proxy/PathValueProxyWatcher";
24
24
  import { isInProxyDatabase, rawSchema } from "../2-proxy/pathDatabaseProxyBase";
@@ -26,7 +26,6 @@ import { isValueProxy2, getProxyPath } from "../2-proxy/pathValueProxy";
26
26
  import { getCurrentCallAllowUndefined, getCurrentCall, CallSpec, PathFunctionRunner } from "../3-path-functions/PathFunctionRunner";
27
27
  import { logErrors } from "../errors";
28
28
  import { getLastPathPart, getPathIndexAssert, getPathStr2 } from "../path";
29
- import { QuerysubController, anyPredictionsPending, flushDelayedFunctions, onCallPredict, waitUntilAllPredictionsFinish } from "./QuerysubController";
30
29
  import { PermissionsCheck } from "./permissions";
31
30
  import { inlineNestedCalls, syncSchema } from "../3-path-functions/syncSchema";
32
31
  import type { identityStorageKey, IdentityStorageType } from "../-a-auth/certs";
@@ -75,6 +74,7 @@ let yargObj = parseArgsFactory()
75
74
  .option("verboseframework", { type: "boolean", desc: "Log internal SocketFunction framework" })
76
75
  .option("nodelay", { type: "boolean", desc: "Don't delay committing functions, even ones that are marked to be delayed." })
77
76
  .option("hot", { type: "boolean", desc: "force hot reloading to turn on even if public is true." })
77
+ .option("ngc", { type: "boolean", desc: "Never GC values, useful for debugging." })
78
78
  .argv
79
79
  ;
80
80
  setImmediate(() => {
@@ -97,6 +97,8 @@ setImmediate(() => {
97
97
  ClientWatcher.DEBUG_READS = true;
98
98
  PathFunctionRunner.DEBUG_CALLS = true;
99
99
  Querysub.DEBUG_CALLS = true;
100
+ authorityStorage.DEBUG_UNWATCH = true;
101
+ enableDebugRejections();
100
102
  }
101
103
  if (yargObj.verbosenetwork) {
102
104
  SocketFunction.logMessages = true;
@@ -107,6 +109,9 @@ setImmediate(() => {
107
109
  if (yargObj.nodelay) {
108
110
  Querysub.DELAY_COMMIT_DELAY = 0;
109
111
  }
112
+ if (yargObj.ngc) {
113
+ authorityStorage.DEBUG_NEVER_GC = true;
114
+ }
110
115
  });
111
116
 
112
117
  if (!registerGetCompressNetwork) {
@@ -202,12 +207,6 @@ export class Querysub {
202
207
  */
203
208
  public static MAX_FUTURE_CALL_TIME = 10_000;
204
209
 
205
- /** NOTE: Is required for the function runner to work. Eventually, we should allow you to request the full history on just specific paths.
206
- * - It's not *actually* the full history, As the underlying path value storage will delete values that are past the golden threshold anyways.
207
- */
208
- public static SEND_FULL_HISTORY_ON_INITIAL_SYNC = false;
209
-
210
-
211
210
  public static COMPRESS_NETWORK = true;
212
211
 
213
212
  public static now = getSyncedTime;
@@ -1361,4 +1360,4 @@ import { LOCAL_DOMAIN } from "../0-path-value-core/PathRouter";
1361
1360
  import { authorityLookup } from "../0-path-value-core/AuthorityLookup";
1362
1361
  import { encodeParentFilter } from "../0-path-value-core/hackedPackedPathParentFiltering";
1363
1362
  import { AliveChecker, registerAliveChecker } from "../2-proxy/garbageCollection";
1364
-
1363
+ import { QuerysubController, anyPredictionsPending, flushDelayedFunctions, onCallPredict, waitUntilAllPredictionsFinish } from "./QuerysubController";
@@ -43,7 +43,6 @@ import { authorityLookup } from "../0-path-value-core/AuthorityLookup";
43
43
  import { PathValueArchives } from "../0-path-value-core/pathValueArchives";
44
44
 
45
45
 
46
- export { Querysub, id };
47
46
 
48
47
 
49
48
  // TODO: Eventually this will be split by domain, which required nodeIds to be split by domain, so that getControllerNodeId can check nodeIds on a specific domain.
@@ -684,12 +683,28 @@ export class QuerysubControllerBase {
684
683
  }
685
684
  return result;
686
685
  }
686
+ public async debugGetDetailedSyncing(): Promise<Map<string, number>> {
687
+ let callerId = SocketFunction.getCaller().nodeId;
688
+ let watchers = pathWatcher.debugGetWatcherPaths(callerId);
689
+ let result = new Map<string, { count: number }>();
690
+ for (let path of watchers?.paths ?? []) {
691
+ let nodeId = remoteWatcher.getValueWatchRemoteNodeId(path) ?? "UNKNOWN";
692
+ let obj = result.get(nodeId);
693
+ if (!obj) {
694
+ obj = { count: 0 };
695
+ result.set(nodeId, obj);
696
+ }
697
+ obj.count++;
698
+ }
699
+ return new Map(Array.from(result.entries()).map(([nodeId, obj]) => [nodeId, obj.count]));
700
+ }
687
701
 
688
702
  public async debugGetValuePathCount(): Promise<number> {
689
703
  return PathValueArchives.getValuePathCount();
690
704
  }
691
705
  }
692
706
 
707
+ let lazyManagementPages = () => require("../diagnostics/managementPages") as typeof import("../diagnostics/managementPages");
693
708
  export const QuerysubController = SocketFunction.register(
694
709
  "QuerysubController-6db5ef05-7563-4473-a440-2f64f03fe6ef",
695
710
  new QuerysubControllerBase(),
@@ -701,6 +716,9 @@ export const QuerysubController = SocketFunction.register(
701
716
  // NOTE: Most of these debug functions are pretty innocuous. A lot of it is actually already exposed, and other parts of it is fine. It's fine for the user to know what node a path is on. It's fine for them to know how many value paths there are.
702
717
  debugGetPathNodeIds: {},
703
718
  debugGetNodeSpecs: {},
719
+ debugGetDetailedSyncing: {
720
+ hooks: [lazyManagementPages().assertIsManagementUser]
721
+ },
704
722
  debugGetSingleReadNode: {},
705
723
  getModulePath: {},
706
724
  getDevFunctionSpecFromCall: {},
@@ -11,7 +11,7 @@ import { ignoreErrors } from "../errors";
11
11
  import { epochTime } from "../0-path-value-core/pathValueCore";
12
12
  import { isClient } from "../config2";
13
13
  import { sort } from "socket-function/src/misc";
14
- import { Querysub } from "./QuerysubController";
14
+ import { Querysub } from "./Querysub";
15
15
  import { CALL_PERMISSIONS_KEY } from "./permissionsShared";
16
16
  import { getDomain, isLocal, isPublic } from "../config";
17
17
 
@@ -3,7 +3,7 @@ import { CallSpec, debugCallSpec, functionSchema } from "../3-path-functions/Pat
3
3
  import { FunctionMetadata } from "../3-path-functions/syncSchema";
4
4
  import { getParentPathStr, getPathFromStr, getPathStr2 } from "../path";
5
5
  import { PromiseObj } from "../promise";
6
- import { Querysub } from "./QuerysubController";
6
+ import { Querysub } from "./Querysub";
7
7
  import { PredictResult } from "./querysubPrediction";
8
8
  import { delay } from "socket-function/src/batching";
9
9
  import { blue, green, magenta, yellow } from "socket-function/src/formatting/logColors";
@@ -1,5 +1,5 @@
1
1
  import { cacheJSONArgsEqual, lazy } from "socket-function/src/caching";
2
- import { MAX_CHANGE_AGE, PathValue, ReadLock, Time, authorityStorage, predictionLockVersion } from "../0-path-value-core/pathValueCore";
2
+ import { DEFER_LOCK_WINDOW, MAX_CHANGE_AGE, PREDICT_HOLD_TIME, PathValue, ReadLock, Time, authorityStorage, predictionLockVersion } from "../0-path-value-core/pathValueCore";
3
3
  import { validStateComputer } from "../0-path-value-core/ValidStateComputer";
4
4
  import { proxyWatcher, atomicObjectRead, DryRunResult, getCurrentCallCreationProxy } from "../2-proxy/PathValueProxyWatcher";
5
5
  import { getProxyPath } from "../2-proxy/pathValueProxy";
@@ -7,7 +7,8 @@ import { CallSpec, FunctionResult, FunctionSpec, functionSchema, overrideCurrent
7
7
  import { getModuleFromConfig, setGitURLMapping } from "../3-path-functions/pathFunctionLoader";
8
8
  import { logErrors } from "../errors";
9
9
  import { getPathFromStr, getPathStr1 } from "../path";
10
- import { Querysub, QuerysubController, QuerysubControllerBase, registerPredictionBlocker, querysubNodeId } from "./QuerysubController";
10
+ import { QuerysubController, QuerysubControllerBase, registerPredictionBlocker, querysubNodeId } from "./QuerysubController";
11
+ import { Querysub } from "./Querysub";
11
12
  import { parseArgs } from "../3-path-functions/PathFunctionHelpers";
12
13
  import { magenta, red } from "socket-function/src/formatting/logColors";
13
14
  import { pathValueSerializer } from "../-h-path-value-serialize/PathValueSerializer";
@@ -24,12 +25,13 @@ import { delay } from "socket-function/src/batching";
24
25
  setFlag(require, "cbor-x", "allowclient", true);
25
26
  const cborEncoder = lazy(() => new cbor.Encoder({ structuredClone: true }));
26
27
 
27
- let onPredictionFinishedCallbacks: Array<(data: { callId: string; result: FunctionResult; functionId: string }) => void> = [];
28
+ let onPredictionFinishedCallbacks: Array<(data: { callId: string; result: FunctionResult; functionId: string; creatorId: number }) => void> = [];
28
29
 
29
30
  export function onPredictionFinished(callback: (data: {
30
31
  callId: string;
31
32
  result: FunctionResult;
32
33
  functionId: string;
34
+ creatorId: number;
33
35
  }) => void) {
34
36
  onPredictionFinishedCallbacks.push(callback);
35
37
  }
@@ -127,14 +129,19 @@ function predictCallBase(config: {
127
129
  let actualValueFinished = new PromiseObj();
128
130
  function onActualFinished() {
129
131
  if (actualValueFinished.resolveCalled) return;
130
- let resultObj = authorityStorage.getValueAtTime(pathResultWrite, undefined);
132
+ let resultObj = authorityStorage.getValueAtOrBeforeTime(pathResultWrite, undefined);
131
133
  let result = pathValueSerializer.getPathValue(resultObj) as FunctionResult | undefined;
132
134
  if (!result) return;
133
135
  if (result.lastInternalLoopCount === -1) return;
134
- void cleanupPrediction();
136
+ // NOTE: Using resultObj.transactionPaths, It is possible, although a little bit annoying, for us to determine if it's likely we are missing transaction paths because we know the prediction paths and we know if we're watching those or not. And if we're watching them and they hash the same paths in the result write, and the times are before the result write, then we know we're missing data.
137
+ // BUT... It doesn't matter because we could just wait here, and there's not really any downside to waiting here.
138
+ setTimeout(() => {
139
+ void cleanupPrediction();
140
+ }, PREDICT_HOLD_TIME);
135
141
  actualValueFinished.resolve();
142
+ let creatorId = resultObj && resultObj.time.creatorId || 0;
136
143
  for (let callback of onPredictionFinishedCallbacks) {
137
- callback({ callId: call.CallId, result, functionId: call.FunctionId });
144
+ callback({ callId: call.CallId, result, functionId: call.FunctionId, creatorId });
138
145
  }
139
146
  }
140
147
  function onApplied() {
@@ -224,7 +231,7 @@ function predictCallBase(config: {
224
231
  }
225
232
  dryRunResult = tempDryRunResult;
226
233
  } catch (e: any) {
227
- if (!pathValueSerializer.getPathValue(authorityStorage.getValueAtTime(pathResultWrite, undefined))) {
234
+ if (!pathValueSerializer.getPathValue(authorityStorage.getValueAtOrBeforeTime(pathResultWrite, undefined))) {
228
235
  console.log(`Skipping prediction for ${debugName} due to error running predictive call. Likely just an out of order error.`, e.stack);
229
236
  } else {
230
237
  // NOTE: This case happens a lot, because of how we handle locks. We don't receive locked values, and so
@@ -246,9 +253,15 @@ function predictCallBase(config: {
246
253
  readParentPaths: dryRunResult.readParentPaths,
247
254
  replacedWriteValues: dryRunResult.writes.map(write => {
248
255
  let path = write.path;
249
- let newWrite: PathValue = authorityStorage.getValueAtTime(write.path, write.time) || {
250
- path, valid: true, time: write.time, locks: [], lockCount: 0, value: undefined, canGCValue: true, isTransparent: true,
251
- };
256
+ let newWrite: PathValue | undefined = authorityStorage.getValueExactMaybeRejected(write.path, write.time);
257
+ if (newWrite && !newWrite.valid) {
258
+ newWrite = undefined;
259
+ }
260
+ if (!newWrite) {
261
+ newWrite = {
262
+ path, valid: true, time: write.time, locks: [], lockCount: 0, value: undefined, canGCValue: true, isTransparent: true,
263
+ };
264
+ }
252
265
  newWrite = { ...newWrite };
253
266
  // Use a time very slightly after, so it clobbers the write, and any writes before it, but none after.
254
267
  newWrite.time = write.time;
@@ -271,7 +284,7 @@ function predictCallBase(config: {
271
284
  // If we already received the actual call, we can't add the prediction, as... the invalidation
272
285
  // code won't properly immediately reject our prediction, as we are not the authority on the
273
286
  // path, so it treats it as source of truth.
274
- if (authorityStorage.getValueAtTime(pathResultWrite, undefined)?.value) {
287
+ if (authorityStorage.getValueAtOrBeforeTime(pathResultWrite, undefined)?.value) {
275
288
  if (Querysub.DEBUG_PREDICTIONS || Querysub.DEBUG_CALLS) {
276
289
  console.log(magenta(`Abort predict call, already received call result`), `${call.DomainName}.${call.FunctionId}`);
277
290
  }
@@ -347,7 +360,7 @@ function predictCallBase(config: {
347
360
  let predictionsCopy = cborEncoder().decode(cborEncoder().encode(predictions)) as typeof predictions;
348
361
  for (let predict of predictionsCopy.writes ?? []) {
349
362
  if (predict.path === pathResultWrite) continue;
350
- let finalValueObj = authorityStorage.getValueAtTime(predict.path, afterTime);
363
+ let finalValueObj = authorityStorage.getValueAtOrBeforeTime(predict.path, afterTime);
351
364
  if (!authorityStorage.isSynced(predict.path)) continue;
352
365
 
353
366
  let finalValue = pathValueSerializer.getPathValue(finalValueObj);
@@ -5,7 +5,7 @@ import { qreact } from "../4-dom/qreact";
5
5
  import preact, { ContextType } from "preact";
6
6
  import { errorMessage, warnMessage } from "../library-components/colors";
7
7
  import { URLParam } from "../library-components/URLParam";
8
- import { Querysub } from "../4-querysub/QuerysubController";
8
+ import { Querysub } from "../4-querysub/Querysub";
9
9
 
10
10
  export { errorMessage, warnMessage };
11
11
 
@@ -5,7 +5,7 @@ import { getPathFromStr, getPathStr } from "../path";
5
5
  import { delay } from "socket-function/src/batching";
6
6
  import { logErrors } from "../errors";
7
7
  import { clientWatcher } from "../1-path-client/pathValueClientWatcher";
8
- import { Querysub } from "../4-querysub/QuerysubController";
8
+ import { Querysub } from "../4-querysub/Querysub";
9
9
  import { closeAllModals, showModal } from "./Modal";
10
10
  import { FullscreenModal, showFullscreenModal } from "./FullscreenModal";
11
11
  import { css } from "typesafecss";
@@ -295,7 +295,7 @@ class WatchModal extends qreact.Component<{
295
295
  if (filter && !path.toLowerCase().includes(filter)) {
296
296
  return undefined;
297
297
  }
298
- let valueObj = authorityStorage.getValueAtTime(path);
298
+ let valueObj = authorityStorage.getValueAtOrBeforeTime(path);
299
299
  let value = pathValueSerializer.getPathValue(valueObj);
300
300
  let valueStr = String(value);
301
301
  try {
@@ -1,6 +1,6 @@
1
1
  import "../inject";
2
2
 
3
- import { Querysub } from "../4-querysub/QuerysubController";
3
+ import { Querysub } from "../4-querysub/Querysub";
4
4
  import { logErrors } from "../errors";
5
5
  import { isNodeTrue, timeInDay, timeInHour } from "socket-function/src/misc";
6
6
  import path from "path";
@@ -2,14 +2,14 @@ import "../inject";
2
2
 
3
3
  import { logErrors } from "../errors";
4
4
  import { PathValueArchives, pathValueArchives } from "../0-path-value-core/pathValueArchives";
5
- import { ARCHIVE_FLUSH_LIMIT, PathValue, VALUE_GC_THRESHOLD } from "../0-path-value-core/pathValueCore";
5
+ import { ARCHIVE_FLUSH_LIMIT, ARCHIVE_STABLE_THRESHOLD, PathValue } from "../0-path-value-core/pathValueCore";
6
6
  import { runInfinitePollCallAtStart } from "socket-function/src/batching";
7
7
  import { measureBlock } from "socket-function/src/profiling/measure";
8
8
  import { pathValueSerializer } from "../-h-path-value-serialize/PathValueSerializer";
9
9
  import { ArchiveTransaction, FileInfo } from "../0-path-value-core/archiveLocks/ArchiveLocks";
10
10
  import { formatNumber } from "socket-function/src/formatting/format";
11
11
  import { isNodeTrue, sort } from "socket-function/src/misc";
12
- import { Querysub } from "../4-querysub/QuerysubController";
12
+ import { Querysub } from "../4-querysub/Querysub";
13
13
  import { magenta } from "socket-function/src/formatting/logColors";
14
14
  import { PathRouter } from "../0-path-value-core/PathRouter";
15
15
  import { disablePathAuditer } from "../diagnostics/pathAuditerCallback";
@@ -129,7 +129,7 @@ async function main() {
129
129
 
130
130
  if (yargObj.watch) {
131
131
  console.log("Running in watch mode.");
132
- await runInfinitePollCallAtStart(VALUE_GC_THRESHOLD * 0.8, runGenesisJoinIteration);
132
+ await runInfinitePollCallAtStart(ARCHIVE_STABLE_THRESHOLD * 0.8, runGenesisJoinIteration);
133
133
  } else {
134
134
  try {
135
135
  await runGenesisJoinIteration({ force: true });
package/src/config.ts CHANGED
@@ -89,8 +89,12 @@ export function getNotifyEmails() {
89
89
  return Array.from(new Set([...(yargObj.notifyemails || []), ...(querysubConfig().notifyemails || [])]));
90
90
  }
91
91
 
92
+ let isClient: boolean | undefined;
92
93
  export function baseIsClient() {
93
- return !isNode() || yargObj.client;
94
+ if (isClient === undefined) {
95
+ isClient = !isNode() || yargObj.client;
96
+ }
97
+ return isClient;
94
98
  }
95
99
 
96
100
  export function isRecovery() {
package/src/config2.ts CHANGED
@@ -8,20 +8,22 @@ import { AuthoritySpec } from "./0-path-value-core/PathRouter";
8
8
  import { parseFilterable } from "./misc/filterable";
9
9
  import { lazy } from "socket-function/src/caching";
10
10
 
11
- let isInCall = false;
12
11
  export function isClient() {
13
- isInCall = true;
14
- try {
15
- return !isNode() || baseIsClient() || !isInCall && !hasArchivesPermissions();
16
- } finally {
17
- isInCall = false;
18
- }
12
+ return baseIsClient();
19
13
  }
20
14
 
21
15
  export function isServer() {
22
16
  return !isClient();
23
17
  }
24
18
 
19
+ /**
20
+ If we need to evaluate valid states, it means we need to store the full history, which is expensive, and we need to request the full history.
21
+ - AND, We cannot. have any optimizations to do with having no locks because something, just because it has no locks doesn't mean the previous values can be removed, as they might be dependent on by a different lock entirely.
22
+ */
23
+ export function evaluateValidStates() {
24
+ return isServer();
25
+ }
26
+
25
27
  // NOTE: getOurAuthorities moved to PathRouterHashOverrides, and renamed to getOurAuthoritySpec
26
28
 
27
29
 
@@ -3,7 +3,7 @@ import { SocketFunction } from "socket-function/SocketFunction";
3
3
  import { qreact } from "../../4-dom/qreact";
4
4
  import { css } from "../../4-dom/css";
5
5
  import { MachineServiceController } from "../machineSchema";
6
- import { Querysub } from "../../4-querysub/QuerysubController";
6
+ import { Querysub } from "../../4-querysub/Querysub";
7
7
  import { InputLabel } from "../../library-components/InputLabel";
8
8
  import { DropdownSelector } from "../../library-components/DropdownSelector";
9
9
  import { closeAllModals } from "../../5-diagnostics/Modal";
@@ -2,7 +2,7 @@ import { css } from "typesafecss";
2
2
  import { qreact } from "../../4-dom/qreact";
3
3
  import { formatDateJSX } from "../../misc/formatJSX";
4
4
  import { formatTime } from "socket-function/src/formatting/format";
5
- import { Querysub } from "../../4-querysub/QuerysubController";
5
+ import { Querysub } from "../../4-querysub/Querysub";
6
6
 
7
7
  module.hotreload = true;
8
8
 
@@ -9,7 +9,7 @@ import { ATag, Anchor } from "../../library-components/ATag";
9
9
  import { ShowMore } from "../../library-components/ShowMore";
10
10
  import { managementPageURL } from "../../diagnostics/managementPages";
11
11
  import { t } from "../../2-proxy/schema2";
12
- import { Querysub } from "../../4-querysub/QuerysubController";
12
+ import { Querysub } from "../../4-querysub/Querysub";
13
13
  import { getLogViewerParams } from "../../diagnostics/logs/IndexedLogs/LogViewerParams";
14
14
 
15
15
  export class MachineDetailPage extends qreact.Component {
@@ -3,7 +3,7 @@ import { qreact } from "../../4-dom/qreact";
3
3
  import { MACHINE_RESYNC_INTERVAL, MachineServiceController } from "../machineSchema";
4
4
  import { css } from "typesafecss";
5
5
  import { t } from "../../2-proxy/schema2";
6
- import { Querysub } from "../../4-querysub/QuerysubController";
6
+ import { Querysub } from "../../4-querysub/Querysub";
7
7
  import { currentViewParam, selectedMachineIdParam } from "../urlParams";
8
8
  import { formatNumber, formatVeryNiceDateTime } from "socket-function/src/formatting/format";
9
9
  import { sort } from "socket-function/src/misc";
@@ -3,7 +3,7 @@ import { qreact } from "../../4-dom/qreact";
3
3
  import { MACHINE_RESYNC_INTERVAL, MachineServiceController, ServiceConfig } from "../machineSchema";
4
4
  import { css } from "typesafecss";
5
5
  import { t } from "../../2-proxy/schema2";
6
- import { Querysub } from "../../4-querysub/QuerysubController";
6
+ import { Querysub } from "../../4-querysub/Querysub";
7
7
  import { currentViewParam, selectedServiceIdParam, selectedMachineIdParam } from "../urlParams";
8
8
  import { formatTime, formatVeryNiceDateTime } from "socket-function/src/formatting/format";
9
9
  import { InputPicker } from "../../library-components/InputPicker";
@@ -3,7 +3,7 @@ import { qreact } from "../../4-dom/qreact";
3
3
  import { MachineServiceController, ServiceConfig } from "../machineSchema";
4
4
  import { css } from "typesafecss";
5
5
  import { t } from "../../2-proxy/schema2";
6
- import { Querysub } from "../../4-querysub/QuerysubController";
6
+ import { Querysub } from "../../4-querysub/Querysub";
7
7
  import { currentViewParam, selectedServiceIdParam } from "../urlParams";
8
8
  import { formatNiceDateTime, formatTime, formatVeryNiceDateTime } from "socket-function/src/formatting/format";
9
9
  import { sort, timeInMinute } from "socket-function/src/misc";
@@ -3,7 +3,7 @@ import { SocketFunction } from "socket-function/SocketFunction";
3
3
  import { qreact } from "../../4-dom/qreact";
4
4
  import { css } from "typesafecss";
5
5
  import { t } from "../../2-proxy/schema2";
6
- import { Querysub } from "../../4-querysub/QuerysubController";
6
+ import { Querysub } from "../../4-querysub/Querysub";
7
7
  import { MachineServiceController, ServiceConfig } from "../machineSchema";
8
8
  import { MachinePicker } from "./MachinePicker";
9
9
  import { isDefined } from "../../misc";