querysub 0.40.0 → 0.42.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "querysub",
3
- "version": "0.40.0",
3
+ "version": "0.42.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",
@@ -24,7 +24,7 @@
24
24
  "node-forge": "https://github.com/sliftist/forge#e618181b469b07bdc70b968b0391beb8ef5fecd6",
25
25
  "pako": "^2.1.0",
26
26
  "preact": "^10.11.3",
27
- "socket-function": "^0.38.0",
27
+ "socket-function": "^0.39.0",
28
28
  "terser": "^5.31.0",
29
29
  "typesafecss": "^0.6.3",
30
30
  "yaml": "^2.5.0",
@@ -18,6 +18,7 @@ import { parseArgs } from "./PathFunctionHelpers";
18
18
  import { PERMISSIONS_FUNCTION_ID, getExportPath } from "./syncSchema";
19
19
  import { formatTime } from "socket-function/src/formatting/format";
20
20
  import { set_debug_getFunctionRunnerShards } from "../-g-core-values/NodeCapabilities";
21
+ import { diskLog } from "../diagnostics/logs/diskLogger";
21
22
 
22
23
  export const functionSchema = rawSchema<{
23
24
  [domainName: string]: {
@@ -630,6 +631,7 @@ export class PathFunctionRunner {
630
631
  if (PathFunctionRunner.DEBUG_CALLS) {
631
632
  console.log(`FINISHED${nooped ? " (skipped)" : ""} ${getDebugName(callPath, functionSpec, true)}, writes: ${finalWrites?.length}`);
632
633
  }
634
+ diskLog(`Finished FunctionRunner funciton`, { ...callPath, argsEncoded: "", functionSpec });
633
635
  PathFunctionRunner.RUN_FINISH_COUNT++;
634
636
  }
635
637
  }
@@ -185,7 +185,7 @@ export class NodeViewer extends qreact.Component {
185
185
  }
186
186
 
187
187
  let builtinGroups = {
188
- "Default": ["buttons", "devToolsURL", "nodeId", "ip", "uptime", "loadTime", "Heap", "Buffers", "All Memory", "Blocking Lag", "port", "threadId", "machineId", "apiError", "live_entryPoint"],
188
+ "Default": ["buttons", "devToolsURL", "nodeId", "ip", "uptime", "loadTime", "Est % Usage", "Heap", "Buffers", "All Memory", "Blocking Lag", "port", "threadId", "machineId", "apiError", "live_entryPoint"],
189
189
  };
190
190
  // Column => group
191
191
  let builtInGroupsLookup = new Map<string, string>();
@@ -279,7 +279,7 @@ export class NodeViewer extends qreact.Component {
279
279
  },
280
280
  },
281
281
  ip: {},
282
- loadTime: { formatter: "timeSpan" },
282
+ loadTime: { title: "Meta Time", formatter: "timeSpan" },
283
283
  ...x.table?.columns,
284
284
  //capabilities: null,
285
285
  apiError: { formatter: "error" },
@@ -0,0 +1,64 @@
1
+ import { SocketFunctionClientHook } from "socket-function/SocketFunctionTypes";
2
+ import { cache, lazy } from "socket-function/src/caching";
3
+ import { decodeCborx, encodeCborx } from "../../misc/cloneHelpers";
4
+ import { sha256 } from "js-sha256";
5
+ import { errorToUndefined } from "../../errors";
6
+ import { isNode } from "typesafecss";
7
+
8
+ let getRootDirectory = lazy(async () => {
9
+ await navigator.storage.persist();
10
+ return navigator.storage.getDirectory();
11
+ });
12
+
13
+ export const getBrowserLargeFileCache = cache((name: string) => new BrowserLargeFileCache(name));
14
+
15
+ export class BrowserLargeFileCache {
16
+ constructor(private name: string) { }
17
+
18
+ private getDir = lazy(async () => {
19
+ let root = await getRootDirectory();
20
+ return await root.getDirectoryHandle(this.name, { create: true });
21
+ });
22
+
23
+ public async set(key: string, value: Buffer) {
24
+ let dir = await this.getDir();
25
+ let file = await dir.getFileHandle(key, { create: true });
26
+ let writable = await file.createWritable();
27
+ await writable.write(value);
28
+ await writable.close();
29
+ }
30
+ public async get(key: string): Promise<Buffer | undefined> {
31
+ let dir = await this.getDir();
32
+ try {
33
+ let file = await dir.getFileHandle(key, { create: false });
34
+ let readable = await file.getFile();
35
+ return Buffer.from(await readable.arrayBuffer());
36
+ } catch {
37
+ return undefined;
38
+ }
39
+ }
40
+ }
41
+
42
+ /** Cache key = [args, functionName, classGuid]
43
+ * - Not nodeId, as that change so frequently, so caching based on server is not usually useful.
44
+ * - If you want to cache based on nodeId, pass it as an unused arg.
45
+ *
46
+ * Uses a file per key, and never cleans them up. So... basically, this sucks, but, it should
47
+ * work for a few specific functions with large and consistent values.
48
+ */
49
+ export const cacheCalls: SocketFunctionClientHook = async config => {
50
+ if (isNode()) return;
51
+
52
+ let { args, functionName, classGuid } = config.call;
53
+ let bucket = sha256(JSON.stringify({ functionName, classGuid }));
54
+ let cache = getBrowserLargeFileCache(bucket);
55
+ let key = sha256(encodeCborx(args));
56
+ let cachedValue = await cache.get(key);
57
+ if (cachedValue) {
58
+ config.overrideResult = decodeCborx(cachedValue);
59
+ return;
60
+ }
61
+ config.onResult.push(async (result) => {
62
+ await errorToUndefined(cache.set(key, encodeCborx(result)));
63
+ });
64
+ };
@@ -10,7 +10,7 @@ import { getSyncedController } from "../../library-components/SyncedController";
10
10
  import { getBrowserUrlNode } from "../../-f-node-discovery/NodeDiscovery";
11
11
  import { formatDateTime, formatNiceDateTime, formatNumber, formatTime } from "socket-function/src/formatting/format";
12
12
  import { Anchor } from "../../library-components/ATag";
13
- import { cache, cacheLimited, cacheShallowConfigArgEqual } from "socket-function/src/caching";
13
+ import { cache, cacheLimited, cacheShallowConfigArgEqual, lazy } from "socket-function/src/caching";
14
14
  import { InputLabel, InputLabelURL } from "../../library-components/InputLabel";
15
15
  import { DropdownSelector } from "../../library-components/DropdownSelector";
16
16
  import { Table, TableType } from "../../5-diagnostics/Table";
@@ -30,6 +30,7 @@ import { getNodeIdLocation } from "socket-function/src/nodeCache";
30
30
  import { decodeNodeId, encodeNodeId, getMachineId } from "../../-a-auth/certs";
31
31
  import { Button } from "../../library-components/Button";
32
32
  import { TimeRangeSelector } from "../../library-components/TimeRangeSelector";
33
+ import { BrowserLargeFileCache, cacheCalls } from "./BrowserLargeFileCache";
33
34
 
34
35
  // TODO: Realtime log mode, by reading from the previous length forward, to add buffers
35
36
  // to what we already read.
@@ -550,10 +551,15 @@ export const DiskLoggerController = SocketFunction.register(
550
551
  new DiskLoggerControllerBase(),
551
552
  () => ({
552
553
  getRemoteLogFiles: {},
553
- getRemoteLogBuffer: {},
554
+ getRemoteLogBuffer: {
555
+ compress: true,
556
+ clientHooks: [cacheCalls]
557
+ },
554
558
 
555
559
  getLogFiles: {},
556
- getLogBuffer: {},
560
+ getLogBuffer: {
561
+ compress: true,
562
+ },
557
563
  }),
558
564
  () => ({
559
565
  hooks: [assertIsManagementUser],
@@ -44,8 +44,8 @@ export type LogObj = {
44
44
 
45
45
  // NOTE: This is visible, otherwise it's easy to accidentally copy it, and not know why
46
46
  // the text is behaving strangely (not === other seemingly equal text, etc).
47
+ // NOTE: Also hardcoded in measure.ts (in socket-function)
47
48
  export const noDiskLogPrefix = "█ ";
48
-
49
49
  export const diskLog = logDisk;
50
50
  export function logDisk(...args: unknown[]) {
51
51
  if (!isNode()) return;
@@ -1,13 +1,40 @@
1
1
  import { runInfinitePoll, runInfinitePollCallAtStart } from "socket-function/src/batching";
2
- import { FormattedMeasureTable, logMeasureTable, startMeasure } from "socket-function/src/profiling/measure";
2
+ import { FormattedMeasureTable, MeasureProfile, logMeasureTable, startMeasure } from "socket-function/src/profiling/measure";
3
3
  import { logErrors } from "../errors";
4
- import { isNode } from "socket-function/src/misc";
4
+ import { isNode, sort } from "socket-function/src/misc";
5
5
  import debugbreak from "debugbreak";
6
6
  import { registerPeriodic } from "./periodic";
7
7
  import { getOwnMachineId } from "../-a-auth/certs";
8
8
  import { SocketFunction } from "socket-function/SocketFunction";
9
9
  import { logDisk } from "./logs/diskLogger";
10
+ import { registerNodeMetadata } from "../5-diagnostics/nodeMetadata";
11
+ import { formatPercent } from "socket-function/src/formatting/format";
10
12
 
13
+ let lastProfile: MeasureProfile | undefined;
14
+
15
+ registerNodeMetadata({
16
+ columnName: "Est Recent % Usage",
17
+ getValue() {
18
+ if (!lastProfile) return "";
19
+ let entries = Object.values(lastProfile.entries);
20
+ if (entries.length === 0) return "";
21
+ const timeProfiled = lastProfile.endTime - lastProfile.startTime;
22
+ let timeActive = entries.map(x => x.ownTime.sum).reduce((a, b) => a + b, 0);
23
+ let frac = timeActive / timeProfiled;
24
+ let rootSums = new Map<string, number>();
25
+ for (let entry of entries) {
26
+ let name = entry.name.split("|")[0];
27
+ let sum = rootSums.get(name) ?? 0;
28
+ rootSums.set(name, sum + entry.ownTime.sum);
29
+ }
30
+ let most = sort(Array.from(rootSums.entries()), x => -x[1])[0];
31
+ return `${formatPercent(frac)} (${formatPercent(most[1] / timeProfiled)} ${endEllipsis(most[0], 26)})`;
32
+ },
33
+ });
34
+ function endEllipsis(str: string, maxLength: number) {
35
+ if (str.length <= maxLength) return str;
36
+ return "..." + str.slice(-(maxLength - 3));
37
+ }
11
38
 
12
39
  let measureObj = startMeasure();
13
40
  function logProfileMeasuresTimingsNow() {
@@ -29,6 +56,8 @@ function logProfileMeasuresTimingsNow() {
29
56
  mergeDepth: 1,
30
57
  minTimeToLog: 250,
31
58
  }));
59
+
60
+ lastProfile = profile;
32
61
  // if (isNode()) {
33
62
  // if (SocketFunction.mountedNodeId) {
34
63
  // console.log("Mounted as " + SocketFunction.mountedNodeId);