querysub 0.2.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 (169) hide show
  1. package/.dependency-cruiser.js +304 -0
  2. package/.eslintrc.js +51 -0
  3. package/.github/copilot-instructions.md +1 -0
  4. package/.vscode/settings.json +25 -0
  5. package/bin/deploy.js +4 -0
  6. package/bin/function.js +4 -0
  7. package/bin/server.js +4 -0
  8. package/costsBenefits.txt +112 -0
  9. package/deploy.ts +3 -0
  10. package/inject.ts +1 -0
  11. package/package.json +60 -0
  12. package/prompts.txt +54 -0
  13. package/spec.txt +820 -0
  14. package/src/-a-archives/archiveCache.ts +913 -0
  15. package/src/-a-archives/archives.ts +148 -0
  16. package/src/-a-archives/archivesBackBlaze.ts +792 -0
  17. package/src/-a-archives/archivesDisk.ts +418 -0
  18. package/src/-a-archives/copyLocalToBackblaze.ts +24 -0
  19. package/src/-a-auth/certs.ts +517 -0
  20. package/src/-a-auth/der.ts +122 -0
  21. package/src/-a-auth/ed25519.ts +1015 -0
  22. package/src/-a-auth/node-forge-ed25519.d.ts +17 -0
  23. package/src/-b-authorities/dnsAuthority.ts +203 -0
  24. package/src/-b-authorities/emailAuthority.ts +57 -0
  25. package/src/-c-identity/IdentityController.ts +200 -0
  26. package/src/-d-trust/NetworkTrust2.ts +150 -0
  27. package/src/-e-certs/EdgeCertController.ts +288 -0
  28. package/src/-e-certs/certAuthority.ts +192 -0
  29. package/src/-f-node-discovery/NodeDiscovery.ts +543 -0
  30. package/src/-g-core-values/NodeCapabilities.ts +134 -0
  31. package/src/-g-core-values/oneTimeForward.ts +91 -0
  32. package/src/-h-path-value-serialize/PathValueSerializer.ts +769 -0
  33. package/src/-h-path-value-serialize/stringSerializer.ts +176 -0
  34. package/src/0-path-value-core/LoggingClient.tsx +24 -0
  35. package/src/0-path-value-core/NodePathAuthorities.ts +978 -0
  36. package/src/0-path-value-core/PathController.ts +1 -0
  37. package/src/0-path-value-core/PathValueCommitter.ts +565 -0
  38. package/src/0-path-value-core/PathValueController.ts +231 -0
  39. package/src/0-path-value-core/archiveLocks/ArchiveLocks.ts +154 -0
  40. package/src/0-path-value-core/archiveLocks/ArchiveLocks2.ts +820 -0
  41. package/src/0-path-value-core/archiveLocks/archiveSnapshots.ts +180 -0
  42. package/src/0-path-value-core/debugLogs.ts +90 -0
  43. package/src/0-path-value-core/pathValueArchives.ts +483 -0
  44. package/src/0-path-value-core/pathValueCore.ts +2217 -0
  45. package/src/1-path-client/RemoteWatcher.ts +558 -0
  46. package/src/1-path-client/pathValueClientWatcher.ts +702 -0
  47. package/src/2-proxy/PathValueProxyWatcher.ts +1857 -0
  48. package/src/2-proxy/archiveMoveHarness.ts +376 -0
  49. package/src/2-proxy/garbageCollection.ts +753 -0
  50. package/src/2-proxy/pathDatabaseProxyBase.ts +37 -0
  51. package/src/2-proxy/pathValueProxy.ts +139 -0
  52. package/src/2-proxy/schema2.ts +518 -0
  53. package/src/3-path-functions/PathFunctionHelpers.ts +129 -0
  54. package/src/3-path-functions/PathFunctionRunner.ts +619 -0
  55. package/src/3-path-functions/PathFunctionRunnerMain.ts +67 -0
  56. package/src/3-path-functions/deployBlock.ts +10 -0
  57. package/src/3-path-functions/deployCheck.ts +7 -0
  58. package/src/3-path-functions/deployMain.ts +160 -0
  59. package/src/3-path-functions/pathFunctionLoader.ts +282 -0
  60. package/src/3-path-functions/syncSchema.ts +475 -0
  61. package/src/3-path-functions/tests/functionsTest.ts +135 -0
  62. package/src/3-path-functions/tests/rejectTest.ts +77 -0
  63. package/src/4-dom/css.tsx +29 -0
  64. package/src/4-dom/cssTypes.d.ts +212 -0
  65. package/src/4-dom/qreact.tsx +2322 -0
  66. package/src/4-dom/qreactTest.tsx +417 -0
  67. package/src/4-querysub/Querysub.ts +877 -0
  68. package/src/4-querysub/QuerysubController.ts +620 -0
  69. package/src/4-querysub/copyEvent.ts +0 -0
  70. package/src/4-querysub/permissions.ts +289 -0
  71. package/src/4-querysub/permissionsShared.ts +1 -0
  72. package/src/4-querysub/querysubPrediction.ts +525 -0
  73. package/src/5-diagnostics/FullscreenModal.tsx +67 -0
  74. package/src/5-diagnostics/GenericFormat.tsx +165 -0
  75. package/src/5-diagnostics/Modal.tsx +79 -0
  76. package/src/5-diagnostics/Table.tsx +183 -0
  77. package/src/5-diagnostics/TimeGrouper.tsx +114 -0
  78. package/src/5-diagnostics/diskValueAudit.ts +216 -0
  79. package/src/5-diagnostics/memoryValueAudit.ts +442 -0
  80. package/src/5-diagnostics/nodeMetadata.ts +135 -0
  81. package/src/5-diagnostics/qreactDebug.tsx +309 -0
  82. package/src/5-diagnostics/shared.ts +26 -0
  83. package/src/5-diagnostics/synchronousLagTracking.ts +47 -0
  84. package/src/TestController.ts +35 -0
  85. package/src/allowclient.flag +0 -0
  86. package/src/bits.ts +86 -0
  87. package/src/buffers.ts +69 -0
  88. package/src/config.ts +53 -0
  89. package/src/config2.ts +48 -0
  90. package/src/diagnostics/ActionsHistory.ts +56 -0
  91. package/src/diagnostics/NodeViewer.tsx +503 -0
  92. package/src/diagnostics/SizeLimiter.ts +62 -0
  93. package/src/diagnostics/TimeDebug.tsx +18 -0
  94. package/src/diagnostics/benchmark.ts +139 -0
  95. package/src/diagnostics/errorLogs/ErrorLogController.ts +515 -0
  96. package/src/diagnostics/errorLogs/ErrorLogCore.ts +274 -0
  97. package/src/diagnostics/errorLogs/LogClassifiers.tsx +302 -0
  98. package/src/diagnostics/errorLogs/LogFilterUI.tsx +84 -0
  99. package/src/diagnostics/errorLogs/LogNotify.tsx +101 -0
  100. package/src/diagnostics/errorLogs/LogTimeSelector.tsx +724 -0
  101. package/src/diagnostics/errorLogs/LogViewer.tsx +757 -0
  102. package/src/diagnostics/errorLogs/hookErrors.ts +60 -0
  103. package/src/diagnostics/errorLogs/logFiltering.tsx +149 -0
  104. package/src/diagnostics/heapTag.ts +13 -0
  105. package/src/diagnostics/listenOnDebugger.ts +77 -0
  106. package/src/diagnostics/logs/DiskLoggerPage.tsx +572 -0
  107. package/src/diagnostics/logs/ObjectDisplay.tsx +165 -0
  108. package/src/diagnostics/logs/ansiFormat.ts +108 -0
  109. package/src/diagnostics/logs/diskLogGlobalContext.ts +38 -0
  110. package/src/diagnostics/logs/diskLogger.ts +305 -0
  111. package/src/diagnostics/logs/diskShimConsoleLogs.ts +32 -0
  112. package/src/diagnostics/logs/injectFileLocationToConsole.ts +50 -0
  113. package/src/diagnostics/logs/logGitHashes.ts +30 -0
  114. package/src/diagnostics/managementPages.tsx +289 -0
  115. package/src/diagnostics/periodic.ts +89 -0
  116. package/src/diagnostics/runSaturationTest.ts +416 -0
  117. package/src/diagnostics/satSchema.ts +64 -0
  118. package/src/diagnostics/trackResources.ts +82 -0
  119. package/src/diagnostics/watchdog.ts +55 -0
  120. package/src/errors.ts +132 -0
  121. package/src/forceProduction.ts +3 -0
  122. package/src/fs.ts +72 -0
  123. package/src/heapDumps.ts +666 -0
  124. package/src/https.ts +2 -0
  125. package/src/inject.ts +1 -0
  126. package/src/library-components/ATag.tsx +84 -0
  127. package/src/library-components/Button.tsx +344 -0
  128. package/src/library-components/ButtonSelector.tsx +64 -0
  129. package/src/library-components/DropdownCustom.tsx +151 -0
  130. package/src/library-components/DropdownSelector.tsx +32 -0
  131. package/src/library-components/Input.tsx +334 -0
  132. package/src/library-components/InputLabel.tsx +198 -0
  133. package/src/library-components/InputPicker.tsx +125 -0
  134. package/src/library-components/LazyComponent.tsx +62 -0
  135. package/src/library-components/MeasureHeightCSS.tsx +48 -0
  136. package/src/library-components/MeasuredDiv.tsx +47 -0
  137. package/src/library-components/ShowMore.tsx +51 -0
  138. package/src/library-components/SyncedController.ts +171 -0
  139. package/src/library-components/TimeRangeSelector.tsx +407 -0
  140. package/src/library-components/URLParam.ts +263 -0
  141. package/src/library-components/colors.tsx +14 -0
  142. package/src/library-components/drag.ts +114 -0
  143. package/src/library-components/icons.tsx +692 -0
  144. package/src/library-components/niceStringify.ts +50 -0
  145. package/src/library-components/renderToString.ts +52 -0
  146. package/src/misc/PromiseRace.ts +101 -0
  147. package/src/misc/color.ts +30 -0
  148. package/src/misc/getParentProcessId.cs +53 -0
  149. package/src/misc/getParentProcessId.ts +53 -0
  150. package/src/misc/hash.ts +83 -0
  151. package/src/misc/ipPong.js +13 -0
  152. package/src/misc/networking.ts +2 -0
  153. package/src/misc/random.ts +45 -0
  154. package/src/misc.ts +19 -0
  155. package/src/noserverhotreload.flag +0 -0
  156. package/src/path.ts +226 -0
  157. package/src/persistentLocalStore.ts +37 -0
  158. package/src/promise.ts +15 -0
  159. package/src/server.ts +73 -0
  160. package/src/src.d.ts +1 -0
  161. package/src/test/heapProcess.ts +36 -0
  162. package/src/test/mongoSatTest.tsx +55 -0
  163. package/src/test/satTest.ts +193 -0
  164. package/src/test/test.tsx +552 -0
  165. package/src/zip.ts +92 -0
  166. package/src/zipThreaded.ts +106 -0
  167. package/src/zipThreadedWorker.js +19 -0
  168. package/tsconfig.json +27 -0
  169. package/yarnSpec.txt +56 -0
@@ -0,0 +1,483 @@
1
+ import { blue, green, magenta, red, yellow } from "socket-function/src/formatting/logColors";
2
+ import { measureFnc } from "socket-function/src/profiling/measure";
3
+ import { ARCHIVE_FLUSH_LIMIT, ARCHIVE_FLUSH_LIMIT_START, compareTime, debugPathValuePath, getCompressNetwork, isCoreQuiet, PathValue, PathValueSnapshot, registerGetCompressNetwork, Time } from "./pathValueCore";
4
+ import { getArchives, nestArchives } from "../-a-archives/archives";
5
+ import { cache, cacheLimited, lazy } from "socket-function/src/caching";
6
+ import { getOwnNodeId, getOwnNodeIdAssert } from "../-f-node-discovery/NodeDiscovery";
7
+ import { pathValueSerializer } from "../-h-path-value-serialize/PathValueSerializer";
8
+ import debugbreak from "debugbreak";
9
+ import { formatNumber, formatTime } from "socket-function/src/formatting/format";
10
+ import { list } from "socket-function/src/misc";
11
+ import { AuthorityPath, pathValueAuthority2 } from "./NodePathAuthorities";
12
+ import { NodeIdParts, decodeNodeId, decodeNodeIdAssert, encodeNodeId } from "../-a-auth/certs";
13
+ import { createArchiveLocker2 } from "./archiveLocks/ArchiveLocks2";
14
+ import { devDebugbreak, isNoNetwork } from "../config";
15
+ import { wrapArchivesWithCache } from "../-a-archives/archiveCache";
16
+
17
+ const archives = lazy(() => wrapArchivesWithCache(getArchives("path-values/")));
18
+ const archivesLocks = lazy(() => getArchives("path-values-locks/"));
19
+ const archivesRecycleBin = lazy(() => getArchives("path-values-recycle-bin/"));
20
+
21
+ export type RawValuePaths = {
22
+ dataPath: string;
23
+ values: PathValue[];
24
+ error?: string;
25
+ };
26
+ export type DecodedValuePath = NodeIdParts & {
27
+ // Path is the original path we decoded this from.
28
+ path: string;
29
+
30
+ time: number;
31
+ seqNum: number;
32
+ valueCount: number;
33
+ byteCount: number;
34
+ minTime: number | undefined;
35
+ maxTime: number | undefined;
36
+
37
+ sourceType: "genesis" | "merge" | "gc" | string;
38
+ // misc can be anything that doesn't include "_"
39
+ // - AND, either was need lastMoved, OR, it needs to not decode with decodeNodeId
40
+ misc?: string;
41
+ };
42
+ export type DecodedValuePathInput = Omit<DecodedValuePath, "path">;
43
+ function defaultDecodedValuePath(path: string): DecodedValuePath {
44
+ return {
45
+ path,
46
+ time: 0, seqNum: 0, valueCount: 0, byteCount: 0, threadId: "", domain: "", machineId: "", port: -1,
47
+ maxTime: undefined, minTime: undefined,
48
+ sourceType: "genesis",
49
+ };
50
+ }
51
+
52
+
53
+
54
+ export class PathValueArchives {
55
+ nextWriteSeqNum = 1;
56
+
57
+ // NOTE: We only archive the latest value for each path, and only read the latest. This means that all
58
+ // archives values MUST be golden (valid and as old as the max change state), otherwise the archives might
59
+ // turn invalid values into valid values.
60
+
61
+ @measureFnc
62
+ public decodeDataPathBatch(dataPath: string[]) {
63
+ return dataPath.map(x => this.decodeDataPath(x));
64
+ }
65
+ public decodeDataPath(path: string): DecodedValuePath {
66
+ let originalPath = path;
67
+ if (path.endsWith(".locked")) {
68
+ path = path.slice(0, -".locked".length);
69
+ }
70
+ if (!path.endsWith(".data")) return defaultDecodedValuePath(path);
71
+ let dataPath = path.slice(0, -".data".length);
72
+ let fileName = dataPath.replaceAll("\\", "/").split("/").at(-1)!;
73
+ let parts = fileName.split("_");
74
+ if (parts[0] !== "data") return defaultDecodedValuePath(dataPath);
75
+ let nodeId = parts[1];
76
+ let decodedNodeId = decodeNodeId(nodeId);
77
+ if (!decodedNodeId) return defaultDecodedValuePath(dataPath);
78
+ let time = +parts[2] || 0;
79
+ let seqNum = +parts[3] || 0;
80
+ let valueCount = +parts[4] || 0;
81
+ let byteCount = +parts[5] || 0;
82
+ let minTime = +parts[6] || 0;
83
+ let maxTime = +parts[7] || 0;
84
+ let sourceType = parts[8] as DecodedValuePath["sourceType"] || "genesis";
85
+ let misc = parts[9];
86
+ return {
87
+ path: originalPath,
88
+ ...decodedNodeId,
89
+ minTime, maxTime,
90
+ time, seqNum, valueCount, byteCount,
91
+ sourceType,
92
+ misc,
93
+ };
94
+ }
95
+ public encodeDataPath(decoded: DecodedValuePathInput) {
96
+ return `data_${encodeNodeId(decoded)}_${decoded.time}_${decoded.seqNum}_${decoded.valueCount}_${decoded.byteCount}_${decoded.minTime}_${decoded.maxTime}_${decoded.sourceType}_${decoded.misc || ""}.data`;
97
+ }
98
+
99
+ public getDefaultValuePathInput(): DecodedValuePathInput {
100
+ return {
101
+ ...decodeNodeIdAssert(getOwnNodeIdAssert()),
102
+ time: Date.now(),
103
+ seqNum: this.nextWriteSeqNum++,
104
+ valueCount: 0,
105
+ byteCount: 0,
106
+ minTime: Date.now(), maxTime: Date.now(),
107
+ sourceType: "genesis",
108
+ };
109
+ }
110
+
111
+ public async encodeValuePaths(
112
+ values: PathValue[],
113
+ config?: {
114
+ pathOverrides?: Partial<DecodedValuePathInput>,
115
+ // Don't dedupe values
116
+ noFilter?: boolean;
117
+ }
118
+ ) {
119
+ let time = Date.now();
120
+
121
+ if (!isCoreQuiet) {
122
+ console.log(blue(`Serializing ${values.length} ValuePaths`));
123
+ }
124
+ if (values.length === 0) return undefined;
125
+
126
+ if (!config?.noFilter) {
127
+ values = await this.filterForDiskStorage(values);
128
+ }
129
+
130
+ let buffers = await pathValueSerializer.serialize(values, { noLocks: true, compress: getCompressNetwork(), singleBuffer: true });
131
+ let data = buffers[0];
132
+
133
+ let minTime = values[0].time.time;
134
+ let maxTime = values[0].time.time;
135
+ for (let value of values) {
136
+ let time = value.time.time;
137
+ if (time < minTime) minTime = time;
138
+ if (time > maxTime) maxTime = time;
139
+ }
140
+
141
+ // NOTE: Due to merging storing firstTime/lastTime won't be overly useful here, as we should shard enough
142
+ // that nodes only have a few data files (and so the time extent would be quickly become the
143
+ // entire available time range).
144
+ let file = this.encodeDataPath({
145
+ ...this.getDefaultValuePathInput(),
146
+ valueCount: values.length,
147
+ byteCount: data.byteLength,
148
+ minTime, maxTime,
149
+ sourceType: "genesis",
150
+ ...config?.pathOverrides,
151
+ });
152
+
153
+ if (!isCoreQuiet) {
154
+ time = Date.now() - time;
155
+ console.log(blue(`Finished serializing ${values.length} in ${formatTime(time)} for ${file}`));
156
+ }
157
+
158
+ return { file, data };
159
+ }
160
+
161
+
162
+ @measureFnc
163
+ public async archiveValues(pathAuthority: AuthorityPath, values: PathValue[]) {
164
+ let authorityPath = pathValueAuthority2.getArchiveDirectory(pathAuthority);
165
+
166
+ let encodedObj = await this.encodeValuePaths(values);
167
+ if (!encodedObj) return;
168
+ let { file, data } = encodedObj;
169
+
170
+ let time = Date.now();
171
+
172
+ let maxAttemptArchiveTime = Date.now() - ARCHIVE_FLUSH_LIMIT;
173
+
174
+ let oldestTime = Number.MAX_SAFE_INTEGER;
175
+ for (let value of values) {
176
+ let time = value.time.time;
177
+ // We should be ignoring values WAY before this on receive. We can't archive it now,
178
+ // because even though not archiving it will lose data, if we do archive it we will
179
+ // break existing servers (and it could cause security issues, etc).
180
+ if (time < maxAttemptArchiveTime) {
181
+ devDebugbreak();
182
+ console.error(`Tried to archive a value which is too far into the past, ${time} < ${maxAttemptArchiveTime}. To commit this to disk would break locks, and could leave the database in an invalid state. Killing server so clients will resync with a server that is in a better state.`);
183
+ process.exit();
184
+ }
185
+ if (time < oldestTime) oldestTime = time;
186
+ }
187
+ let slowestFileWriteTime = oldestTime + ARCHIVE_FLUSH_LIMIT;
188
+ await archives().set(authorityPath + file, data);
189
+
190
+ let fileInfo = await archives().getInfo(authorityPath + file);
191
+ // NOTE: If no fileInfo... then our file was merged? Which... is BAD, as it means we took
192
+ // too long to read it, so we probably took too long to write it too!
193
+ if (!fileInfo || fileInfo.writeTime > slowestFileWriteTime) {
194
+ console.error(red(`File ${authorityPath + file} was written too slowly, ${fileInfo?.writeTime || "undefined"} < ${slowestFileWriteTime}. This means some values will be rejected by reads. Killing server, our state is irrecoverable. Our watches have invalid data, and we have to stop before we create more invalid dependencies.`));
195
+ process.exit();
196
+ }
197
+
198
+
199
+ if (!isCoreQuiet) {
200
+ time = Date.now() - time;
201
+ console.log(blue(`Finished archiving ${values.length} to ${authorityPath} in ${formatTime(time)}`));
202
+ }
203
+ }
204
+
205
+ @measureFnc
206
+ public async loadValues(authority: AuthorityPath): Promise<PathValueSnapshot> {
207
+
208
+
209
+ let dirName = pathValueAuthority2.getArchiveDirectory(authority);
210
+ console.log(blue(`Loading data ${dirName} at ${new Date().toISOString()}`));
211
+
212
+ // let testFileName = getOwnNodeId() + "_" + Date.now() + "_" + 1_000_000_000;
213
+ // await archives().assertPathValid(dirName + "data_" + testFileName + ".data");
214
+
215
+ let snapshot: PathValueSnapshot = {
216
+ values: {},
217
+ };
218
+ let totalCount = 0;
219
+ let uniqueCount = 0;
220
+ let fileCount = 0;
221
+ let totalSize = 0;
222
+
223
+ let dataPaths: string[] = [];
224
+ let readCache = new Map<string, Buffer>();
225
+
226
+ let downloadStartTime = Date.now();
227
+ while (true) {
228
+ // NOTE: Getting all data paths FIRST is important. Otherwise files moves (even ones which take
229
+ // into account readers by copying, waiting a minute, and then deleting) can cause us to miss data
230
+ // (we read the target dest, find no file, read a bunch of other files over a few minutes, get to
231
+ // the source file, but by now it has moved, so we don't see either file, even though at all times
232
+ // both files existed).
233
+ dataPaths = await this.getValuePaths(authority);
234
+ console.log(blue(` ${dataPaths.length} data paths`));
235
+ // NOTE: If the notMatched count is high enough, it is possible NodePathAuthorities.ts:authoritiesMightOverlap is
236
+ // too loose, and should be removing more cases.
237
+
238
+ let pendingDataPaths = dataPaths.slice();
239
+ // We CAN'T just read everything at once, as this will cause us to
240
+ // run out of file handles, and error out.
241
+ async function runReadThread() {
242
+ while (pendingDataPaths.length > 0) {
243
+ let dataPath = pendingDataPaths.pop()!;
244
+ if (readCache.has(dataPath)) continue;
245
+ let data = await archives().get(dataPath);
246
+ if (!data) continue;
247
+ readCache.set(dataPath, data);
248
+ }
249
+ }
250
+ await Promise.all(list(32).map(runReadThread));
251
+
252
+ let files = dataPaths.map(x => readCache.get(x));
253
+ if (files.some(x => !x)) {
254
+ let missingFiles = dataPaths.filter(x => !readCache.has(x));
255
+ // This likely means a merge happened while we were reading the files. If we just ignore the
256
+ // missing files, we will almost certainly miss the merge output files, which will cause us
257
+ // to miss a lot of data, and be in an entirely broken state.
258
+ console.log(red("Some files are missing, rereading directory and trying again"));
259
+ for (let missingFile of missingFiles) {
260
+ console.log(red(` ${missingFile}`));
261
+ }
262
+ continue;
263
+ }
264
+ break;
265
+ }
266
+ console.log(green(`Downloaded/read ${dataPaths.length} data paths in ${formatTime(Date.now() - downloadStartTime)}`));
267
+
268
+ let parseTime = Date.now();
269
+ let notMatched = 0;
270
+ let tooOldValues = 0;
271
+
272
+ for (let dataFile of dataPaths) {
273
+ let data = readCache.get(dataFile);
274
+ if (!data) throw new Error(`Data file ${dataFile} not in lookup which we just checked?`);
275
+ totalSize += data.byteLength;
276
+ fileCount++;
277
+ let loadObj = await PathValueArchives.loadValuesFromBuffer({ path: dataFile, data });
278
+ let dataValues = loadObj.values;
279
+ tooOldValues += loadObj.tooOldValues;
280
+ totalCount += loadObj.rawCount;
281
+
282
+ for (let value of dataValues) {
283
+ // NOTE: We can't return extra values, as they might be outdated values which were
284
+ // set to undefined, and then deleted, so returning them will create spurious values.
285
+ if (!pathValueAuthority2.isInAuthority(authority, value.path)) {
286
+ notMatched++;
287
+ continue;
288
+ }
289
+ let path = value.path;
290
+ if (!snapshot.values[path]) {
291
+ uniqueCount++;
292
+ snapshot.values[path] = [value];
293
+ } else {
294
+ if (compareTime(value.time, snapshot.values[path][0].time) >= 0) {
295
+ snapshot.values[path][0] = value;
296
+ }
297
+ }
298
+ }
299
+ }
300
+
301
+ let relevantCount = totalCount - notMatched;
302
+ parseTime = Date.now() - parseTime;
303
+ console.log((`${green("Finished parsing")} ${formatNumber(totalCount)} in ${formatNumber(totalSize)}B (${formatNumber(uniqueCount / relevantCount * 100)}% unique paths = ${formatNumber(uniqueCount)}), in ${green(formatTime(parseTime))}, ${fileCount} files, authority ${magenta(dirName)}, (${formatNumber(notMatched / totalCount * 100)}% ignored, due to being for another authority) at ${new Date().toISOString()}. Startup time ${green(formatTime(process.uptime() * 1000))}`));
304
+
305
+ if (tooOldValues) {
306
+ console.warn(yellow(`Discarded ${formatNumber(tooOldValues)} values discarded for being part of hanging server writes`));
307
+ }
308
+
309
+ return {
310
+ values: snapshot.values,
311
+ };
312
+ }
313
+
314
+ public static async loadValuesFromBuffer(config: {
315
+ path: string;
316
+ data: Buffer;
317
+ skipStrings?: boolean;
318
+ skipValues?: boolean;
319
+ }): Promise<{
320
+ values: PathValue[];
321
+ rawCount: number;
322
+ tooOldValues: number;
323
+ }> {
324
+ const { path, data } = config;
325
+ let tooOldValues = 0;
326
+
327
+ let dataValues: PathValue[] = [];
328
+
329
+ try {
330
+ dataValues = await pathValueSerializer.deserialize([data], {
331
+ singleBuffer: true,
332
+ skipStrings: config.skipStrings,
333
+ skipValues: config.skipValues,
334
+ });
335
+ } catch (e: any) {
336
+ console.log(red(`Bad archive data file at ${config.path}, error: ${e.stack}`));
337
+ }
338
+ let rawCount = dataValues.length;
339
+
340
+ let fileObj = pathValueArchives.decodeDataPath(path);
341
+ if (
342
+ fileObj.sourceType === "genesis"
343
+ && fileObj.time > ARCHIVE_FLUSH_LIMIT_START
344
+ ) {
345
+ let oldestAllowedTime = fileObj.time - ARCHIVE_FLUSH_LIMIT_START;
346
+ dataValues = dataValues.filter(x => {
347
+ if (x.time.time < oldestAllowedTime) {
348
+ tooOldValues++;
349
+ return false;
350
+ }
351
+ return true;
352
+ });
353
+ }
354
+
355
+ return {
356
+ values: dataValues,
357
+ rawCount,
358
+ tooOldValues,
359
+ };
360
+ }
361
+
362
+ public async getRawValueFiles(dataPath: string[], config?: {
363
+ includeRecycleBin?: boolean;
364
+ }): Promise<(Buffer | undefined)[]> {
365
+ // let results: (Buffer | undefined)[] = [];
366
+ // let sum = 0;
367
+ // for (let path of dataPath) {
368
+ // console.log(blue(`Getting ${path}`));
369
+ // let result = await archives().get(path);
370
+ // results.push(result);
371
+ // console.log(blue(`Finished getting ${path}, size ${formatNumber(result?.byteLength || 0)}`));
372
+ // sum += result?.byteLength || 0;
373
+ // }
374
+ // console.log(blue(`Total size ${formatNumber(sum)}`));
375
+ // return results;
376
+ let results: (Buffer | undefined)[] = [];
377
+ const BATCH_SIZE = 64;
378
+ for (let i = 0; i < dataPath.length; i += BATCH_SIZE) {
379
+ let cur = dataPath.slice(i, i + BATCH_SIZE);
380
+ let curResults = await Promise.all(cur.map(x => archives().get(x)));
381
+ results.push(...curResults);
382
+ }
383
+ if (config?.includeRecycleBin) {
384
+ let notFoundIndexes = new Map<number, string>();
385
+ for (let i = 0; i < dataPath.length; i++) {
386
+ if (!results[i]) {
387
+ notFoundIndexes.set(i, dataPath[i]);
388
+ }
389
+ }
390
+ let notFoundList = Array.from(notFoundIndexes.keys());
391
+ for (let i = 0; i < notFoundList.length; i += BATCH_SIZE) {
392
+ let curIndexes = notFoundList.slice(i, i + BATCH_SIZE);
393
+ let cur = curIndexes.map(x => dataPath[x]);
394
+ let curResults = await Promise.all(cur.map(x => archivesRecycleBin().get(x)));
395
+ for (let j = 0; j < curResults.length; j++) {
396
+ results[curIndexes[j]] = curResults[j];
397
+ }
398
+ }
399
+ }
400
+ return results;
401
+ }
402
+
403
+ public async getArchiveLocker() {
404
+ // return createArchiveLocker({
405
+ // archiveValues: archives(),
406
+ // archiveRecycleBin: archivesRecycleBin(),
407
+ // archiveLocks: archivesLocks(),
408
+ // });
409
+ return createArchiveLocker2({
410
+ archiveValues: archives(),
411
+ archiveRecycleBin: archivesRecycleBin(),
412
+ archiveLocks: archivesLocks(),
413
+ });
414
+ }
415
+ @measureFnc
416
+ public async getAuthorityDirs(authority: AuthorityPath): Promise<string[]> {
417
+ let allAuthorityDirectories = await archives().find("", { shallow: true, type: "folders" });
418
+
419
+ let pickedDirectories = new Set<string>();
420
+ const authorityDir = pathValueAuthority2.getArchiveDirectory(authority);
421
+ pickedDirectories.add(authorityDir);
422
+ for (let filePath of allAuthorityDirectories) {
423
+ if (pathValueAuthority2.mightMatchArchiveDirectory(authority, filePath)) {
424
+ pickedDirectories.add(filePath);
425
+ }
426
+ }
427
+
428
+ return Array.from(pickedDirectories);
429
+ }
430
+
431
+ @measureFnc
432
+ public async getValuePaths(authority: AuthorityPath): Promise<string[]> {
433
+ // let paths: string[] = [];
434
+ // let pickedDirectories = await this.getAuthorityDirs(authority);
435
+
436
+ // for (let authorityFilePath of pickedDirectories) {
437
+ // let dataFiles = await archives().find(authorityFilePath + "/data_", { shallow: true, type: "files" });
438
+ // for (let dataFile of dataFiles) {
439
+ // paths.push(dataFile);
440
+ // }
441
+ // }
442
+
443
+ let locker = await this.getArchiveLocker();
444
+ let allFiles = (await locker.getAllValidFiles()).map(x => x.file);
445
+ return allFiles.filter(x => pathValueAuthority2.mightMatchArchiveDirectory(authority, x));
446
+ }
447
+ @measureFnc
448
+ public async getValuePathSizes(paths: string[]): Promise<number[]> {
449
+ return await Promise.all(paths.map(async path => {
450
+ let info = await archives().getInfo(path);
451
+ // if (!info) {
452
+ // let value = await archives().get(path);
453
+ // if (value) {
454
+ // debugbreak(2);
455
+ // debugger;
456
+ // await archives().getInfo(path);
457
+ // console.log(info, value);
458
+ // }
459
+ // }
460
+ return info?.size || 0;
461
+ }));
462
+ }
463
+
464
+
465
+ @measureFnc
466
+ private filterForDiskStorage(values: PathValue[]): PathValue[] {
467
+ let latestValues = new Map<string, PathValue>();
468
+ for (let value of values) {
469
+ if (!value.valid) {
470
+ console.error(`Only valid values should be archived. Tried to archive invalid value ${debugPathValuePath(value)}`);
471
+ continue;
472
+ }
473
+ let prev = latestValues.get(value.path);
474
+ if (prev && compareTime(value.time, prev.time) < 0) continue;
475
+ latestValues.set(value.path, value);
476
+ }
477
+
478
+ values = Array.from(latestValues.values());
479
+
480
+ return values;
481
+ }
482
+ }
483
+ export const pathValueArchives = new PathValueArchives();