querysub 0.357.0 → 0.358.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 +1 -0
- package/package.json +2 -1
- package/src/-a-archives/archivesDisk.ts +13 -6
- package/src/-a-archives/archivesMemoryCache.ts +41 -17
- package/src/deployManager/components/MachineDetailPage.tsx +43 -2
- package/src/deployManager/components/MachinesListPage.tsx +10 -2
- package/src/deployManager/machineApplyMainCode.ts +3 -3
- package/src/deployManager/machineSchema.ts +39 -0
- package/src/diagnostics/NodeViewer.tsx +2 -1
- package/src/diagnostics/logs/IndexedLogs/BufferIndex.ts +124 -123
- package/src/diagnostics/logs/IndexedLogs/BufferIndexHelpers.ts +83 -1
- package/src/diagnostics/logs/IndexedLogs/BufferListStreamer.ts +2 -0
- package/src/diagnostics/logs/IndexedLogs/BufferUnitIndex.ts +21 -24
- package/src/diagnostics/logs/IndexedLogs/BufferUnitSet.ts +1 -1
- package/src/diagnostics/logs/IndexedLogs/FilePathSelector.tsx +186 -25
- package/src/diagnostics/logs/IndexedLogs/IndexedLogs.ts +231 -144
- package/src/diagnostics/logs/IndexedLogs/LogViewer3.tsx +307 -108
- package/src/diagnostics/logs/IndexedLogs/TimeFileTree.ts +1 -1
- package/src/diagnostics/logs/IndexedLogs/moveIndexLogsToPublic.ts +37 -7
- package/src/diagnostics/logs/errorNotifications2/errorNotifications2.ts +0 -0
- package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycles.tsx +51 -33
- package/src/diagnostics/logs/lifeCycleAnalysis/test.ts +0 -180
- package/src/functional/limitProcessing.ts +39 -0
|
@@ -2,8 +2,8 @@ import { lazy } from "socket-function/src/caching";
|
|
|
2
2
|
import { Archives, nestArchives } from "../../../-a-archives/archives";
|
|
3
3
|
import { deepCloneJSON, keyByArray, nextId, sort, timeInHour, timeInMinute, timeInSecond } from "socket-function/src/misc";
|
|
4
4
|
import { BufferIndex } from "./BufferIndex";
|
|
5
|
-
import { delay, runInSerial, runInfinitePoll } from "socket-function/src/batching";
|
|
6
|
-
import { Reader, SearchParams } from "./BufferIndexHelpers";
|
|
5
|
+
import { delay, runInParallel, runInSerial, runInfinitePoll } from "socket-function/src/batching";
|
|
6
|
+
import { IndexedLogResults, Reader, SearchParams, addReadToResults, createEmptyIndexedLogResults, INDEX_EXTENSION } from "./BufferIndexHelpers";
|
|
7
7
|
import { getDomain, isPublic } from "../../../config";
|
|
8
8
|
import { getArchivesLocal } from "../../../-a-archives/archivesDisk";
|
|
9
9
|
import { getArchivesBackblaze, getArchivesBackblazePrivateImmutable } from "../../../-a-archives/archivesBackBlaze";
|
|
@@ -21,49 +21,16 @@ import { FindProgressTracker } from "./FindProgressTracker";
|
|
|
21
21
|
import { SocketFunction } from "socket-function/SocketFunction";
|
|
22
22
|
import { assertIsManagementUser } from "../../managementPages";
|
|
23
23
|
import { ignoreErrors } from "../../../errors";
|
|
24
|
+
import { blue } from "socket-function/src/formatting/logColors";
|
|
25
|
+
import { LimitGroup } from "../../../functional/limitProcessing";
|
|
24
26
|
|
|
25
27
|
export type TimeFilePathWithSize = TimeFilePath & {
|
|
26
28
|
size: number;
|
|
27
|
-
sourceName: string;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export type IndexedLogResults = {
|
|
31
|
-
matchCount: number;
|
|
32
|
-
|
|
33
|
-
// NOTE: A lot of the metadata won't be accurate if multiple searches happen at the same time. However, for debugging, it should be sufficient.
|
|
34
|
-
reads: {
|
|
35
|
-
cached: boolean;
|
|
36
|
-
remote: boolean;
|
|
37
|
-
count: number;
|
|
38
|
-
size: number;
|
|
39
|
-
|
|
40
|
-
totalSize: number;
|
|
41
|
-
totalCount: number;
|
|
42
|
-
}[];
|
|
43
|
-
|
|
44
|
-
localFilesSearched: number;
|
|
45
|
-
backblazeFilesSearched: number;
|
|
46
|
-
|
|
47
|
-
totalBlockCount: number;
|
|
48
|
-
blockCheckedCount: number;
|
|
49
|
-
blocksCheckedCompressedSize: number;
|
|
50
|
-
blocksCheckedDecompressedSize: number;
|
|
51
|
-
blockErrors: string[];
|
|
52
|
-
|
|
53
|
-
fileErrors: string[];
|
|
54
|
-
|
|
55
|
-
indexesSearched: number;
|
|
56
29
|
indexSize: number;
|
|
57
|
-
|
|
58
|
-
timeToFirstMatch: number;
|
|
59
|
-
fileFindTime: number;
|
|
60
|
-
indexSearchTime: number;
|
|
61
|
-
blockSearchTime: number;
|
|
62
|
-
|
|
63
|
-
totalSearchTime: number;
|
|
30
|
+
sourceName: string;
|
|
64
31
|
};
|
|
65
32
|
|
|
66
|
-
let
|
|
33
|
+
let loggerByName = new Map<string, IndexedLogs<unknown>>();
|
|
67
34
|
|
|
68
35
|
|
|
69
36
|
export class IndexedLogs<T> {
|
|
@@ -74,22 +41,24 @@ export class IndexedLogs<T> {
|
|
|
74
41
|
forceUsePublicLogs?: boolean;
|
|
75
42
|
getTime: (result: T) => number | undefined;
|
|
76
43
|
}) {
|
|
77
|
-
|
|
44
|
+
loggerByName.set(this.config.name, this as any);
|
|
78
45
|
}
|
|
79
46
|
|
|
80
47
|
|
|
81
48
|
private static shouldRunLoop = false;
|
|
82
49
|
public static runLogMoveLoop() {
|
|
83
50
|
IndexedLogs.shouldRunLoop = true;
|
|
84
|
-
for (let indexedLogs of
|
|
51
|
+
for (let indexedLogs of loggerByName.values()) {
|
|
85
52
|
indexedLogs.runLogMoverLoop();
|
|
86
53
|
}
|
|
87
54
|
}
|
|
88
55
|
|
|
89
56
|
private findCallbacks = new Map<string, (match: T) => void>();
|
|
57
|
+
private resultsCallbacks = new Map<string, (results: IndexedLogResults) => void>();
|
|
90
58
|
public async clientFind(config: {
|
|
91
59
|
params: SearchParams;
|
|
92
60
|
onResult: (match: T) => void;
|
|
61
|
+
onResults?: (results: IndexedLogResults) => void;
|
|
93
62
|
}): Promise<IndexedLogResults> {
|
|
94
63
|
let controller = IndexedLogShimController.nodes[SocketFunction.getBrowserNodeId()];
|
|
95
64
|
let findId = nextId();
|
|
@@ -97,6 +66,9 @@ export class IndexedLogs<T> {
|
|
|
97
66
|
config.onResult(match);
|
|
98
67
|
};
|
|
99
68
|
this.findCallbacks.set(findId, callback);
|
|
69
|
+
if (config.onResults) {
|
|
70
|
+
this.resultsCallbacks.set(findId, config.onResults);
|
|
71
|
+
}
|
|
100
72
|
try {
|
|
101
73
|
return await controller.find({
|
|
102
74
|
findId,
|
|
@@ -107,9 +79,14 @@ export class IndexedLogs<T> {
|
|
|
107
79
|
// There's some trailing time after the controller call finishes when the results will be trickling back to us.
|
|
108
80
|
setTimeout(() => {
|
|
109
81
|
this.findCallbacks.delete(findId);
|
|
82
|
+
this.resultsCallbacks.delete(findId);
|
|
110
83
|
}, timeInMinute * 30);
|
|
111
84
|
}
|
|
112
85
|
}
|
|
86
|
+
public clientCancelAllCallbacks() {
|
|
87
|
+
this.findCallbacks.clear();
|
|
88
|
+
this.resultsCallbacks.clear();
|
|
89
|
+
}
|
|
113
90
|
public async clientGetPaths(config: {
|
|
114
91
|
startTime: number;
|
|
115
92
|
endTime: number;
|
|
@@ -128,12 +105,24 @@ export class IndexedLogs<T> {
|
|
|
128
105
|
result: unknown;
|
|
129
106
|
}) {
|
|
130
107
|
let callback = this.findCallbacks.get(config.findId);
|
|
131
|
-
if (!callback)
|
|
108
|
+
if (!callback) throw new Error(`Find callback ${config.findId} not found`);
|
|
132
109
|
callback(config.result as T);
|
|
133
110
|
}
|
|
111
|
+
public async onResults(config: {
|
|
112
|
+
findId: string;
|
|
113
|
+
results: IndexedLogResults;
|
|
114
|
+
}): Promise<boolean> {
|
|
115
|
+
let callback = this.resultsCallbacks.get(config.findId);
|
|
116
|
+
if (!callback) throw new Error(`Results callback ${config.findId} not found`);
|
|
117
|
+
callback(config.results);
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
134
120
|
|
|
135
121
|
public async clientForceMoveLogsToPublic() {
|
|
136
|
-
|
|
122
|
+
let controller = IndexedLogShimController.nodes[SocketFunction.getBrowserNodeId()];
|
|
123
|
+
await controller.forceMoveLogsToPublic({
|
|
124
|
+
indexedLogsName: this.config.name,
|
|
125
|
+
});
|
|
137
126
|
}
|
|
138
127
|
|
|
139
128
|
private localLogsStats = {
|
|
@@ -153,23 +142,29 @@ export class IndexedLogs<T> {
|
|
|
153
142
|
totalCacheCount: 0,
|
|
154
143
|
};
|
|
155
144
|
|
|
145
|
+
private fileSizeCache = new Map<string, number>();
|
|
146
|
+
|
|
156
147
|
private getLocalLogs = lazy((): Archives => {
|
|
157
148
|
let baseDisk = getArchivesLocal(getDomain());
|
|
158
149
|
let archives = nestArchives("indexed-logs/" + this.config.name, baseDisk);
|
|
159
150
|
archives = createArchivesMemoryCache(archives, {
|
|
160
151
|
maxSize: 1024 * 1024 * 512,
|
|
161
152
|
maxCount: 1000 * 100,
|
|
162
|
-
stats: this.localLogsStats
|
|
153
|
+
stats: this.localLogsStats,
|
|
154
|
+
sizeCache: this.fileSizeCache,
|
|
155
|
+
// Local disk reads are fast, but even local reads can have high latency
|
|
156
|
+
extraReadSize: 1024 * 1024 * 1,
|
|
163
157
|
});
|
|
164
158
|
return archives;
|
|
165
159
|
});
|
|
166
160
|
private getPublicLogs = lazy((): Archives => {
|
|
167
161
|
let basePublic: Archives = getArchivesLocal(getDomain());
|
|
168
|
-
|
|
162
|
+
// NOTE: The local disk is so fast that reading in 10 megabytes is nothing, And if we read in too small of a value, the overhead per read ends up making this take forever.
|
|
163
|
+
let extraReadSize = 1024 * 1024 * 10;
|
|
169
164
|
if (this.config.forceUsePublicLogs || isPublic()) {
|
|
170
165
|
basePublic = getArchivesBackblaze(getDomain());
|
|
171
|
-
//
|
|
172
|
-
extraReadSize = 1024 * 1024 *
|
|
166
|
+
// NOTE: While the latency to back plays is high, now we're reading in parallel, so it shouldn't be as big of an issue.
|
|
167
|
+
extraReadSize = 1024 * 1024 * 1;
|
|
173
168
|
}
|
|
174
169
|
let archives = nestArchives("final-indexed-logs/" + this.config.name, basePublic);
|
|
175
170
|
archives = createArchivesMemoryCache(archives, {
|
|
@@ -178,6 +173,7 @@ export class IndexedLogs<T> {
|
|
|
178
173
|
fullyImmutable: true,
|
|
179
174
|
extraReadSize,
|
|
180
175
|
stats: this.backblazeLogsStats,
|
|
176
|
+
sizeCache: this.fileSizeCache,
|
|
181
177
|
});
|
|
182
178
|
return archives;
|
|
183
179
|
});
|
|
@@ -198,20 +194,19 @@ export class IndexedLogs<T> {
|
|
|
198
194
|
private forceFlushStream: (() => Promise<void>) | undefined;
|
|
199
195
|
|
|
200
196
|
private getCurrentLogStream(): LogStreamer<T> {
|
|
197
|
+
|
|
201
198
|
if (!this.currentLogStream) {
|
|
202
199
|
let { startTime, endTime } = this.getTimeBlock(Date.now());
|
|
203
200
|
let path = new TimeFileTree(this.getLocalLogs()).getNewPendingPath({ startTime, endTime });
|
|
204
|
-
let indexPath = this.getIndexPath(path);
|
|
205
201
|
let streamer = BufferIndex.createStreamer();
|
|
206
202
|
let currentLogStreamSize = 0;
|
|
207
203
|
let currentLogStreamCount = 0;
|
|
208
204
|
let newStreamer = async () => {
|
|
209
205
|
let result = streamer.close();
|
|
210
206
|
await this.getLocalLogs().append(path, result.data);
|
|
211
|
-
await this.getLocalLogs().append(
|
|
207
|
+
await this.getLocalLogs().append(path + INDEX_EXTENSION, result.index);
|
|
212
208
|
|
|
213
209
|
path = new TimeFileTree(this.getLocalLogs()).getNewPendingPath({ startTime, endTime });
|
|
214
|
-
indexPath = this.getIndexPath(path);
|
|
215
210
|
streamer = BufferIndex.createStreamer();
|
|
216
211
|
currentLogStreamSize = 0;
|
|
217
212
|
currentLogStreamCount = 0;
|
|
@@ -224,6 +219,14 @@ export class IndexedLogs<T> {
|
|
|
224
219
|
|
|
225
220
|
};
|
|
226
221
|
let writeBuffers = async (buffers: Buffer[]) => {
|
|
222
|
+
if (Date.now() > endTime) {
|
|
223
|
+
let timeBlockObj = this.getTimeBlock(Date.now());
|
|
224
|
+
startTime = timeBlockObj.startTime;
|
|
225
|
+
endTime = timeBlockObj.endTime;
|
|
226
|
+
path = new TimeFileTree(this.getLocalLogs()).getNewPendingPath(timeBlockObj);
|
|
227
|
+
await newStreamer();
|
|
228
|
+
}
|
|
229
|
+
|
|
227
230
|
let maxSize = this.config.maxSingleFileData || MAX_SINGLE_FILE_DATA;
|
|
228
231
|
let maxCount = this.config.maxCountPerFile || MAX_COUNT_PER_FILE;
|
|
229
232
|
|
|
@@ -239,7 +242,7 @@ export class IndexedLogs<T> {
|
|
|
239
242
|
let result = streamer.add(group);
|
|
240
243
|
await this.getLocalLogs().append(path, result.data);
|
|
241
244
|
if (result.index) {
|
|
242
|
-
await this.getLocalLogs().append(
|
|
245
|
+
await this.getLocalLogs().append(path + INDEX_EXTENSION, result.index);
|
|
243
246
|
}
|
|
244
247
|
|
|
245
248
|
currentLogStreamSize += result.data.length;
|
|
@@ -284,10 +287,6 @@ export class IndexedLogs<T> {
|
|
|
284
287
|
return groups;
|
|
285
288
|
}
|
|
286
289
|
|
|
287
|
-
private getIndexPath(dataPath: string): string {
|
|
288
|
-
return dataPath + ".index";
|
|
289
|
-
}
|
|
290
|
-
|
|
291
290
|
public append(datum: T) {
|
|
292
291
|
this.getCurrentLogStream().append(datum);
|
|
293
292
|
if (IndexedLogs.shouldRunLoop) {
|
|
@@ -295,6 +294,7 @@ export class IndexedLogs<T> {
|
|
|
295
294
|
}
|
|
296
295
|
}
|
|
297
296
|
|
|
297
|
+
@measureFnc
|
|
298
298
|
public async getPaths(config: {
|
|
299
299
|
startTime: number;
|
|
300
300
|
endTime: number;
|
|
@@ -326,16 +326,98 @@ export class IndexedLogs<T> {
|
|
|
326
326
|
let archives = path.logCount !== undefined ? backblazeLogs : localLogs;
|
|
327
327
|
let info = await archives.getInfo(path.fullPath);
|
|
328
328
|
let size = info?.size || 0;
|
|
329
|
-
|
|
329
|
+
this.fileSizeCache.set(path.fullPath, size);
|
|
330
|
+
let indexPath = path.fullPath + INDEX_EXTENSION;
|
|
331
|
+
let indexInfo = await archives.getInfo(indexPath);
|
|
332
|
+
let indexSize = indexInfo?.size || 0;
|
|
333
|
+
this.fileSizeCache.set(indexPath, indexSize);
|
|
334
|
+
return { ...path, size, indexSize, sourceName: this.config.name };
|
|
330
335
|
}));
|
|
331
336
|
|
|
332
337
|
return pathsWithSize;
|
|
333
338
|
}
|
|
334
339
|
|
|
340
|
+
@measureFnc
|
|
335
341
|
public async find(config: {
|
|
336
342
|
params: SearchParams;
|
|
337
343
|
onResult: (match: T) => void;
|
|
344
|
+
onResults?: (results: IndexedLogResults) => Promise<boolean>;
|
|
338
345
|
}): Promise<IndexedLogResults> {
|
|
346
|
+
let startTime = Date.now();
|
|
347
|
+
let interval: NodeJS.Timeout | undefined;
|
|
348
|
+
|
|
349
|
+
let results: IndexedLogResults = createEmptyIndexedLogResults();
|
|
350
|
+
results.limitGroup = new LimitGroup({
|
|
351
|
+
maxTimePerBeforeWait: 500,
|
|
352
|
+
waitTime: 250,
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
let initialLocalStats = deepCloneJSON(this.localLogsStats);
|
|
356
|
+
let initialBackblazeStats = deepCloneJSON(this.backblazeLogsStats);
|
|
357
|
+
|
|
358
|
+
let updateResultsStats = () => {
|
|
359
|
+
results.reads = [];
|
|
360
|
+
|
|
361
|
+
function addStatsRead(initial: ArchivesMemoryCacheStats, final: ArchivesMemoryCacheStats, remote: boolean) {
|
|
362
|
+
let uncached = addReadToResults(results, {
|
|
363
|
+
cached: false,
|
|
364
|
+
remote,
|
|
365
|
+
count: final.uncachedReads - initial.uncachedReads,
|
|
366
|
+
size: final.uncachedReadSize - initial.uncachedReadSize,
|
|
367
|
+
});
|
|
368
|
+
uncached.totalSize = final.totalCacheSize;
|
|
369
|
+
uncached.totalCount = final.totalCacheCount;
|
|
370
|
+
|
|
371
|
+
let cached = addReadToResults(results, {
|
|
372
|
+
cached: true,
|
|
373
|
+
remote,
|
|
374
|
+
count: final.cachedReads - initial.cachedReads,
|
|
375
|
+
size: final.cachedReadSize - initial.cachedReadSize,
|
|
376
|
+
});
|
|
377
|
+
cached.totalSize = final.totalCacheSize;
|
|
378
|
+
cached.totalCount = final.totalCacheCount;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
addStatsRead(initialLocalStats, this.localLogsStats, false);
|
|
382
|
+
addStatsRead(initialBackblazeStats, this.backblazeLogsStats, true);
|
|
383
|
+
|
|
384
|
+
results.totalSearchTime = Date.now() - startTime;
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
const onResultsCallback = config.onResults;
|
|
388
|
+
if (onResultsCallback) {
|
|
389
|
+
interval = setInterval(async () => {
|
|
390
|
+
updateResultsStats();
|
|
391
|
+
let shouldContinue = await onResultsCallback(deepCloneJSON(results));
|
|
392
|
+
if (!shouldContinue) {
|
|
393
|
+
if (!results.cancel) {
|
|
394
|
+
console.log(blue(`Cancelled search on ${this.config.name}`));
|
|
395
|
+
}
|
|
396
|
+
results.cancel = true;
|
|
397
|
+
}
|
|
398
|
+
}, timeInSecond);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
try {
|
|
402
|
+
await this.findBase({
|
|
403
|
+
params: config.params,
|
|
404
|
+
onResult: config.onResult,
|
|
405
|
+
results,
|
|
406
|
+
});
|
|
407
|
+
updateResultsStats();
|
|
408
|
+
return results;
|
|
409
|
+
} finally {
|
|
410
|
+
if (interval) {
|
|
411
|
+
clearInterval(interval);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
private async findBase(config: {
|
|
417
|
+
params: SearchParams;
|
|
418
|
+
onResult: (match: T) => void;
|
|
419
|
+
results: IndexedLogResults;
|
|
420
|
+
}): Promise<void> {
|
|
339
421
|
let startTime = Date.now();
|
|
340
422
|
let localLogs = this.getLocalLogs();
|
|
341
423
|
let backblazeLogs = this.getPublicLogs();
|
|
@@ -347,54 +429,56 @@ export class IndexedLogs<T> {
|
|
|
347
429
|
only: config.params.only,
|
|
348
430
|
});
|
|
349
431
|
paths = paths.filter(x => x.sourceName === this.config.name);
|
|
432
|
+
for (let path of paths) {
|
|
433
|
+
this.fileSizeCache.set(path.fullPath, path.size);
|
|
434
|
+
this.fileSizeCache.set(path.fullPath + INDEX_EXTENSION, path.indexSize);
|
|
435
|
+
}
|
|
350
436
|
fileFindTime = Date.now() - fileFindTime;
|
|
351
437
|
|
|
352
438
|
// Newest first
|
|
353
439
|
sort(paths, x => - x.startTime);
|
|
354
440
|
|
|
441
|
+
let results = config.results;
|
|
355
442
|
let localPaths = paths.filter(x => x.logCount === undefined);
|
|
356
|
-
let
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
let stats = {
|
|
361
|
-
blockCheckedCount: 0,
|
|
362
|
-
blocksCheckedCompressedSize: 0,
|
|
363
|
-
blocksCheckedDecompressedSize: 0,
|
|
364
|
-
totalBlockCount: 0,
|
|
365
|
-
indexSearchTime: 0,
|
|
366
|
-
blockSearchTime: 0,
|
|
367
|
-
indexesSearched: 0,
|
|
368
|
-
indexSize: 0,
|
|
369
|
-
matchCount: 0,
|
|
370
|
-
timeToFirstMatch: -1,
|
|
371
|
-
};
|
|
372
|
-
let fileErrors: string[] = [];
|
|
373
|
-
let blockErrors: string[] = [];
|
|
443
|
+
let remotePaths = paths.filter(x => x.logCount !== undefined);
|
|
444
|
+
results.totalLocalFiles = localPaths.length;
|
|
445
|
+
results.totalBackblazeFiles = remotePaths.length;
|
|
446
|
+
results.fileFindTime = fileFindTime;
|
|
374
447
|
|
|
375
448
|
let progressTracker = new FindProgressTracker<T>({
|
|
376
449
|
params: config.params,
|
|
377
450
|
deserialize: (buffer: Buffer) => LogStreamer.deserialize<T>(buffer),
|
|
378
451
|
getTime: this.config.getTime,
|
|
379
452
|
onResult: (match: T) => {
|
|
380
|
-
if (
|
|
381
|
-
|
|
453
|
+
if (results.timeToFirstMatch < 0) {
|
|
454
|
+
results.timeToFirstMatch = Date.now() - startTime;
|
|
382
455
|
}
|
|
383
|
-
|
|
456
|
+
results.matchCount++;
|
|
384
457
|
config.onResult(match);
|
|
385
458
|
},
|
|
386
459
|
});
|
|
387
460
|
|
|
388
461
|
|
|
389
|
-
|
|
390
|
-
if (!progressTracker.isSourceRelevant(path))
|
|
462
|
+
const searchPath = async (path: TimeFilePathWithSize) => {
|
|
463
|
+
if (!progressTracker.isSourceRelevant(path)) return;
|
|
464
|
+
// Wait, so we don't lock up the main thread?
|
|
465
|
+
await delay(0);
|
|
391
466
|
|
|
392
|
-
let
|
|
467
|
+
let remote = path.logCount !== undefined;
|
|
468
|
+
let archives = remote ? backblazeLogs : localLogs;
|
|
469
|
+
if (remote) {
|
|
470
|
+
results.backblazeFilesSearched++;
|
|
471
|
+
} else {
|
|
472
|
+
results.localFilesSearched++;
|
|
473
|
+
}
|
|
393
474
|
try {
|
|
394
475
|
let readIndexTime = Date.now();
|
|
395
|
-
let index = await archives.get(
|
|
476
|
+
let index = await archives.get(
|
|
477
|
+
path.fullPath + INDEX_EXTENSION,
|
|
478
|
+
{ range: { start: 0, end: path.indexSize } }
|
|
479
|
+
) || Buffer.alloc(0);
|
|
396
480
|
readIndexTime = Date.now() - readIndexTime;
|
|
397
|
-
|
|
481
|
+
results.indexSearchTime += readIndexTime;
|
|
398
482
|
let dataReader: Reader = {
|
|
399
483
|
getLength: async () => path.size,
|
|
400
484
|
read: async (offset, length) => {
|
|
@@ -406,76 +490,44 @@ export class IndexedLogs<T> {
|
|
|
406
490
|
},
|
|
407
491
|
};
|
|
408
492
|
let totalSearchTime = Date.now();
|
|
409
|
-
let
|
|
493
|
+
let blockSearchTimeBefore = results.blockSearchTime;
|
|
494
|
+
await BufferIndex.find({
|
|
410
495
|
index,
|
|
411
496
|
dataReader,
|
|
412
497
|
params: {
|
|
413
498
|
...config.params,
|
|
414
|
-
limit: config.params.limit -
|
|
499
|
+
limit: config.params.limit - results.matchCount,
|
|
415
500
|
},
|
|
416
|
-
keepIterating: () => progressTracker.isSourceRelevant(path),
|
|
501
|
+
keepIterating: () => !results.cancel && progressTracker.isSourceRelevant(path),
|
|
417
502
|
onResult: (match: Buffer) => {
|
|
418
|
-
if (
|
|
503
|
+
if (results.matchCount >= config.params.limit) return;
|
|
419
504
|
progressTracker.addResult(match, path);
|
|
420
505
|
},
|
|
506
|
+
results,
|
|
421
507
|
});
|
|
422
|
-
blockErrors.push(...findObj.blocksWithErrors);
|
|
423
508
|
totalSearchTime = Date.now() - totalSearchTime;
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
stats.indexSize += findObj.indexSize;
|
|
428
|
-
stats.blockCheckedCount += findObj.blocksChecked;
|
|
429
|
-
stats.blocksCheckedCompressedSize += findObj.blocksCheckedCompressedSize;
|
|
430
|
-
stats.blocksCheckedDecompressedSize += findObj.blocksCheckedDecompressedSize;
|
|
431
|
-
stats.totalBlockCount += findObj.totalBlockCount;
|
|
432
|
-
|
|
433
|
-
if (stats.matchCount >= config.params.limit) break;
|
|
509
|
+
let blockSearchTimeAdded = results.blockSearchTime - blockSearchTimeBefore;
|
|
510
|
+
results.indexSearchTime += totalSearchTime - blockSearchTimeAdded;
|
|
511
|
+
|
|
434
512
|
} catch (e: any) {
|
|
435
|
-
fileErrors.push(String(e?.stack || e));
|
|
513
|
+
results.fileErrors.push({ error: String(e?.stack || e), path: path.fullPath });
|
|
436
514
|
console.warn(`Error in reading log file ${path.fullPath} logs: ${e}`);
|
|
437
515
|
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
if (stats.timeToFirstMatch < 0) {
|
|
441
|
-
stats.timeToFirstMatch = Date.now() - startTime;
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
let reads: { cached: boolean; remote: boolean; count: number; size: number; totalSize: number; totalCount: number; }[] = [];
|
|
446
|
-
function addStatsRead(initial: ArchivesMemoryCacheStats, final: ArchivesMemoryCacheStats, remote: boolean) {
|
|
447
|
-
reads.push({
|
|
448
|
-
cached: false,
|
|
449
|
-
remote,
|
|
450
|
-
count: final.uncachedReads - initial.uncachedReads,
|
|
451
|
-
size: final.uncachedReadSize - initial.uncachedReadSize,
|
|
452
|
-
totalSize: final.totalCacheSize,
|
|
453
|
-
totalCount: final.totalCacheCount,
|
|
454
|
-
});
|
|
455
|
-
reads.push({
|
|
456
|
-
cached: true,
|
|
457
|
-
remote,
|
|
458
|
-
count: final.cachedReads - initial.cachedReads,
|
|
459
|
-
size: final.cachedReadSize - initial.cachedReadSize,
|
|
460
|
-
totalSize: final.totalCacheSize,
|
|
461
|
-
totalCount: final.totalCacheCount,
|
|
462
|
-
});
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
addStatsRead(initialLocalStats, this.localLogsStats, false);
|
|
466
|
-
addStatsRead(initialBackblazeStats, this.backblazeLogsStats, true);
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
return {
|
|
470
|
-
reads,
|
|
471
|
-
localFilesSearched: localPaths.length,
|
|
472
|
-
backblazeFilesSearched: backblazePaths.length,
|
|
473
|
-
fileFindTime,
|
|
474
|
-
fileErrors,
|
|
475
|
-
blockErrors,
|
|
476
|
-
totalSearchTime: Date.now() - startTime,
|
|
477
|
-
...stats,
|
|
478
516
|
};
|
|
517
|
+
let localDone = (async () => {
|
|
518
|
+
for (let path of localPaths) {
|
|
519
|
+
await searchPath(path);
|
|
520
|
+
}
|
|
521
|
+
})();
|
|
522
|
+
// No parallel count when running locally, as running it in parallel makes timing more difficult.
|
|
523
|
+
let parallelCount = isPublic() ? 32 : 1;
|
|
524
|
+
let remoteParallel = runInParallel({ parallelCount }, searchPath);
|
|
525
|
+
await Promise.all(remotePaths.map(remoteParallel));
|
|
526
|
+
await localDone;
|
|
527
|
+
|
|
528
|
+
if (results.timeToFirstMatch < 0) {
|
|
529
|
+
results.timeToFirstMatch = Date.now() - startTime;
|
|
530
|
+
}
|
|
479
531
|
}
|
|
480
532
|
|
|
481
533
|
public async moveLogsToPublic(forceAll = false) {
|
|
@@ -490,7 +542,7 @@ export class IndexedLogs<T> {
|
|
|
490
542
|
publicMoveThreshold: PUBLIC_MOVE_THRESHOLD,
|
|
491
543
|
maxSingleFileData: this.config.maxSingleFileData || MAX_SINGLE_FILE_DATA,
|
|
492
544
|
movingTimeout: MOVING_TIMEOUT,
|
|
493
|
-
|
|
545
|
+
indexExtension: INDEX_EXTENSION,
|
|
494
546
|
});
|
|
495
547
|
}
|
|
496
548
|
|
|
@@ -529,13 +581,26 @@ class IndexedLogClient {
|
|
|
529
581
|
result: unknown;
|
|
530
582
|
}) {
|
|
531
583
|
if (isNode()) return;
|
|
532
|
-
let indexedLogs =
|
|
584
|
+
let indexedLogs = loggerByName.get(config.indexedLogsName);
|
|
533
585
|
if (!indexedLogs) throw new Error(`Indexed logs ${config.indexedLogsName} not found`);
|
|
534
586
|
indexedLogs.onFindResult({
|
|
535
587
|
findId: config.findId,
|
|
536
588
|
result: config.result,
|
|
537
589
|
});
|
|
538
590
|
}
|
|
591
|
+
public async onResults(config: {
|
|
592
|
+
findId: string;
|
|
593
|
+
indexedLogsName: string;
|
|
594
|
+
results: IndexedLogResults;
|
|
595
|
+
}): Promise<boolean> {
|
|
596
|
+
if (isNode()) return true;
|
|
597
|
+
let indexedLogs = loggerByName.get(config.indexedLogsName);
|
|
598
|
+
if (!indexedLogs) throw new Error(`Indexed logs ${config.indexedLogsName} not found`);
|
|
599
|
+
return await indexedLogs.onResults({
|
|
600
|
+
findId: config.findId,
|
|
601
|
+
results: config.results,
|
|
602
|
+
});
|
|
603
|
+
}
|
|
539
604
|
}
|
|
540
605
|
class IndexedLogShim {
|
|
541
606
|
public async find(config: {
|
|
@@ -544,8 +609,9 @@ class IndexedLogShim {
|
|
|
544
609
|
params: SearchParams;
|
|
545
610
|
}): Promise<IndexedLogResults> {
|
|
546
611
|
let caller = SocketFunction.getCaller();
|
|
547
|
-
let indexedLogs =
|
|
612
|
+
let indexedLogs = loggerByName.get(config.indexedLogsName);
|
|
548
613
|
if (!indexedLogs) throw new Error(`Indexed logs ${config.indexedLogsName} not found`);
|
|
614
|
+
|
|
549
615
|
return indexedLogs.find({
|
|
550
616
|
params: config.params,
|
|
551
617
|
onResult: (match: unknown) => {
|
|
@@ -555,6 +621,18 @@ class IndexedLogShim {
|
|
|
555
621
|
result: match,
|
|
556
622
|
}));
|
|
557
623
|
},
|
|
624
|
+
onResults: async (results: IndexedLogResults) => {
|
|
625
|
+
try {
|
|
626
|
+
await IndexedLogClientController.nodes[caller.nodeId].onResults({
|
|
627
|
+
findId: config.findId,
|
|
628
|
+
indexedLogsName: config.indexedLogsName,
|
|
629
|
+
results: results,
|
|
630
|
+
});
|
|
631
|
+
return true;
|
|
632
|
+
} catch (e) {
|
|
633
|
+
return false;
|
|
634
|
+
}
|
|
635
|
+
},
|
|
558
636
|
});
|
|
559
637
|
}
|
|
560
638
|
public async getPaths(config: {
|
|
@@ -563,7 +641,7 @@ class IndexedLogShim {
|
|
|
563
641
|
endTime: number;
|
|
564
642
|
only?: "local" | "public";
|
|
565
643
|
}): Promise<TimeFilePathWithSize[]> {
|
|
566
|
-
let indexedLogs =
|
|
644
|
+
let indexedLogs = loggerByName.get(config.indexedLogsName);
|
|
567
645
|
if (!indexedLogs) throw new Error(`Indexed logs ${config.indexedLogsName} not found`);
|
|
568
646
|
return indexedLogs.getPaths({
|
|
569
647
|
startTime: config.startTime,
|
|
@@ -571,6 +649,14 @@ class IndexedLogShim {
|
|
|
571
649
|
only: config.only,
|
|
572
650
|
});
|
|
573
651
|
}
|
|
652
|
+
|
|
653
|
+
public async forceMoveLogsToPublic(config: {
|
|
654
|
+
indexedLogsName: string;
|
|
655
|
+
}) {
|
|
656
|
+
let indexedLogs = loggerByName.get(config.indexedLogsName);
|
|
657
|
+
if (!indexedLogs) throw new Error(`Indexed logs ${config.indexedLogsName} not found`);
|
|
658
|
+
await indexedLogs.moveLogsToPublic(true);
|
|
659
|
+
}
|
|
574
660
|
}
|
|
575
661
|
|
|
576
662
|
const IndexedLogShimController = SocketFunction.register(
|
|
@@ -583,7 +669,7 @@ const IndexedLogShimController = SocketFunction.register(
|
|
|
583
669
|
getPaths: {
|
|
584
670
|
hooks: [assertIsManagementUser]
|
|
585
671
|
},
|
|
586
|
-
|
|
672
|
+
forceMoveLogsToPublic: {
|
|
587
673
|
hooks: [assertIsManagementUser]
|
|
588
674
|
}
|
|
589
675
|
})
|
|
@@ -593,6 +679,7 @@ const IndexedLogClientController = SocketFunction.register(
|
|
|
593
679
|
"IndexedLogClient-019c87b9-1c6d-72ed-8bc9-d52451e2c1b9",
|
|
594
680
|
new IndexedLogClient(),
|
|
595
681
|
() => ({
|
|
596
|
-
onFind: {}
|
|
682
|
+
onFind: {},
|
|
683
|
+
onResults: {}
|
|
597
684
|
})
|
|
598
685
|
);
|