querysub 0.403.0 → 0.405.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/.cursorrules +2 -0
  2. package/bin/audit-imports.js +4 -0
  3. package/bin/join.js +1 -1
  4. package/package.json +7 -4
  5. package/spec.txt +77 -0
  6. package/src/-a-archives/archiveCache.ts +9 -4
  7. package/src/-a-archives/archivesBackBlaze.ts +1039 -1039
  8. package/src/-a-auth/certs.ts +0 -12
  9. package/src/-c-identity/IdentityController.ts +12 -3
  10. package/src/-f-node-discovery/NodeDiscovery.ts +32 -26
  11. package/src/-g-core-values/NodeCapabilities.ts +12 -2
  12. package/src/0-path-value-core/AuthorityLookup.ts +239 -0
  13. package/src/0-path-value-core/LockWatcher2.ts +150 -0
  14. package/src/0-path-value-core/PathRouter.ts +543 -0
  15. package/src/0-path-value-core/PathRouterRouteOverride.ts +72 -0
  16. package/src/0-path-value-core/PathRouterServerAuthoritySpec.tsx +73 -0
  17. package/src/0-path-value-core/PathValueCommitter.ts +222 -488
  18. package/src/0-path-value-core/PathValueController.ts +277 -239
  19. package/src/0-path-value-core/PathWatcher.ts +534 -0
  20. package/src/0-path-value-core/ShardPrefixes.ts +31 -0
  21. package/src/0-path-value-core/ValidStateComputer.ts +303 -0
  22. package/src/0-path-value-core/archiveLocks/ArchiveLocks.ts +1 -1
  23. package/src/0-path-value-core/archiveLocks/ArchiveLocks2.ts +80 -44
  24. package/src/0-path-value-core/archiveLocks/archiveSnapshots.ts +13 -16
  25. package/src/0-path-value-core/auditLogs.ts +2 -0
  26. package/src/0-path-value-core/hackedPackedPathParentFiltering.ts +97 -0
  27. package/src/0-path-value-core/pathValueArchives.ts +491 -492
  28. package/src/0-path-value-core/pathValueCore.ts +195 -1496
  29. package/src/0-path-value-core/startupAuthority.ts +74 -0
  30. package/src/1-path-client/RemoteWatcher.ts +90 -82
  31. package/src/1-path-client/pathValueClientWatcher.ts +808 -815
  32. package/src/2-proxy/PathValueProxyWatcher.ts +10 -8
  33. package/src/2-proxy/archiveMoveHarness.ts +182 -214
  34. package/src/2-proxy/garbageCollection.ts +9 -8
  35. package/src/2-proxy/schema2.ts +21 -1
  36. package/src/3-path-functions/PathFunctionHelpers.ts +206 -180
  37. package/src/3-path-functions/PathFunctionRunner.ts +943 -766
  38. package/src/3-path-functions/PathFunctionRunnerMain.ts +5 -3
  39. package/src/3-path-functions/pathFunctionLoader.ts +2 -2
  40. package/src/3-path-functions/syncSchema.ts +596 -521
  41. package/src/4-deploy/deployFunctions.ts +19 -4
  42. package/src/4-deploy/deployGetFunctionsInner.ts +8 -2
  43. package/src/4-deploy/deployMain.ts +51 -68
  44. package/src/4-deploy/edgeClientWatcher.tsx +6 -1
  45. package/src/4-deploy/edgeNodes.ts +2 -2
  46. package/src/4-dom/qreact.tsx +2 -4
  47. package/src/4-dom/qreactTest.tsx +7 -13
  48. package/src/4-querysub/Querysub.ts +21 -8
  49. package/src/4-querysub/QuerysubController.ts +45 -29
  50. package/src/4-querysub/permissions.ts +2 -2
  51. package/src/4-querysub/querysubPrediction.ts +80 -70
  52. package/src/4-querysub/schemaHelpers.ts +5 -1
  53. package/src/5-diagnostics/GenericFormat.tsx +14 -9
  54. package/src/archiveapps/archiveGCEntry.tsx +9 -2
  55. package/src/archiveapps/archiveJoinEntry.ts +96 -84
  56. package/src/bits.ts +19 -0
  57. package/src/config.ts +21 -3
  58. package/src/config2.ts +23 -48
  59. package/src/deployManager/components/DeployPage.tsx +7 -3
  60. package/src/deployManager/machineSchema.ts +4 -1
  61. package/src/diagnostics/ActionsHistory.ts +3 -8
  62. package/src/diagnostics/AuditLogPage.tsx +2 -3
  63. package/src/diagnostics/FunctionCallInfo.tsx +141 -0
  64. package/src/diagnostics/FunctionCallInfoState.ts +162 -0
  65. package/src/diagnostics/MachineThreadInfo.tsx +1 -1
  66. package/src/diagnostics/NodeViewer.tsx +37 -48
  67. package/src/diagnostics/SyncTestPage.tsx +241 -0
  68. package/src/diagnostics/auditImportViolations.ts +185 -0
  69. package/src/diagnostics/listenOnDebugger.ts +3 -3
  70. package/src/diagnostics/logs/IndexedLogs/BufferUnitSet.ts +10 -4
  71. package/src/diagnostics/logs/IndexedLogs/IndexedLogs.ts +2 -2
  72. package/src/diagnostics/logs/IndexedLogs/LogViewer3.tsx +24 -22
  73. package/src/diagnostics/logs/IndexedLogs/moveIndexLogsToPublic.ts +1 -1
  74. package/src/diagnostics/logs/diskLogGlobalContext.ts +1 -0
  75. package/src/diagnostics/logs/errorNotifications2/logWatcher.ts +1 -3
  76. package/src/diagnostics/logs/lifeCycleAnalysis/LifeCycleEntryEditor.tsx +34 -16
  77. package/src/diagnostics/logs/lifeCycleAnalysis/LifeCycleEntryReadMode.tsx +4 -6
  78. package/src/diagnostics/logs/lifeCycleAnalysis/LifeCycleInstanceTableView.tsx +36 -5
  79. package/src/diagnostics/logs/lifeCycleAnalysis/LifeCyclePage.tsx +19 -5
  80. package/src/diagnostics/logs/lifeCycleAnalysis/LifeCycleRenderer.tsx +15 -7
  81. package/src/diagnostics/logs/lifeCycleAnalysis/NestedLifeCycleInfo.tsx +28 -106
  82. package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycleMatching.ts +2 -0
  83. package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycleMisc.ts +0 -0
  84. package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycleSearch.tsx +18 -7
  85. package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycles.tsx +3 -0
  86. package/src/diagnostics/managementPages.tsx +10 -3
  87. package/src/diagnostics/misc-pages/ArchiveViewer.tsx +20 -26
  88. package/src/diagnostics/misc-pages/ArchiveViewerTree.tsx +6 -4
  89. package/src/diagnostics/misc-pages/ComponentSyncStats.tsx +2 -2
  90. package/src/diagnostics/misc-pages/LocalWatchViewer.tsx +7 -9
  91. package/src/diagnostics/misc-pages/SnapshotViewer.tsx +23 -12
  92. package/src/diagnostics/misc-pages/archiveViewerShared.tsx +1 -1
  93. package/src/diagnostics/pathAuditer.ts +486 -0
  94. package/src/diagnostics/pathAuditerCallback.ts +20 -0
  95. package/src/diagnostics/watchdog.ts +8 -1
  96. package/src/library-components/URLParam.ts +1 -1
  97. package/src/misc/hash.ts +1 -0
  98. package/src/path.ts +21 -7
  99. package/src/server.ts +54 -47
  100. package/src/user-implementation/loginEmail.tsx +1 -1
  101. package/tempnotes.txt +65 -0
  102. package/test.ts +298 -97
  103. package/src/0-path-value-core/NodePathAuthorities.ts +0 -1057
  104. package/src/0-path-value-core/PathController.ts +0 -1
  105. package/src/5-diagnostics/diskValueAudit.ts +0 -218
  106. package/src/5-diagnostics/memoryValueAudit.ts +0 -438
  107. package/src/archiveapps/archiveMergeEntry.tsx +0 -48
  108. package/src/archiveapps/lockTest.ts +0 -127
package/src/config2.ts CHANGED
@@ -1,48 +1,23 @@
1
- import { deepCloneJSON, isNode } from "socket-function/src/misc";
2
- import { hasArchivesPermissions } from "./-a-archives/archives";
3
- import { authorityRaw, baseIsClient, getDomain } from "./config";
4
- import { JSONLACKS } from "socket-function/src/JSONLACKS/JSONLACKS";
5
- import { AuthorityPath } from "./0-path-value-core/NodePathAuthorities";
6
- import { rootPathStr, prependToPathStr } from "./path";
7
- import fs from "fs";
8
-
9
- let isInCall = false;
10
- export function isClient() {
11
- isInCall = true;
12
- try {
13
- return !isNode() || baseIsClient() || !isInCall && !hasArchivesPermissions();
14
- } finally {
15
- isInCall = false;
16
- }
17
- }
18
-
19
- export function isServer() {
20
- return !isClient();
21
- }
22
-
23
- export function getOurAuthorities() {
24
- let authorities: AuthorityPath[] = [];
25
- let authority = authorityRaw();
26
-
27
- let baseAuthorityPaths: AuthorityPath[] = [{
28
- pathPrefix: rootPathStr
29
- }];
30
- if (authority) {
31
- if (fs.existsSync(authority)) {
32
- baseAuthorityPaths = JSONLACKS.parse(fs.readFileSync(authority, "utf8"));
33
- } else {
34
- baseAuthorityPaths = JSONLACKS.parse(Buffer.from(authority, "base64").toString("utf8"));
35
- }
36
- }
37
-
38
- let domain = getDomain();
39
- for (let base of baseAuthorityPaths) {
40
- base = deepCloneJSON(base);
41
- base.pathPrefix = prependToPathStr(base.pathPrefix, domain);
42
- if (base.excludedChildren) {
43
- base.excludedChildren = base.excludedChildren.map(x => prependToPathStr(x, domain));
44
- }
45
- authorities.push(base);
46
- }
47
- return authorities;
48
- }
1
+ import { deepCloneJSON, isNode } from "socket-function/src/misc";
2
+ import { hasArchivesPermissions } from "./-a-archives/archives";
3
+ import { baseIsClient, getDomain } from "./config";
4
+ import { JSONLACKS } from "socket-function/src/JSONLACKS/JSONLACKS";
5
+ import { rootPathStr, prependToPathStr, getPathDepth } from "./path";
6
+ import fs from "fs";
7
+ import { AuthoritySpec } from "./0-path-value-core/PathRouter";
8
+
9
+ let isInCall = false;
10
+ export function isClient() {
11
+ isInCall = true;
12
+ try {
13
+ return !isNode() || baseIsClient() || !isInCall && !hasArchivesPermissions();
14
+ } finally {
15
+ isInCall = false;
16
+ }
17
+ }
18
+
19
+ export function isServer() {
20
+ return !isClient();
21
+ }
22
+
23
+ // NOTE: getOurAuthorities moved to PathRouterHashOverrides, and renamed to getOurAuthoritySpec
@@ -257,7 +257,10 @@ export class DeployPage extends qreact.Component {
257
257
  render() {
258
258
  let controller = MachineServiceController(SocketFunction.browserNodeId());
259
259
  let gitInfo = controller.getGitInfo();
260
- let pendingFunctions = controller.getPendingFunctions() || [];
260
+ let pendingFunctions = controller.getPendingFunctions() || {
261
+ functionSpecs: [],
262
+ prefixes: [],
263
+ };
261
264
  let liveFunctions = controller.getLiveFunctions() || [];
262
265
 
263
266
  let liveGitRef = mostCommon(liveFunctions.map(x => x.gitRef)) || "";
@@ -285,7 +288,8 @@ export class DeployPage extends qreact.Component {
285
288
 
286
289
  try {
287
290
  await deployFunctionsWithProgress({
288
- functionSpecs: pendingFunctions,
291
+ functionSpecs: pendingFunctions.functionSpecs,
292
+ prefixes: pendingFunctions.prefixes,
289
293
  // NOTE: Old code should always be compatible with new code, so a long delay is fine. We don't want to have deal with bug reports today for bugs we fixed tomorrow, but we also don't want to force users to refresh, so this time is longer than the expected maximum session length.
290
294
  // NOTE: Even if we want to deploy now, we can't deploy IMMEDIATELY. Give users at least 10 minutes to accept that that page will forcefully refresh.
291
295
  notifyRefreshDelay: forceDeployNowURL.value ? timeInMinute * 10 : timeInHour * 12,
@@ -379,7 +383,7 @@ export class DeployPage extends qreact.Component {
379
383
  ✅ Everything is up to date - no changes to deploy
380
384
  </div>
381
385
  ) || (
382
- FunctionDiffView({ pendingFunctions, liveFunctions })
386
+ FunctionDiffView({ pendingFunctions: pendingFunctions.functionSpecs, liveFunctions })
383
387
  )}
384
388
  </div>;
385
389
  }
@@ -118,7 +118,7 @@ async function registerNodeForMachineCleanup(nodeId: string) {
118
118
  console.log(green(`Registering node for machine cleanup at ${nodeIdPath}`));
119
119
  await fs.promises.writeFile(nodeIdPath, nodeId);
120
120
  } else {
121
- console.log(`Not registering node for machine cleanup because we are not in the service folder: ${currentPath}`);
121
+ console.log(`Not registering node for machine cleanup because we are not in the service folder (${SERVICE_FOLDER_NAME}), and are instead in: ${currentPath}`);
122
122
  }
123
123
  }
124
124
 
@@ -358,6 +358,7 @@ export class MachineServiceControllerBase {
358
358
 
359
359
  public async deployFunctions(config: {
360
360
  functionSpecs: FunctionSpec[];
361
+ prefixes: string[];
361
362
  notifyRefreshDelay: number;
362
363
  deployOnlyCode?: boolean;
363
364
  deployOnlyUI?: boolean;
@@ -368,6 +369,7 @@ export class MachineServiceControllerBase {
368
369
  };
369
370
  await deployFunctions({
370
371
  functionSpecs: config.functionSpecs,
372
+ prefixes: config.prefixes,
371
373
  notifyRefreshDelay: config.notifyRefreshDelay,
372
374
  deployOnlyCode: config.deployOnlyCode,
373
375
  deployOnlyUI: config.deployOnlyUI,
@@ -410,6 +412,7 @@ const DeployProgressController = SocketFunction.register(
410
412
  );
411
413
  export async function deployFunctionsWithProgress(config: {
412
414
  functionSpecs: FunctionSpec[];
415
+ prefixes: string[];
413
416
  notifyRefreshDelay: number;
414
417
  deployOnlyCode?: boolean;
415
418
  deployOnlyUI?: boolean;
@@ -1,13 +1,8 @@
1
- import { cache } from "socket-function/src/caching";
2
- import { blue, green, red } from "socket-function/src/formatting/logColors";
3
- import { getStorageDir, getSubFolder } from "../fs";
1
+ import { blue, green } from "socket-function/src/formatting/logColors";
4
2
  import { ClientWatcher } from "../1-path-client/pathValueClientWatcher";
5
- import { PathValueProxyWatcher } from "../2-proxy/PathValueProxyWatcher";
6
- import { debugPathValuePath, PathValue, PathWatcherCallback, ReadLock, WatchConfig, WriteState } from "../0-path-value-core/pathValueCore";
7
- import fs from "fs";
8
- import { measureWrap } from "socket-function/src/profiling/measure";
3
+ import { debugPathValuePath, PathValue } from "../0-path-value-core/pathValueCore";
9
4
  import { pathValueSerializer } from "../-h-path-value-serialize/PathValueSerializer";
10
- import { LOCAL_DOMAIN_PATH } from "../0-path-value-core/NodePathAuthorities";
5
+ import { LOCAL_DOMAIN_PATH } from "../0-path-value-core/PathRouter";
11
6
 
12
7
 
13
8
 
@@ -5,8 +5,6 @@ import { css } from "typesafecss";
5
5
  import { getSyncedController } from "../library-components/SyncedController";
6
6
  import { DebugLog, DebugLogController, getLogHistoryEquals, getLogHistoryIncludes } from "../0-path-value-core/auditLogs";
7
7
  import { remoteWatcher } from "../1-path-client/RemoteWatcher";
8
- import { pathValueAuthority2 } from "../0-path-value-core/NodePathAuthorities";
9
- import { getParentPathStr } from "../path";
10
8
  import { getBrowserUrlNode, getOwnNodeId } from "../-f-node-discovery/NodeDiscovery";
11
9
  import { nextId, sort } from "socket-function/src/misc";
12
10
  import { t } from "../2-proxy/schema2";
@@ -14,6 +12,7 @@ import { InputLabelURL } from "../library-components/InputLabel";
14
12
  import { URLParam } from "../library-components/URLParam";
15
13
  import { Table } from "../5-diagnostics/Table";
16
14
  import { ObjectDisplay } from "./logs/ObjectDisplay";
15
+ import { PathRouter } from "../0-path-value-core/PathRouter";
17
16
 
18
17
  /**
19
18
  TODO:
@@ -92,7 +91,7 @@ class AuditLogControllerBase {
92
91
  let { path, includeDescendants } = config;
93
92
  let authorityNodeId = remoteWatcher.getExistingWatchRemoteNodeId(path);
94
93
  if (!authorityNodeId) {
95
- authorityNodeId = await pathValueAuthority2.getSingleReadNodeSync(path);
94
+ authorityNodeId = await PathRouter.getReadyAuthority(path)?.nodeId;
96
95
  }
97
96
  let logs: DebugLog[] = [];
98
97
  let remoteLogs: DebugLog[] = [];
@@ -0,0 +1,141 @@
1
+ import { qreact } from "../4-dom/qreact";
2
+ import { css } from "../4-dom/css";
3
+ import { t } from "../2-proxy/schema2";
4
+ import { callState, ensureSubscribed } from "./FunctionCallInfoState";
5
+ import { formatNumber, formatPercent, formatTime } from "socket-function/src/formatting/format";
6
+ import { measureBlock } from "socket-function/src/profiling/measure";
7
+ import { StatsValue, getStatsTop } from "socket-function/src/profiling/stats";
8
+ import { isCurrentUserSuperUser } from "../user-implementation/userData";
9
+
10
+ module.hotreload = true;
11
+
12
+ function formatTimeStats(stats: StatsValue): string {
13
+ if (stats.count === 0) return formatTime(0);
14
+
15
+ let avgTime = stats.sum / stats.count;
16
+ let top = getStatsTop(stats);
17
+
18
+ if (!top.topHeavy) {
19
+ return formatTime(avgTime);
20
+ }
21
+
22
+ let bottomAvg = (stats.sum - top.value) / (stats.count - top.count) || 0;
23
+ let topAvg = top.value / top.count;
24
+ let topPercent = formatPercent(top.countFraction);
25
+
26
+ return `${formatTime(bottomAvg)} (top ${topPercent} = ${formatTime(topAvg)})`;
27
+ }
28
+
29
+ export class FunctionCallInfo extends qreact.Component {
30
+ showDetailedStats() {
31
+ if (!isCurrentUserSuperUser()) return undefined;
32
+ let state = callState();
33
+
34
+ let avgInternalReruns = state.totalCalls > 0 && state.totalInternalReruns / state.totalCalls || 0;
35
+ let avgFullReruns = state.totalCalls > 0 && state.totalFullReruns / state.totalCalls || 0;
36
+ let percentMultipleInternalRuns = state.totalCalls > 0 && state.callsWithMultipleInternalRuns / state.totalCalls || 0;
37
+ let percentCascadingRuns = state.totalCalls > 0 && state.callsWithCascadingRuns / state.totalCalls || 0;
38
+ let avgCascadingReruns = state.callsWithCascadingRuns > 0 && state.totalInternalRerunsForCascading / state.callsWithCascadingRuns || 0;
39
+ let percentMultipleFullRuns = state.totalCalls > 0 && state.callsWithMultipleFullRuns / state.totalCalls || 0;
40
+
41
+ console.log("=== Function Call Statistics ===");
42
+ console.log([
43
+ `${formatNumber(state.totalCalls)} ⚡`,
44
+ [
45
+ `${formatNumber(avgInternalReruns)} ♻️`,
46
+ percentMultipleInternalRuns > 0 && `(${formatPercent(percentMultipleInternalRuns)} 🔦)`,
47
+ percentCascadingRuns > 0 && `(${formatPercent(percentCascadingRuns)} / ${formatNumber(avgCascadingReruns)} / MAX ${state.maxInternalReruns} 📈)`
48
+ ].filter(v => v).join(" "),
49
+ percentMultipleFullRuns > 0 && `${formatNumber(avgFullReruns)} (${formatPercent(percentMultipleFullRuns)}) ⚠️`,
50
+ `${formatTimeStats(state.evalTimeStats)} 💫 / ${formatTimeStats(state.totalTimeStats)} 🕒`
51
+ ].filter(v => v).join(" | "));
52
+ console.log("");
53
+ console.log("=== Per-Function Statistics ===");
54
+
55
+ let perFunctionStats = state.perFunctionStats;
56
+ if (perFunctionStats) {
57
+ for (let functionId in perFunctionStats) {
58
+ let stats = perFunctionStats[functionId];
59
+ let avgInternal = stats.totalInternalReruns / stats.totalCalls;
60
+ let avgFull = stats.totalFullReruns / stats.totalCalls;
61
+ let pctInternal = stats.callsWithMultipleInternalRuns / stats.totalCalls;
62
+ let pctCascading = stats.callsWithCascadingRuns / stats.totalCalls;
63
+ let avgCascading = stats.callsWithCascadingRuns > 0 && stats.totalInternalRerunsForCascading / stats.callsWithCascadingRuns || 0;
64
+ let pctFull = stats.callsWithMultipleFullRuns / stats.totalCalls;
65
+
66
+ console.log([
67
+ `${functionId}: ${formatNumber(stats.totalCalls)} ⚡`,
68
+ [
69
+ `${formatNumber(avgInternal)} ♻️`,
70
+ pctInternal > 0 && `(${formatPercent(pctInternal)} 🔦)`,
71
+ pctCascading > 0 && `(${formatPercent(pctCascading)} / ${formatNumber(avgCascading)} / MAX ${stats.maxInternalReruns} 📈)`
72
+ ].filter(v => v).join(" "),
73
+ pctFull > 0 && `${formatNumber(avgFull)} (${formatPercent(pctFull)}) ⚠️`,
74
+ `${formatTimeStats(stats.evalTimeStats)} 💫 / ${formatTimeStats(stats.totalTimeStats)} 🕒`
75
+ ].filter(v => v).join(" | "));
76
+ }
77
+ }
78
+ }
79
+
80
+ render() {
81
+ ensureSubscribed();
82
+ let state = callState();
83
+
84
+ let avgInternalReruns = state.totalCalls > 0 && state.totalInternalReruns / state.totalCalls || 0;
85
+ let avgFullReruns = state.totalCalls > 0 && state.totalFullReruns / state.totalCalls || 0;
86
+ let percentMultipleInternalRuns = state.totalCalls > 0 && state.callsWithMultipleInternalRuns / state.totalCalls || 0;
87
+ let percentCascadingRuns = state.totalCalls > 0 && state.callsWithCascadingRuns / state.totalCalls || 0;
88
+ let avgCascadingReruns = state.callsWithCascadingRuns > 0 && state.totalInternalRerunsForCascading / state.callsWithCascadingRuns || 0;
89
+ let percentMultipleFullRuns = state.totalCalls > 0 && state.callsWithMultipleFullRuns / state.totalCalls || 0;
90
+
91
+ return <div className={css.button.vbox(4).pad2(4).alignItems("end")} onClick={() => this.showDetailedStats()}>
92
+ <div className={css.hbox(10)}>
93
+ <span title="Committed synced function calls">
94
+ {formatNumber(state.totalCalls)}⚡
95
+ </span>
96
+ <div className={css.hbox(7)}>
97
+ <span title="Number of function evaluations">
98
+ {formatNumber(avgInternalReruns)}♻️
99
+ </span>
100
+ {percentMultipleInternalRuns > 0 && <span title="Cases where the first call did not have all the synced data">
101
+ (🔦{formatPercent(percentMultipleInternalRuns)})
102
+ </span>}
103
+ {percentCascadingRuns > 0 && <span title="The initial sync did not provide all the data, requiring cascading syncing.">
104
+ (📈{formatPercent(percentCascadingRuns)} / {formatNumber(avgCascadingReruns)} / MAX {state.maxInternalReruns})
105
+ </span>}
106
+ </div>
107
+ {percentMultipleFullRuns > 0 && <span title="Rejected calls requiring full reruns. Unless you are changing data another user is actively changing, this is a bug!" className={css.hsl(60, 100, 80).pad2(2, 1).hslcolor(0, 0, 0)}>
108
+ {formatNumber(avgFullReruns)} ({formatPercent(percentMultipleFullRuns)}) ⚠️
109
+ </span>}
110
+ </div>
111
+ <div>
112
+ <span title="Average time to evaluate the application function (excluding proxy overhead)">
113
+ {formatTimeStats(state.evalTimeStats)}💫
114
+ </span>
115
+ {" / "}
116
+ <span title="Average time to evaluate from start to finish, including rejections, etc. Top % shows the top % count, and if top % is shown the other amount has the top values removed.">
117
+ {formatTimeStats(state.totalTimeStats)}🕒
118
+ </span>
119
+ </div>
120
+ <div className={css.hbox(20)}>
121
+ <div className={css.hbox(5)}>
122
+ <span title="Remote watched paths (syncing from other nodes)">
123
+ {formatNumber(state.remotePathCount)}🔥
124
+ </span>
125
+ {" / "}
126
+ <span title="Local watched paths">
127
+ {formatNumber(state.localPathCount)}🏠
128
+ </span>
129
+ {" / "}
130
+ <span title="Total values stored in memory">
131
+ {formatNumber(state.totalValueCount)}📦
132
+ </span>
133
+ {" / "}
134
+ <span title="Watchers">
135
+ {formatNumber(state.proxyWatcherCount)}🕵🏻‍♀️
136
+ </span>
137
+ </div>
138
+ </div>
139
+ </div>;
140
+ }
141
+ }
@@ -0,0 +1,162 @@
1
+ import { Querysub } from "../4-querysub/QuerysubController";
2
+ import { onPredictionFinished } from "../4-querysub/querysubPrediction";
3
+ import { lazy } from "socket-function/src/caching";
4
+ import { t } from "../2-proxy/schema2";
5
+ import { StatsValue, createStatsValue, addToStatsValue } from "socket-function/src/profiling/stats";
6
+ import { authorityStorage } from "../0-path-value-core/pathValueCore";
7
+ import { pathWatcher } from "../0-path-value-core/PathWatcher";
8
+ import { proxyWatcher } from "../2-proxy/PathValueProxyWatcher";
9
+
10
+ interface CallStatsData {
11
+ totalCalls: number;
12
+ totalInternalReruns: number;
13
+ totalFullReruns: number;
14
+ callsWithMultipleInternalRuns: number;
15
+ callsWithCascadingRuns: number;
16
+ totalInternalRerunsForCascading: number;
17
+ maxInternalReruns: number;
18
+ callsWithMultipleFullRuns: number;
19
+ evalTimeStats: StatsValue;
20
+ timeTakenStats: StatsValue;
21
+ totalTimeStats: StatsValue;
22
+ localPathCount: number;
23
+ remotePathCount: number;
24
+ totalValueCount: number;
25
+ proxyWatcherCount: number;
26
+ perFunctionStats: {
27
+ [functionId: string]: {
28
+ totalCalls: number;
29
+ totalInternalReruns: number;
30
+ totalFullReruns: number;
31
+ callsWithMultipleInternalRuns: number;
32
+ callsWithCascadingRuns: number;
33
+ totalInternalRerunsForCascading: number;
34
+ maxInternalReruns: number;
35
+ callsWithMultipleFullRuns: number;
36
+ evalTimeStats: StatsValue;
37
+ timeTakenStats: StatsValue;
38
+ totalTimeStats: StatsValue;
39
+ }
40
+ };
41
+ }
42
+
43
+ let statsData: CallStatsData = {
44
+ totalCalls: 0,
45
+ totalInternalReruns: 0,
46
+ totalFullReruns: 0,
47
+ callsWithMultipleInternalRuns: 0,
48
+ callsWithCascadingRuns: 0,
49
+ totalInternalRerunsForCascading: 0,
50
+ maxInternalReruns: 0,
51
+ callsWithMultipleFullRuns: 0,
52
+ evalTimeStats: createStatsValue(),
53
+ timeTakenStats: createStatsValue(),
54
+ totalTimeStats: createStatsValue(),
55
+ localPathCount: 0,
56
+ remotePathCount: 0,
57
+ totalValueCount: 0,
58
+ proxyWatcherCount: 0,
59
+ perFunctionStats: {},
60
+ };
61
+
62
+ let callStateSchema = Querysub.createLocalSchema("callState", {
63
+ sequenceNumber: t.number,
64
+ localPathCount: t.number,
65
+ remotePathCount: t.number,
66
+ totalValueCount: t.number,
67
+ proxyWatcherCount: t.number,
68
+ });
69
+
70
+ export function callState(): CallStatsData {
71
+ callStateSchema().sequenceNumber;
72
+ return statsData;
73
+ }
74
+
75
+ export let ensureSubscribed = lazy(() => {
76
+ onPredictionFinished((data) => {
77
+ statsData.totalCalls++;
78
+ statsData.totalInternalReruns += data.result.totalInternalLoopCount;
79
+ statsData.totalFullReruns += data.result.outerLoopCount;
80
+ addToStatsValue(statsData.evalTimeStats, data.result.evalTime);
81
+ addToStatsValue(statsData.timeTakenStats, data.result.timeTaken);
82
+ addToStatsValue(statsData.totalTimeStats, data.result.totalTime);
83
+
84
+ if (data.result.totalInternalLoopCount > 1) {
85
+ statsData.callsWithMultipleInternalRuns++;
86
+ }
87
+ if (data.result.totalInternalLoopCount > 2) {
88
+ statsData.callsWithCascadingRuns++;
89
+ statsData.totalInternalRerunsForCascading += data.result.totalInternalLoopCount;
90
+ if (data.result.totalInternalLoopCount > statsData.maxInternalReruns) {
91
+ statsData.maxInternalReruns = data.result.totalInternalLoopCount;
92
+ }
93
+ }
94
+ if (data.result.outerLoopCount > 1) {
95
+ statsData.callsWithMultipleFullRuns++;
96
+ }
97
+
98
+ if (!(data.functionId in statsData.perFunctionStats)) {
99
+ statsData.perFunctionStats[data.functionId] = {
100
+ totalCalls: 0,
101
+ totalInternalReruns: 0,
102
+ totalFullReruns: 0,
103
+ callsWithMultipleInternalRuns: 0,
104
+ callsWithCascadingRuns: 0,
105
+ totalInternalRerunsForCascading: 0,
106
+ maxInternalReruns: 0,
107
+ callsWithMultipleFullRuns: 0,
108
+ evalTimeStats: createStatsValue(),
109
+ timeTakenStats: createStatsValue(),
110
+ totalTimeStats: createStatsValue(),
111
+ };
112
+ }
113
+
114
+ let fnStats = statsData.perFunctionStats[data.functionId];
115
+ fnStats.totalCalls++;
116
+ fnStats.totalInternalReruns += data.result.totalInternalLoopCount;
117
+ fnStats.totalFullReruns += data.result.outerLoopCount;
118
+ addToStatsValue(fnStats.evalTimeStats, data.result.evalTime);
119
+ addToStatsValue(fnStats.timeTakenStats, data.result.timeTaken);
120
+ addToStatsValue(fnStats.totalTimeStats, data.result.totalTime);
121
+
122
+ if (data.result.totalInternalLoopCount > 1) {
123
+ fnStats.callsWithMultipleInternalRuns++;
124
+ }
125
+ if (data.result.totalInternalLoopCount > 2) {
126
+ fnStats.callsWithCascadingRuns++;
127
+ fnStats.totalInternalRerunsForCascading += data.result.totalInternalLoopCount;
128
+ if (data.result.totalInternalLoopCount > fnStats.maxInternalReruns) {
129
+ fnStats.maxInternalReruns = data.result.totalInternalLoopCount;
130
+ }
131
+ }
132
+ if (data.result.outerLoopCount > 1) {
133
+ fnStats.callsWithMultipleFullRuns++;
134
+ }
135
+
136
+ Querysub.commit(() => {
137
+ let schema = callStateSchema();
138
+ schema.sequenceNumber = schema.sequenceNumber + 1;
139
+ });
140
+ });
141
+
142
+ function updateSyncStats() {
143
+ let watcherStats = pathWatcher.getWatcherStats();
144
+ let valueCount = authorityStorage.getValueCount();
145
+ let proxyWatcherCount = proxyWatcher.getAllWatchers().size;
146
+
147
+ statsData.localPathCount = watcherStats.localPathCount;
148
+ statsData.remotePathCount = watcherStats.remotePathCount;
149
+ statsData.totalValueCount = valueCount;
150
+ statsData.proxyWatcherCount = proxyWatcherCount;
151
+
152
+ Querysub.commit(() => {
153
+ let schema = callStateSchema();
154
+ schema.localPathCount = watcherStats.localPathCount;
155
+ schema.remotePathCount = watcherStats.remotePathCount;
156
+ schema.totalValueCount = valueCount;
157
+ schema.proxyWatcherCount = proxyWatcherCount;
158
+ });
159
+ }
160
+ updateSyncStats();
161
+ setInterval(updateSyncStats, 15000);
162
+ });
@@ -246,7 +246,7 @@ class MachineThreadInfoBase {
246
246
  }
247
247
 
248
248
  }
249
- const MachineThreadInfoController = getSyncedController(SocketFunction.register(
249
+ export const MachineThreadInfoController = getSyncedController(SocketFunction.register(
250
250
  "MachineThreadInfoController-019c88cb-a16f-7219-9a5e-08919bd1475b",
251
251
  new MachineThreadInfoBase(),
252
252
  () => ({