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
package/tempnotes.txt CHANGED
@@ -1,65 +1,12 @@
1
1
 
2
- BUGS:
3
- FIXED? The value audit code says that the servers are missing values, And it's trying to send the test values between the different path value servers, even though they should be sharded to never share values.
4
- FIXED? The CYOA server keeps locking up
5
- █ PathValueSerializer() 46.34% ( 66.6s = 75.3K * 765us + 550K * 16.3us )
6
- - The values keep syncing to both servers.
7
- FIXED? The CYOA and the function server keep failing the audits as well. We might need to look at the logs to see how this could happen. It'll probably be obvious once we look at the logs for those paths.
8
- - Is it because we're unwatching it? That would make a lot of sense.
9
- - I guess we can just step in and see if its watched
10
- pathWatcher.watchers.get(path)
11
- remoteWatcher.remoteWatchPaths.get(path)
12
-
13
-
14
-
15
- 3) gc, to split it into multiple directories
16
- 4) Verify the site still works
17
-
18
- 5) Verify that the sharded startup only loads in the paths it needs and isn't wasteful
19
- - With 100 granularity the waste should be quite low. Unless we get REALLY unlucky? Hmm...
20
-
21
- 6) Record path waste per PathValueServer, and add an admin warn component that requests this, then shows a warning for PathValueServers that have a high waste load %
22
-
23
-
24
- 3) Test sharding again
25
- 4) Verify the test page works well, and shards
26
- - We have the function sharding, so the values shouldn't be sent to both servers because the function calls themselves should be sharded.
27
-
28
-
29
- 5) Test full path hash syncing, using data that's NOT in a prefix, and making sure we can sync it with keys to get some of it (and all of it)
30
- a) When connecting from a trusted node, everything should work fine, the flag should pass through correctly, as remote watchers should know what flag to pass.
31
- b) When syncing from the client side, only synchronizing all the keys will work. But that does still need to work, and that still requires talking to the correct nodes and passing them the correct flags.
32
-
33
-
34
- 3) add code so that client-side we can get a breakdown of How many paths we have per authority. The goal is to just have most the paths come from a single authority. For reading, it's actually more efficient to talk to a single server rather than many servers, as it's not the amount of data, it's really just the latency overhead of talking to the server. And for writing, same thing, it's a lot easier to just write to one server. And if every single user is reading from every single server, it's just not feasible. It no longer scales well.
35
-
36
- 4) Get the AI to go through and add function remapping code. Basically, the idea is that we take the most accessed lookup that we're accessing and we use the key for that as our hash. For simple functions, we could actually automate this, but I'll see how the AI can do.
37
-
38
- 5) Verify we are mostly accessing paths on one authority
39
-
40
-
41
- 7) Test 4 servers, sharded and redundant as the same time
42
- 8) Verify on the test page
43
-
44
- 8) Shard FunctionRunner
45
- - Now is as good a time as any to deal with any of the sharding issues. And it should work easily enough...
46
- - Verify the secondary run backup code works too.
47
-
48
-
49
- 2) Add disk verification service
50
- - pick a random authority => read the same we would as startup => do an extra backblaze getKeys and getInfo to make sure our cache is accurate => load in the data into memory => verify all old paths with that authority?
51
- - I wasn't planning on verifying all paths, but we should be able to. They have to be old enough, of course. But we kind of already have that logic, so the AI can probably write it. The one somewhat hard thing, of course, is doing the hashes, especially because new data could cause the hashes to change because of GCing in memory. But if that's the case, for now we can error. Eventually we will warn and then we will drill down further to find the specific paths that are wrong. And if they actually are wrong, then we can actually error.
52
-
53
-
54
- 2) Allow remote servers to talk to our local server, via using the connection the local server makes
55
- AH! Add a connectivity page that we can go to, and you can tell all the servers to ping all the other servers, and then it will give us a full list of all that are inaccessible. We should start with this page, deploy it, Then witness that the remote server can't talk to our local servers. They don't even need to be local path value servers, just our local CYOA server, etc.
56
- I think what we need to do is we need to group connections by the resolved identity instead of by the underlying node ID. Maybe this is already done? (But I don't remember doing it).
57
-
58
-
59
2
 
60
3
  3) Deploy EVERYTHING (all services) to remote servers
61
4
  3.0) deploy the path value servers shard it like we were testing locally
62
5
  - 4, for redundancy and sharding
63
- 5) Deploy disk verification service
6
+ 3.1) Make sure the gc services are deployed too
7
+ 5) Deploy disk audit service too
8
+ `yarn audit-disk-values` should work
64
9
 
10
+ I think even if we run into some occasional issues, we should just power through and try to fix them later. Because I'm sick of working on the framework...
65
11
 
12
+ MONTHLY SUMMARY!
package/test.ts CHANGED
@@ -2,307 +2,25 @@ import { chdir } from "process";
2
2
  chdir("D:/repos/qs-cyoa/");
3
3
 
4
4
  import "./inject";
5
-
6
- import path from "path";
7
- import { list, sort } from "socket-function/src/misc";
8
- import { formatNumber } from "socket-function/src/formatting/format";
9
- import { PathValueArchives, archives, archivesLocks, archivesRecycleBin, pathValueArchives } from "./src/0-path-value-core/pathValueArchives";
10
- import { trimPathStrToDepth } from "./src/path";
11
- import { Querysub } from "./src/4-querysub/QuerysubController";
12
- import { clientWatcher } from "./src/1-path-client/pathValueClientWatcher";
5
+ import { SocketFunction } from "socket-function/SocketFunction";
6
+ import { getControllerNodeIdList } from "./src/-g-core-values/NodeCapabilities";
13
7
  import { delay } from "socket-function/src/batching";
14
- import { authorityStorage } from "./src/0-path-value-core/pathValueCore";
15
- import { authorityLookup } from "./src/0-path-value-core/AuthorityLookup";
16
- import { getSnapshot, loadSnapshot, saveSnapshot } from "./src/0-path-value-core/archiveLocks/archiveSnapshots";
17
- import { Transaction } from "./src/0-path-value-core/archiveLocks/ArchiveLocks2";
18
- import { getArchives } from "./src/-a-archives/archives";
19
- import { unique } from "./src/misc";
20
- import { getAllAuthoritySpec, getOurAuthoritySpec } from "./src/0-path-value-core/PathRouterServerAuthoritySpec";
21
- import { PathRouter } from "./src/0-path-value-core/PathRouter";
22
- import { pathValueSerializer } from "./src/-h-path-value-serialize/PathValueSerializer";
23
- import { getShardPrefixes } from "./src/0-path-value-core/ShardPrefixes";
24
-
25
- async function main() {
26
- let prefixes = await getShardPrefixes();
27
- console.log(prefixes);
28
- // let paths = await pathValueArchives.getValuePaths(getAllAuthoritySpec());
29
- // paths = paths.filter(x => x.startsWith("P!REMAINING/"));
30
- // for (let path of paths) {
31
- // let data = await archives().get(path);
32
- // if (!data) {
33
- // console.log(`Missing data file: ${path}`);
34
- // continue;
35
- // }
36
- // let loadObj = await PathValueArchives.loadValuesFromBuffer({ path, data });
37
-
38
- // for (let value of loadObj.values) {
39
- // if (!PathRouter.matchesAuthoritySpec(getAllAuthoritySpec(), value.path)) {
40
- // throw new Error(`Value ${value.path} does not match authority spec`);
41
- // }
42
- // console.log(pathValueSerializer.getPathValue(value));
43
- // }
44
- // }
45
- // return;
8
+ import { green, yellow } from "socket-function/src/formatting/logColors";
9
+ import { Querysub } from "./src/4-querysub/Querysub";
10
+ import { pathValueArchives } from "./src/0-path-value-core/pathValueArchives";
11
+ import { getAllAuthoritySpec } from "./src/0-path-value-core/PathRouterServerAuthoritySpec";
46
12
 
47
- // let path = "P!REMAINING/data_37c5d323e0f9a083.1ed7a7340a015680.querysubtest.com:52026_1775097754671.579_3_1_337_1775097652829.5793_1775097652829.5793_genesis_.data";
48
- // let value = await archives().get(path);
49
- // console.log(value?.toString());
50
- // return;
51
13
 
52
- // const path = list(500).map(x => "a").join("");
53
- // await archives().set(path, Buffer.from("Hello, world!"));
54
- // let value = await archives().get(path);
55
- // console.log(value?.toString());
14
+ async function main() {
15
+ await Querysub.hostService("test");
56
16
 
57
- // function getName(path: string): string {
58
- // return path.split("/").at(-1)!;
59
- // }
17
+ let spec = await getAllAuthoritySpec();
60
18
 
61
- //*
62
- let allValuesObj = await pathValueArchives.loadValues({
63
- nodeId: "",
64
- prefixes: [],
65
- routeStart: 0,
66
- routeEnd: 1,
19
+ let data = await pathValueArchives.loadValues({
20
+ ...spec,
21
+ routeStart: 0.5,
22
+ routeEnd: 0.6,
67
23
  });
68
- let allValues = Object.values(allValuesObj.values).flat();
69
- console.log(`Total values loaded: ${formatNumber(allValues.length)}`);
70
- //*/
71
-
72
- // const badDeleteTransaction = "transaction_ tSeqNum=887 tWriteTime=1774715868374.9143 thread=9dc0 cCount=14 dCount=3482 create=X,X,X,X,X,X,X,X,X,X,X,X,X,X delete=2@0977,3@0977,4@0977,1@0a2a,2@0a2a,3@0a2a,4@0a2... .transaction";
73
-
74
-
75
- // // const snapshotFile = "fileCount=3482 valueCount=1142614 byteCount=1074547019 time=1774724950047.202 hash=9d9acc132ccfbf898ae0c287391e48a88e4e3b3ed3beda16f81a0936e710ab07";
76
- // // const snapshotFile2 = "fileCount=51 valueCount=1040817 byteCount=1019830353 time=1772831139982.38 hash=7c6acb6b705a8c68892f787e9c9f958b527bde8b60fb914045863a4bedc269e7";
77
- // const fixedSnapshotFile = "fileCount=3482 valueCount=1142614 byteCount=1074547019 time=1774760576390.8218 hash=6ef29678cb600f6920d1615c265ff5150ba97c847143fae2832e26d9e1adf1cf";
78
- // let snapshotFileObj = await getSnapshot(fixedSnapshotFile);
79
-
80
- // // let files: string[] = [];
81
- // // for (let file of snapshotFileObj.files) {
82
- // // files.push("NEW_remaining/" + getName(file.file));
83
- // // }
84
- // // let joinedSnapshot = await saveSnapshot({ files });
85
- // // console.log(`Joined snapshot: ${joinedSnapshot.file}`);
86
- // //await loadSnapshot({ overview: { file: fixedSnapshotFile }, noExit: true, hackNoDelete: true });
87
-
88
- // let snapshot = await getSnapshot(fixedSnapshotFile);
89
- // let found = 0;
90
- // let recycleBin = 0;
91
- // for (let file of snapshot.files) {
92
- // let data = await archives().get(file.file);
93
- // if (data) {
94
- // found++;
95
- // console.log("Found");
96
- // } else if (await archivesRecycleBin().get(file.file)) {
97
- // recycleBin++;
98
- // console.log("Found in recycle bin");
99
- // } else {
100
- // console.log("MISSING!");
101
- // }
102
- // }
103
- // // console.log(`Found ${found}/${snapshot.files.length} files | Recycle bin: ${recycleBin}`);
104
-
105
- //await loadSnapshot({ overview: { file: fixedSnapshotFile }, noExit: true, hackNoDelete: true });
106
-
107
-
108
-
109
- // // let joinedFiles = unique([...snapshot1.files, ...snapshot2.files].map(x => x.file));
110
- // // require("debugbreak")(2);
111
- // // debugger;
112
- // // let joinedSnapshot = await saveSnapshot({ files: joinedFiles });
113
- // // console.log(`Joined snapshot: ${joinedSnapshot.file}`);
114
- // // let joinedSnapshotFile = "fileCount=3482 valueCount=1142614 byteCount=1074547019 time=1774745293898.8706 hash=9d9acc132ccfbf898ae0c287391e48a88e4e3b3ed3beda16f81a0936e710ab07";
115
- // // joinedSnapshotFile = snapshotFile2;
116
-
117
- // // await loadSnapshot({ overview: { file: snapshotFile2 }, noExit: true, hackNoDelete: true });
118
-
119
- /*
120
- let allFiles = await getArchives("").find("");
121
- let allFileNames = new Set(allFiles.map(getName));
122
-
123
- let joinedSnapshot = await getSnapshot(snapshotFile2);
124
-
125
- let countByExtension = new Map<string, { value: number }>();
126
- let found = 0;
127
- let recycleBin = 0;
128
- for (let file of joinedSnapshot.files) {
129
- let extension = file.file.split(".").pop() || "";
130
- let existing = countByExtension.get(extension);
131
- if (!existing) {
132
- existing = { value: 0 };
133
- countByExtension.set(extension, existing);
134
- }
135
- existing.value++;
136
- let data = await archives().get(file.file);
137
- if (data) {
138
- found++;
139
- } else if (await archivesRecycleBin().get(file.file)) {
140
- recycleBin++;
141
- } else if (allFileNames.has(getName(file.file))) {
142
- require("debugbreak")(2);
143
- debugger;
144
- }
145
- }
146
- console.log(Array.from(countByExtension.entries()).map(([extension, value]) => `${extension}: ${value.value}`).join(" | "));
147
- console.log(`Found ${found}/${joinedSnapshot.files.length} files | Recycle bin: ${recycleBin}`);
148
-
149
-
150
- //*/
151
-
152
-
153
- // let joined = await saveSnapshot({ files: joinedFiles });
154
- // console.log(`Joined snapshot: ${joined.file}`);
155
-
156
- // let recycleBinFiles = await archivesRecycleBin().find("");
157
-
158
- // //let snapshot = await getSnapshot(snapshotFile);
159
- //await loadSnapshot({ overview: { file: joinedSnapshotFile }, noExit: true, hackNoDelete: true });
160
- // require("debugbreak")(2);
161
- // debugger;
162
-
163
- //Ugh... we restored... SOME values. 34.4K. But we should have restored over 1 million. And... it doesn't work, of course.
164
-
165
-
166
- //let recycleBinFiles = await archivesRecycleBin().find("");
167
-
168
- /*
169
- {
170
- let snapshot = await getSnapshot(joinedSnapshotFile);
171
- let snapshotNames = new Set(snapshot.files.map(x => getName(x.file)));
172
-
173
- let archivesRoot = getArchives("");
174
- let allFiles = await archivesRoot.find("");
175
- let fullPaths = allFiles.filter(x => snapshotNames.has(getName(x)));
176
- let correct = 0;
177
- let wrong = 0;
178
- let duplicate = 0;
179
- let foundPaths = new Set<string>();
180
- for (let i = 0; i < fullPaths.length; i++) {
181
- let fullPath = fullPaths[i];
182
- let name = getName(fullPath);
183
- let correctPath = `path-values-recycle-bin/NEW_remaining/${name}`;
184
- if (fullPath === correctPath) {
185
- correct++;
186
- continue;
187
- }
188
- wrong++;
189
- if (foundPaths.has(name)) {
190
- duplicate++;
191
- continue;
192
- }
193
- console.log(`Path is wrong at ${i}/${fullPaths.length}: ${fullPath}`);
194
- foundPaths.add(name);
195
- await archivesRoot.move({
196
- path: fullPath,
197
- targetPath: correctPath,
198
- target: archivesRoot,
199
- });
200
- }
201
- console.log({ correct, wrong, duplicate, snapshotCount: snapshot.files.length, allCount: allFiles.length, fullPathsCount: fullPaths.length });
202
- }
203
-
204
- //*/
205
-
206
- // let recycleBinValues = await archivesRecycleBin().findInfo("");
207
- // require("debugbreak")(2);
208
- // debugger;
209
-
210
- // // File =-.,querysubtest._com.,/data_0ee02faf16c0fadf.1ed7a7340a015680.querysubtest.com:58985_1773251893137_6_1_337_1773251743670_1773251743670_genesis_.data is not in the recycle bin, skipping
211
-
212
- // // UGH... they values we delete are not in the recycle bin? Wtf...
213
- // // Welp... files ARE KEPT after deletion natively, so maybe we can find them that way
214
-
215
- // //todonext
216
- // // "move" must be broken? Hmm...
217
-
218
- // // HMM... Why do we have all these files in the root? That is surely not correct. These kinda look like our restored snapshot...
219
- // // That would make sense if move lost its ability to nest values. Maybe due to our caching?
220
-
221
-
222
-
223
- // //let snapshot = await getSnapshot(snapshotFile);
224
-
225
-
226
-
227
- // let test = await archives().find("");
228
- // console.log(test.length);
229
-
230
-
231
-
232
- // We have 125K paths in the recycle bin. We read it surprisingly fast (2 minutes), but yeah... we need to fix this...
233
- // So... archivesRecycleBin is VERY slow. Hmm... do we ever clean it up? I think we have to?
234
- // - We will want to thin the snapshots, not just remove all old ones.
235
- // Hmm... So what even happened?
236
- // - We have some transactions. It looks like they just deleted all the data. I guess our GC code accidentally matched everything.
237
- // Ah, but... We know the files that were deleted, and they're just moved to the recycle bin. So we should just really go from the transactions to find all of the old values.
238
- // AND... somehow we deleted everything, all transactions, everything.
239
- // - Must have been during the attempt to restore it.
240
- // let files = await archivesRecycleBin().findInfo("");
241
- // console.log(files);
242
- // console.log(files.length);
243
- // require("debugbreak")(2);
244
- // debugger;
245
-
246
- // let snapshot = await pathValueArchives.loadValues({
247
- // type: "remaining",
248
- // excludedPrefixes: [],
249
- // });
250
- // let allValues = Object.values(snapshot.values).flat();
251
- // console.log(`Total values loaded: ${formatNumber(allValues.length)}`);
252
-
253
-
254
- // let transactionRaw = await archivesRecycleBin().get(badDeleteTransaction);
255
- // if (!transactionRaw) throw new Error(`Transaction not found: ${badDeleteTransaction}`);
256
- // console.log(transactionRaw?.toString());
257
-
258
- // let transaction = JSON.parse(transactionRaw.toString()) as Transaction;
259
- // let files = transaction.ops.filter(a => a.type === "delete").map(a => a.key);
260
- // await saveSnapshot({ files });
261
-
262
-
263
-
264
-
265
- // await Querysub.hostService("test");
266
- // //let topology = await authorityLookup.getTopology();
267
- // let parentPath = ".,querysubtest._com.,PathFunctionRunner.,book.,Data.,edittingBooks.,hkwnmoiwhuyqenbi.,books.,";
268
- // clientWatcher.setWatches({
269
- // paths: new Set([]),
270
- // parentPaths: new Set([parentPath]),
271
- // callback: () => {
272
- // let value = authorityStorage.getPathsFromParent(parentPath);
273
- // console.log({ parentPaths: value?.size, isSynced: authorityStorage.isParentSynced(parentPath) });
274
- // }
275
- // });
276
-
277
- // await Querysub.hostService("test");
278
-
279
- // let snapshot = await pathValueArchives.loadValues({
280
- // type: "remaining",
281
- // excludedPrefixes: [],
282
- // });
283
- // let allValues = Object.values(snapshot.values).flat();
284
- // console.log(`Total values loaded: ${formatNumber(allValues.length)}`);
285
-
286
- // let splitLevel = 4;
287
- // let paths = new Map<string, {
288
- // count: number;
289
- // }>();
290
- // for (let value of allValues) {
291
- // let path = value.path;
292
- // let prefix = trimPathStrToDepth(path, splitLevel);
293
- // let entry = paths.get(prefix);
294
- // if (!entry) {
295
- // entry = { count: 0 };
296
- // paths.set(prefix, entry);
297
- // }
298
- // entry.count++;
299
- // }
300
- // // Log the 10 biggest paths
301
- // let entries = Array.from(paths.entries());
302
- // sort(entries, x => -x[1].count);
303
- // for (let [path, entry] of entries.slice(0, 10)) {
304
- // console.log(`${formatNumber(entry.count)}\t${path}`);
305
- // }
306
24
  }
307
25
 
308
26
  main().catch(console.error)
@@ -1,139 +0,0 @@
1
- import { addToStats, addToStatsValue, createStatsValue, getStatsTop } from "socket-function/src/profiling/stats";
2
- import type { PathValue } from "../0-path-value-core/pathValueCore";
3
- import { registerPeriodic } from "./periodic";
4
- import { formatTime } from "socket-function/src/formatting/format";
5
- import { formatStats } from "socket-function/src/profiling/statsFormat";
6
- import { getPathStr1 } from "../path";
7
- import { registerResource } from "./trackResources";
8
- import type { CallSpec } from "../3-path-functions/PathFunctionRunner";
9
- import { PromiseObj } from "../promise";
10
- import { lazy } from "socket-function/src/caching";
11
- import { isNode } from "socket-function/src/misc";
12
-
13
- const getCallResultPathFnc = lazy(() => import("../4-querysub/querysubPrediction").then((m) => m.getCallResultPath));
14
-
15
- // NOTE: There's no point to track non-predicted calls, because if we don't predict them we won't be watching
16
- // them, so we won't know when they finish!
17
-
18
- class OverlapRateTracker {
19
- public openCount = 0;
20
- private openTime = 0;
21
- public originalOpenTime = Date.now();
22
- private openCloseCount = 0;
23
-
24
- public finishedTime = 0;
25
- private finishedCount = 0;
26
-
27
- public openValue() {
28
- if (this.openCount === 0) {
29
- this.openTime = Date.now();
30
- }
31
- this.openCount++;
32
- this.openCloseCount++;
33
- }
34
- public closeValue() {
35
- this.openCount--;
36
- let now = Date.now();
37
- if (this.openCount === 0 || now - this.openTime > 5000) {
38
- this.finishedTime += now - this.openTime;
39
- this.openTime = now;
40
- this.finishedCount += this.openCloseCount;
41
- this.openCloseCount = 0;
42
- }
43
- }
44
- public viewRate() {
45
- if (this.finishedTime === 0) return 0;
46
- return this.finishedCount / this.finishedTime * 1000;
47
- }
48
- public saturatedFraction() {
49
- return this.finishedTime / (Date.now() - this.originalOpenTime);
50
- }
51
- public harvestRate() {
52
- let rate = this.viewRate();
53
- this.finishedTime = 0;
54
- this.finishedCount = 0;
55
- this.originalOpenTime = Date.now();
56
- return rate;
57
- }
58
- }
59
-
60
- export class Benchmark {
61
-
62
- public static predictCount = 0;
63
- public static receivedCount = 0;
64
-
65
- private static finishedCalls = createStatsValue();
66
- public static overlapRate = new OverlapRateTracker();
67
-
68
- private static predictedCalls = registerResource("Benchmark|predictedCalls", new Map<string, number>());
69
-
70
- private static waitingForCalls = registerResource("Benchmark|waitingForCalls", new Map<string, (() => void)[]>());
71
-
72
- public static onStartPredictCall(path: string) {
73
- this.predictedCalls.set(path, Date.now());
74
- Benchmark.predictCount++;
75
- this.overlapRate.openValue();
76
- }
77
- public static onReceivedValues(values: PathValue[]) {
78
- for (let value of values) {
79
- if (value.canGCValue) continue;
80
- if (!value.valid) continue;
81
- let list = this.waitingForCalls.get(value.path);
82
- if (!list) continue;
83
- this.waitingForCalls.delete(value.path);
84
- for (let resolve of list) {
85
- resolve();
86
- }
87
- }
88
-
89
- let now = Date.now();
90
- for (let value of values) {
91
- if (!value.path.includes(getPathStr1("Results"))) {
92
- continue;
93
- }
94
- if (value.canGCValue) continue;
95
- if (!value.valid) continue;
96
- let predictedTime = this.predictedCalls.get(value.path);
97
- if (!predictedTime) {
98
- continue;
99
- }
100
- this.overlapRate.closeValue();
101
- this.predictedCalls.delete(value.path);
102
- this.receivedCount++;
103
- addToStatsValue(this.finishedCalls, now - predictedTime);;
104
- }
105
- }
106
-
107
- public static async waitForCallToFinish(call: CallSpec) {
108
- let getCallResultPath = await getCallResultPathFnc();
109
- let path = getCallResultPath(call);
110
- let list = this.waitingForCalls.get(path);
111
- if (!list) {
112
- list = [];
113
- this.waitingForCalls.set(path, list);
114
- }
115
- let promiseObj = new PromiseObj();
116
- list.push(promiseObj.resolve);
117
- await promiseObj.promise;
118
- }
119
-
120
- public static logCallsNow() {
121
- let stats = this.finishedCalls;
122
- this.finishedCalls = createStatsValue();
123
- console.log(`Calls ${formatStats(stats)}, rate ${this.overlapRate.harvestRate()}`);
124
- }
125
-
126
- public static getLiveCallStats() {
127
- return this.finishedCalls;
128
- }
129
- public static getLiveRate() {
130
- return this.overlapRate.viewRate();
131
- }
132
- public static saturatedFraction() {
133
- return this.overlapRate.saturatedFraction();
134
- }
135
- }
136
-
137
- if (!isNode()) {
138
- registerPeriodic(() => Benchmark.logCallsNow());
139
- }