querysub 0.356.0 → 0.357.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/.cursorrules +8 -0
- package/bin/movelogs.js +4 -0
- package/package.json +12 -6
- package/scripts/postinstall.js +23 -0
- package/src/-a-archives/archiveCache.ts +10 -12
- package/src/-a-archives/archives.ts +29 -0
- package/src/-a-archives/archivesBackBlaze.ts +60 -12
- package/src/-a-archives/archivesDisk.ts +27 -8
- package/src/-a-archives/archivesLimitedCache.ts +21 -0
- package/src/-a-archives/archivesMemoryCache.ts +350 -0
- package/src/-a-archives/archivesPrivateFileSystem.ts +22 -0
- package/src/-g-core-values/NodeCapabilities.ts +3 -0
- package/src/0-path-value-core/auditLogs.ts +5 -1
- package/src/0-path-value-core/pathValueCore.ts +7 -7
- package/src/4-dom/qreact.tsx +1 -0
- package/src/4-querysub/Querysub.ts +1 -5
- package/src/config.ts +5 -0
- package/src/diagnostics/MachineThreadInfo.tsx +235 -0
- package/src/diagnostics/NodeViewer.tsx +3 -2
- package/src/diagnostics/logs/FastArchiveAppendable.ts +79 -42
- package/src/diagnostics/logs/FastArchiveController.ts +102 -63
- package/src/diagnostics/logs/FastArchiveViewer.tsx +36 -8
- package/src/diagnostics/logs/IndexedLogs/BufferIndex.ts +461 -0
- package/src/diagnostics/logs/IndexedLogs/BufferIndexCPP.cpp +327 -0
- package/src/diagnostics/logs/IndexedLogs/BufferIndexCPP.d.ts +18 -0
- package/src/diagnostics/logs/IndexedLogs/BufferIndexCPP.js +1 -0
- package/src/diagnostics/logs/IndexedLogs/BufferIndexHelpers.ts +140 -0
- package/src/diagnostics/logs/IndexedLogs/BufferIndexLogsOptimizationConstants.ts +22 -0
- package/src/diagnostics/logs/IndexedLogs/BufferIndexWAT.wat +1145 -0
- package/src/diagnostics/logs/IndexedLogs/BufferIndexWAT.wat.d.ts +178 -0
- package/src/diagnostics/logs/IndexedLogs/BufferListStreamer.ts +206 -0
- package/src/diagnostics/logs/IndexedLogs/BufferUnitIndex.ts +719 -0
- package/src/diagnostics/logs/IndexedLogs/BufferUnitSet.ts +146 -0
- package/src/diagnostics/logs/IndexedLogs/FilePathSelector.tsx +408 -0
- package/src/diagnostics/logs/IndexedLogs/FindProgressTracker.ts +45 -0
- package/src/diagnostics/logs/IndexedLogs/IndexedLogs.ts +598 -0
- package/src/diagnostics/logs/IndexedLogs/LogStreamer.ts +47 -0
- package/src/diagnostics/logs/IndexedLogs/LogViewer3.tsx +702 -0
- package/src/diagnostics/logs/IndexedLogs/TimeFileTree.ts +236 -0
- package/src/diagnostics/logs/IndexedLogs/binding.gyp +23 -0
- package/src/diagnostics/logs/IndexedLogs/moveIndexLogsToPublic.ts +221 -0
- package/src/diagnostics/logs/IndexedLogs/moveLogsEntry.ts +10 -0
- package/src/diagnostics/logs/LogViewer2.tsx +120 -55
- package/src/diagnostics/logs/TimeRangeSelector.tsx +5 -2
- package/src/diagnostics/logs/diskLogger.ts +32 -48
- package/src/diagnostics/logs/errorNotifications/ErrorNotificationController.ts +3 -2
- package/src/diagnostics/logs/errorNotifications/errorDigests.tsx +1 -0
- package/src/diagnostics/logs/lifeCycleAnalysis/LifeCyclePages.tsx +150 -0
- package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycles.tsx +132 -15
- package/src/diagnostics/logs/lifeCycleAnalysis/test.ts +180 -0
- package/src/diagnostics/logs/lifeCycleAnalysis/test.wat +106 -0
- package/src/diagnostics/logs/lifeCycleAnalysis/test.wat.d.ts +2 -0
- package/src/diagnostics/logs/lifeCycleAnalysis/testHoist.ts +5 -0
- package/src/diagnostics/logs/logViewerExtractField.ts +2 -3
- package/src/diagnostics/managementPages.tsx +10 -0
- package/src/diagnostics/trackResources.ts +1 -1
- package/src/misc/lz4_wasm_nodejs.d.ts +34 -0
- package/src/misc/lz4_wasm_nodejs.js +178 -0
- package/src/misc/lz4_wasm_nodejs_bg.js +94 -0
- package/src/misc/lz4_wasm_nodejs_bg.wasm +0 -0
- package/src/misc/lz4_wasm_nodejs_bg.wasm.d.ts +15 -0
- package/src/storage/CompressedStream.ts +13 -0
- package/src/storage/LZ4.ts +32 -0
- package/src/storage/ZSTD.ts +10 -0
- package/src/wat/watCompiler.ts +1716 -0
- package/src/wat/watGrammar.pegjs +93 -0
- package/src/wat/watHandler.ts +179 -0
- package/src/wat/watInstructions.txt +707 -0
- package/src/zip.ts +3 -89
- package/src/diagnostics/logs/lifeCycleAnalysis/spec.md +0 -125
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import { measureWrap } from "socket-function/src/profiling/measure";
|
|
2
|
+
import { Archives, createArchivesOverride } from "./archives";
|
|
3
|
+
|
|
4
|
+
interface CacheEntry {
|
|
5
|
+
path: string;
|
|
6
|
+
start: number;
|
|
7
|
+
end: number;
|
|
8
|
+
data: Buffer;
|
|
9
|
+
size: number;
|
|
10
|
+
lastAccess: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type ArchivesMemoryCacheStats = {
|
|
14
|
+
cachedReads: number;
|
|
15
|
+
uncachedReads: number;
|
|
16
|
+
cachedReadSize: number;
|
|
17
|
+
uncachedReadSize: number;
|
|
18
|
+
totalCacheSize: number;
|
|
19
|
+
totalCacheCount: number;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
let cacheDisabled = false;
|
|
23
|
+
export function testDisableCache() {
|
|
24
|
+
cacheDisabled = true;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Creates an in-memory cache for the archives. We expect that the values will only have values appended to them or be deleted. Also, if a value is deleted, we won't erase our in-memory cache, and so we will still allow reading from deleted values. */
|
|
28
|
+
export function createArchivesMemoryCache(
|
|
29
|
+
archives: Archives,
|
|
30
|
+
config?: {
|
|
31
|
+
extraReadSize?: number;
|
|
32
|
+
maxSize?: number;
|
|
33
|
+
maxCount?: number;
|
|
34
|
+
// If the files are guaranteed to be immutable, then all reads, including reading entire file and reading any range, can be cached.
|
|
35
|
+
// - Otherwise, we can't cache when the entire file is read, as there might be further appends after it.
|
|
36
|
+
// - Also, it means if there's a range read that happens, if that range read doesn't return enough bytes, for example, we try to read from 0 to 100, but we only get 50 bytes back, we can only cache the 0 to 50, and then if they read from 0 to 100 again, we can use the cache, but we have to also read 50 to 100 from the source, because the file might have gotten larger.
|
|
37
|
+
fullyImmutable?: boolean;
|
|
38
|
+
stats?: ArchivesMemoryCacheStats;
|
|
39
|
+
}
|
|
40
|
+
): Archives {
|
|
41
|
+
let {
|
|
42
|
+
maxSize = 1024 * 1024 * 1024 * 4,
|
|
43
|
+
maxCount = 1000 * 1000,
|
|
44
|
+
fullyImmutable = false,
|
|
45
|
+
extraReadSize = 1024
|
|
46
|
+
} = config ?? {};
|
|
47
|
+
|
|
48
|
+
// Cache structure: Map from path to sorted array of ranges
|
|
49
|
+
let cacheByPath = new Map<string, CacheEntry[]>();
|
|
50
|
+
// LRU tracking: all entries sorted by last access time
|
|
51
|
+
let lruArray: CacheEntry[] = [];
|
|
52
|
+
let totalSize = 0;
|
|
53
|
+
|
|
54
|
+
const alignRange = measureWrap(function alignRange(start: number, end: number): { start: number; end: number; } {
|
|
55
|
+
if (extraReadSize <= 1) {
|
|
56
|
+
return { start, end };
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
start: Math.floor(start / extraReadSize) * extraReadSize,
|
|
60
|
+
end: Math.ceil(end / extraReadSize) * extraReadSize
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const updateAccess = measureWrap(function updateAccess(entry: CacheEntry) {
|
|
65
|
+
entry.lastAccess = Date.now();
|
|
66
|
+
// Move to end of LRU array (most recent)
|
|
67
|
+
let index = lruArray.indexOf(entry);
|
|
68
|
+
if (index !== -1) {
|
|
69
|
+
lruArray.splice(index, 1);
|
|
70
|
+
lruArray.push(entry);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const evictOldest = measureWrap(function evictOldest() {
|
|
75
|
+
while ((totalSize > maxSize || lruArray.length > maxCount) && lruArray.length > 0) {
|
|
76
|
+
let oldest = lruArray.shift();
|
|
77
|
+
if (oldest) {
|
|
78
|
+
totalSize -= oldest.size;
|
|
79
|
+
// Remove from path map
|
|
80
|
+
let pathEntries = cacheByPath.get(oldest.path);
|
|
81
|
+
if (pathEntries) {
|
|
82
|
+
let index = pathEntries.indexOf(oldest);
|
|
83
|
+
if (index !== -1) {
|
|
84
|
+
pathEntries.splice(index, 1);
|
|
85
|
+
}
|
|
86
|
+
if (pathEntries.length === 0) {
|
|
87
|
+
cacheByPath.delete(oldest.path);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const removeEntry = measureWrap(function removeEntry(entry: CacheEntry) {
|
|
95
|
+
totalSize -= entry.size;
|
|
96
|
+
|
|
97
|
+
// Remove from path map
|
|
98
|
+
let pathEntries = cacheByPath.get(entry.path);
|
|
99
|
+
if (pathEntries) {
|
|
100
|
+
let index = pathEntries.indexOf(entry);
|
|
101
|
+
if (index !== -1) {
|
|
102
|
+
pathEntries.splice(index, 1);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Remove from LRU array
|
|
107
|
+
let lruIndex = lruArray.indexOf(entry);
|
|
108
|
+
if (lruIndex !== -1) {
|
|
109
|
+
lruArray.splice(lruIndex, 1);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const addEntry = measureWrap(function addEntry(path: string, start: number, end: number, data: Buffer) {
|
|
114
|
+
let entry: CacheEntry = {
|
|
115
|
+
path,
|
|
116
|
+
start,
|
|
117
|
+
end,
|
|
118
|
+
data,
|
|
119
|
+
size: data.length,
|
|
120
|
+
lastAccess: Date.now()
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// Add to path map (keeping sorted by start)
|
|
124
|
+
let pathEntries = cacheByPath.get(path);
|
|
125
|
+
if (!pathEntries) {
|
|
126
|
+
pathEntries = [];
|
|
127
|
+
cacheByPath.set(path, pathEntries);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Insert sorted by start position
|
|
131
|
+
let insertIndex = 0;
|
|
132
|
+
while (insertIndex < pathEntries.length && pathEntries[insertIndex].start < start) {
|
|
133
|
+
insertIndex++;
|
|
134
|
+
}
|
|
135
|
+
pathEntries.splice(insertIndex, 0, entry);
|
|
136
|
+
|
|
137
|
+
// Add to LRU array
|
|
138
|
+
lruArray.push(entry);
|
|
139
|
+
totalSize += entry.size;
|
|
140
|
+
|
|
141
|
+
evictOldest();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const findOverlappingEntries = measureWrap(function findOverlappingEntries(path: string, start: number, end: number): CacheEntry[] {
|
|
145
|
+
let pathEntries = cacheByPath.get(path);
|
|
146
|
+
if (!pathEntries) {
|
|
147
|
+
return [];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
let overlapping: CacheEntry[] = [];
|
|
151
|
+
for (let entry of pathEntries) {
|
|
152
|
+
// Check if ranges overlap: entry overlaps if it ends after start and starts before end
|
|
153
|
+
if (entry.end > start && entry.start < end) {
|
|
154
|
+
overlapping.push(entry);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return overlapping;
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const merge = measureWrap(function merge(path: string) {
|
|
161
|
+
let pathEntries = cacheByPath.get(path);
|
|
162
|
+
if (!pathEntries || pathEntries.length <= 1) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Entries are already sorted by start
|
|
167
|
+
// Merge any overlapping or adjacent entries
|
|
168
|
+
for (let i = 0; i < pathEntries.length - 1; i++) {
|
|
169
|
+
let current = pathEntries[i];
|
|
170
|
+
let next = pathEntries[i + 1];
|
|
171
|
+
|
|
172
|
+
// Check if current and next are overlapping or adjacent
|
|
173
|
+
if (current.end >= next.start) {
|
|
174
|
+
// Calculate how much data from next extends beyond current
|
|
175
|
+
let newEnd = Math.max(current.end, next.end);
|
|
176
|
+
let bytesToCopy = newEnd - current.end;
|
|
177
|
+
|
|
178
|
+
if (bytesToCopy > 0) {
|
|
179
|
+
// Extend current's data with data from next
|
|
180
|
+
let newData = Buffer.alloc(newEnd - current.start);
|
|
181
|
+
current.data.copy(newData, 0);
|
|
182
|
+
|
|
183
|
+
// Copy the part of next that extends beyond current
|
|
184
|
+
let nextOffset = current.end - next.start;
|
|
185
|
+
next.data.copy(newData, current.end - current.start, nextOffset, nextOffset + bytesToCopy);
|
|
186
|
+
|
|
187
|
+
current.data = newData;
|
|
188
|
+
current.end = newEnd;
|
|
189
|
+
current.size = newData.length;
|
|
190
|
+
totalSize += bytesToCopy;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Remove next entry
|
|
194
|
+
removeEntry(next);
|
|
195
|
+
|
|
196
|
+
// Check current against the new next
|
|
197
|
+
i--;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
async function cachedGet(path: string, getConfig?: {
|
|
203
|
+
range?: { start: number; end: number; };
|
|
204
|
+
retryCount?: number;
|
|
205
|
+
fastRead?: boolean;
|
|
206
|
+
}): Promise<Buffer | undefined> {
|
|
207
|
+
if (cacheDisabled) {
|
|
208
|
+
return await archives.get(path, getConfig);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
let range = getConfig?.range;
|
|
212
|
+
|
|
213
|
+
// If no range specified, read entire file
|
|
214
|
+
if (!range) {
|
|
215
|
+
// If not fully immutable, don't cache entire file reads (might have appends)
|
|
216
|
+
if (!fullyImmutable) {
|
|
217
|
+
return await archives.get(path, getConfig);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Get file info to determine size, then treat as range read
|
|
221
|
+
let info = await archives.getInfo(path);
|
|
222
|
+
if (!info) {
|
|
223
|
+
return undefined;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Convert to range read from 0 to file size
|
|
227
|
+
range = { start: 0, end: info.size };
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Handle range reads
|
|
231
|
+
let { start, end } = range;
|
|
232
|
+
|
|
233
|
+
// If we have something which satisfies the range request, we should use this first. Otherwise, every single request requires doing an API call, which is extremely slow.
|
|
234
|
+
{
|
|
235
|
+
for (let entry of cacheByPath.get(path) || []) {
|
|
236
|
+
if (entry.start <= start && entry.end >= end) {
|
|
237
|
+
return entry.data.slice(start - entry.start, end - entry.start);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
let size = await archives.getInfo(path);
|
|
243
|
+
if (!size) {
|
|
244
|
+
return undefined;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Track if we had to perform any read from the underlying archives
|
|
248
|
+
let didRead = false;
|
|
249
|
+
{
|
|
250
|
+
let aligned = alignRange(start, end);
|
|
251
|
+
|
|
252
|
+
let readStart = aligned.start;
|
|
253
|
+
let readEnd = Math.min(aligned.end, size.size);
|
|
254
|
+
|
|
255
|
+
let overlapping = findOverlappingEntries(path, start, end);
|
|
256
|
+
|
|
257
|
+
// Check if we can trim the beginning
|
|
258
|
+
for (let entry of overlapping) {
|
|
259
|
+
if (entry.start <= readStart && entry.end > readStart) {
|
|
260
|
+
// We have cached data covering the beginning
|
|
261
|
+
readStart = Math.max(readStart, entry.end);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
let haveEnd = false;
|
|
266
|
+
|
|
267
|
+
// Check if we can trim the end
|
|
268
|
+
for (let entry of overlapping) {
|
|
269
|
+
if (entry.start < readEnd && entry.end >= readEnd) {
|
|
270
|
+
// We have cached data covering the end
|
|
271
|
+
readEnd = Math.min(readEnd, entry.start);
|
|
272
|
+
haveEnd = true;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
// If after trimming we have nothing to read, we have complete coverage
|
|
278
|
+
if (readStart < readEnd) {
|
|
279
|
+
// Read the single contiguous range
|
|
280
|
+
let data = await archives.get(path, {
|
|
281
|
+
...getConfig,
|
|
282
|
+
range: { start: readStart, end: readEnd }
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
if (!data) {
|
|
286
|
+
return undefined;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
didRead = true;
|
|
290
|
+
|
|
291
|
+
// Cache the data we read (it's immutable even if the file can have future appends)
|
|
292
|
+
readEnd = readStart + data.length;
|
|
293
|
+
// If they tried to read too far then we reduce the end value, otherwise we'll be looking for something which we didn't satisfy and we'll get an error.
|
|
294
|
+
if (!haveEnd) {
|
|
295
|
+
end = Math.min(end, readEnd);
|
|
296
|
+
}
|
|
297
|
+
addEntry(path, readStart, readEnd, data);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Always merge after potentially adding new data
|
|
302
|
+
merge(path);
|
|
303
|
+
|
|
304
|
+
// After merging, find the single entry that covers our aligned range
|
|
305
|
+
let pathEntries = cacheByPath.get(path);
|
|
306
|
+
if (!pathEntries) {
|
|
307
|
+
throw new Error(`Expected cached entries for path ${path} after merge`);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
let coveringEntry: CacheEntry | undefined = undefined;
|
|
311
|
+
for (let entry of pathEntries) {
|
|
312
|
+
if (entry.start <= start && entry.end >= end) {
|
|
313
|
+
if (coveringEntry) {
|
|
314
|
+
throw new Error(`Multiple entries cover range ${start}-${end} for path ${path}`);
|
|
315
|
+
}
|
|
316
|
+
coveringEntry = entry;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (!coveringEntry) {
|
|
321
|
+
throw new Error(`No entry covers range ${start}-${end} for path ${path}`);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
updateAccess(coveringEntry);
|
|
325
|
+
|
|
326
|
+
// Extract the requested range
|
|
327
|
+
let offsetStart = start - coveringEntry.start;
|
|
328
|
+
let offsetEnd = offsetStart + (end - start);
|
|
329
|
+
let result = coveringEntry.data.slice(offsetStart, offsetEnd);
|
|
330
|
+
|
|
331
|
+
// Update statistics at the end when we know the final result
|
|
332
|
+
if (config?.stats) {
|
|
333
|
+
if (didRead) {
|
|
334
|
+
config.stats.uncachedReads++;
|
|
335
|
+
config.stats.uncachedReadSize += result.length;
|
|
336
|
+
} else {
|
|
337
|
+
config.stats.cachedReads++;
|
|
338
|
+
config.stats.cachedReadSize += result.length;
|
|
339
|
+
}
|
|
340
|
+
config.stats.totalCacheSize = totalSize;
|
|
341
|
+
config.stats.totalCacheCount = lruArray.length;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return result;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return createArchivesOverride(archives, {
|
|
348
|
+
get: cachedGet
|
|
349
|
+
});
|
|
350
|
+
}
|
|
@@ -75,6 +75,28 @@ class ArchivesPrivateFileSystem {
|
|
|
75
75
|
await writable.close();
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
@measureFnc
|
|
79
|
+
public async append(fileName: string, data: Buffer): Promise<void> {
|
|
80
|
+
this.log(blue(`Appending to file ${fileName} += ${data.length} bytes`));
|
|
81
|
+
|
|
82
|
+
await this.ensureDirsExist(fileName);
|
|
83
|
+
|
|
84
|
+
const pathParts = fileName.split("/");
|
|
85
|
+
const filename = pathParts[pathParts.length - 1];
|
|
86
|
+
const dirPath = pathParts.length > 1 ? pathParts.slice(0, -1).join("/") : "";
|
|
87
|
+
|
|
88
|
+
const directory = await this.getOrCreateDirectory(dirPath);
|
|
89
|
+
const fileHandle = await directory.getFileHandle(filename, { create: true });
|
|
90
|
+
|
|
91
|
+
// Get current file to find the size for appending at the end
|
|
92
|
+
const file = await fileHandle.getFile();
|
|
93
|
+
const currentSize = file.size;
|
|
94
|
+
|
|
95
|
+
const writable = await fileHandle.createWritable({ keepExistingData: true });
|
|
96
|
+
await writable.write({ type: "write", position: currentSize, data });
|
|
97
|
+
await writable.close();
|
|
98
|
+
}
|
|
99
|
+
|
|
78
100
|
@measureFnc
|
|
79
101
|
public async del(fileName: string): Promise<void> {
|
|
80
102
|
this.log(blue(`Deleting file ${fileName}`));
|
|
@@ -19,6 +19,9 @@ import { hackDevtoolsWebsocketForward } from "./oneTimeForward";
|
|
|
19
19
|
import { getOwnMachineId, decodeNodeId, decodeNodeIdAssert } from "../-a-auth/certs";
|
|
20
20
|
import { sort } from "socket-function/src/misc";
|
|
21
21
|
import { getPathStr2 } from "../path";
|
|
22
|
+
setImmediate(() => {
|
|
23
|
+
import("../diagnostics/MachineThreadInfo");
|
|
24
|
+
});
|
|
22
25
|
|
|
23
26
|
let loadTime = Date.now();
|
|
24
27
|
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { SocketFunction } from "socket-function/SocketFunction";
|
|
2
2
|
import { requiresNetworkTrustHook } from "../-d-trust/NetworkTrust2";
|
|
3
|
-
import { isDevDebugbreak } from "../config";
|
|
3
|
+
import { isDevDebugbreak, isDiskAudit } from "../config";
|
|
4
4
|
import { measureWrap } from "socket-function/src/profiling/measure";
|
|
5
5
|
import { QueueLimited } from "socket-function/src/misc";
|
|
6
6
|
import { isNode } from "typesafecss";
|
|
7
|
+
import { logDisk } from "../diagnostics/logs/diskLogger";
|
|
7
8
|
|
|
8
9
|
export type DebugLog = {
|
|
9
10
|
type: string;
|
|
@@ -56,6 +57,9 @@ function debugLogBase(type: string, values: { [key: string]: unknown }) {
|
|
|
56
57
|
disableAuditLogging();
|
|
57
58
|
return;
|
|
58
59
|
}
|
|
60
|
+
if (isDiskAudit()) {
|
|
61
|
+
logDisk("log", type, values, { "diskAudit": true });
|
|
62
|
+
}
|
|
59
63
|
let newEntry: DebugLog = { type, time: Date.now(), values };
|
|
60
64
|
logHistory.push(newEntry);
|
|
61
65
|
};
|
|
@@ -17,7 +17,6 @@ import { binarySearchIndex, isNode, isNodeTrue, last, promiseObj, sort, timeInHo
|
|
|
17
17
|
import { isNodeTrusted, isTrusted, isTrustedByNode } from "../-d-trust/NetworkTrust2";
|
|
18
18
|
import { AuthorityPath, LOCAL_DOMAIN, LOCAL_DOMAIN_PATH, pathValueAuthority2 } from "./NodePathAuthorities";
|
|
19
19
|
import { pathValueSerializer } from "../-h-path-value-serialize/PathValueSerializer";
|
|
20
|
-
import { isNoNetwork } from "../config";
|
|
21
20
|
import { formatTime } from "socket-function/src/formatting/format";
|
|
22
21
|
import { getOwnNodeId, isOwnNodeId } from "../-f-node-discovery/NodeDiscovery";
|
|
23
22
|
import { getNodeIdDomain, getNodeIdDomainMaybeUndefined, getNodeIdIP } from "socket-function/src/nodeCache";
|
|
@@ -406,12 +405,6 @@ export function getCompressNetwork() {
|
|
|
406
405
|
return getCompressNetworkBase();
|
|
407
406
|
}
|
|
408
407
|
|
|
409
|
-
let getCompressDiskBase = () => false;
|
|
410
|
-
export const registerGetCompressDisk = (fnc: () => boolean) => { getCompressDiskBase = fnc; };
|
|
411
|
-
export function getCompressDisk() {
|
|
412
|
-
return getCompressDiskBase();
|
|
413
|
-
}
|
|
414
|
-
|
|
415
408
|
|
|
416
409
|
const filterChildPathsBase = measureWrap(
|
|
417
410
|
function filterChildPathsBase(parentPath: string, packedSuffix: string, paths: Set<string>): Set<string> {
|
|
@@ -1260,6 +1253,13 @@ class PathWatcher {
|
|
|
1260
1253
|
for (let path of newParentsWatched) {
|
|
1261
1254
|
auditLog("new local WATCH PARENT", { path });
|
|
1262
1255
|
}
|
|
1256
|
+
} else {
|
|
1257
|
+
for (let path of newPathsWatched) {
|
|
1258
|
+
auditLog("new non-local WATCH", { path, watcher: config.callback });
|
|
1259
|
+
}
|
|
1260
|
+
for (let path of newParentsWatched) {
|
|
1261
|
+
auditLog("new non-local WATCH PARENT", { path, watcher: config.callback });
|
|
1262
|
+
}
|
|
1263
1263
|
}
|
|
1264
1264
|
logDisk("log", `New PathValue watches`, {
|
|
1265
1265
|
newPathsWatched: newPathsWatched.size,
|
package/src/4-dom/qreact.tsx
CHANGED
|
@@ -1223,6 +1223,7 @@ class QRenderClass {
|
|
|
1223
1223
|
// break our render function.
|
|
1224
1224
|
void Promise.resolve().finally(() => {
|
|
1225
1225
|
logErrors(proxyWatcher.commitFunction({
|
|
1226
|
+
debugName: getDebugName("ref"),
|
|
1226
1227
|
canWrite: true,
|
|
1227
1228
|
baseFunction: ref,
|
|
1228
1229
|
watchFunction() {
|
|
@@ -19,7 +19,7 @@ import { cache, cacheLimited, lazy } from "socket-function/src/caching";
|
|
|
19
19
|
import { getOwnMachineId, getOwnThreadId, getThreadKeyCert, verifyMachineIdForPublicKey } from "../-a-auth/certs";
|
|
20
20
|
import { getHostedIP, getSNICerts, publishMachineARecords } from "../-e-certs/EdgeCertController";
|
|
21
21
|
import { LOCAL_DOMAIN, nodePathAuthority } from "../0-path-value-core/NodePathAuthorities";
|
|
22
|
-
import { debugCoreMode, registerGetCompressNetwork, encodeParentFilter,
|
|
22
|
+
import { debugCoreMode, registerGetCompressNetwork, encodeParentFilter, authorityStorage } from "../0-path-value-core/pathValueCore";
|
|
23
23
|
import { clientWatcher, ClientWatcher } from "../1-path-client/pathValueClientWatcher";
|
|
24
24
|
import { SyncWatcher, proxyWatcher, specialObjectWriteValue, isSynced, PathValueProxyWatcher, atomic, doAtomicWrites, noAtomicSchema, undeleteFromLookup, registerSchemaPrefix, WatcherOptions, doProxyOptions } from "../2-proxy/PathValueProxyWatcher";
|
|
25
25
|
import { isInProxyDatabase, rawSchema } from "../2-proxy/pathDatabaseProxyBase";
|
|
@@ -202,9 +202,6 @@ export class Querysub {
|
|
|
202
202
|
public static MAX_FUTURE_CALL_TIME = 10_000;
|
|
203
203
|
|
|
204
204
|
|
|
205
|
-
/** Compression makes serialization about 2X slower, but reduces the size by about 2X (more or less if your
|
|
206
|
-
* data size is dominated by value size instead of key size). */
|
|
207
|
-
public static COMPRESS_DISK = false;
|
|
208
205
|
public static COMPRESS_NETWORK = true;
|
|
209
206
|
|
|
210
207
|
public static now = getSyncedTime;
|
|
@@ -1297,7 +1294,6 @@ setImmediate(async () => {
|
|
|
1297
1294
|
});
|
|
1298
1295
|
|
|
1299
1296
|
registerGetCompressNetwork(() => Querysub.COMPRESS_NETWORK);
|
|
1300
|
-
registerGetCompressDisk(() => Querysub.COMPRESS_DISK);
|
|
1301
1297
|
|
|
1302
1298
|
(globalThis as any).Querysub = Querysub;
|
|
1303
1299
|
|
package/src/config.ts
CHANGED
|
@@ -22,6 +22,7 @@ let yargObj = parseArgsFactory()
|
|
|
22
22
|
// TODO: The bootstrapper is a single file. Maybe we shouldn't run the entire service just for that. Although... maybe it's fine, as services are light?
|
|
23
23
|
.option("bootstraponly", { type: "boolean", desc: "Don't register as an edge node, so we serve the bootstrap files, but we don't need up to date code because we are not used for endpoints or the UI." })
|
|
24
24
|
.option("notifyemails", { type: "array", desc: "The emails to notify when errors occur." })
|
|
25
|
+
.option("diskaudit", { type: "boolean", desc: "Track all audit logs to disk. This might end up writing A LOT of data." })
|
|
25
26
|
.argv
|
|
26
27
|
;
|
|
27
28
|
type QuerysubConfig = {
|
|
@@ -74,6 +75,10 @@ export function isRecovery() {
|
|
|
74
75
|
return yargObj.recovery;
|
|
75
76
|
}
|
|
76
77
|
|
|
78
|
+
export function isDiskAudit() {
|
|
79
|
+
return !!yargObj.diskaudit;
|
|
80
|
+
}
|
|
81
|
+
|
|
77
82
|
export function devDebugbreak() {
|
|
78
83
|
if (!isNode()) {
|
|
79
84
|
debugger;
|