querysub 0.394.0 → 0.395.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 (30) hide show
  1. package/.cursorrules +8 -0
  2. package/package.json +1 -1
  3. package/src/-a-archives/archivesJSONT.ts +71 -8
  4. package/src/0-path-value-core/pathValueCore.ts +20 -1
  5. package/src/5-diagnostics/GenericFormat.tsx +1 -1
  6. package/src/deployManager/components/MachinesListPage.tsx +1 -2
  7. package/src/diagnostics/logs/IndexedLogs/BufferIndex.ts +12 -3
  8. package/src/diagnostics/logs/IndexedLogs/BufferIndexHelpers.ts +8 -3
  9. package/src/diagnostics/logs/IndexedLogs/BufferUnitIndex.ts +24 -9
  10. package/src/diagnostics/logs/IndexedLogs/BufferUnitSet.ts +0 -1
  11. package/src/diagnostics/logs/IndexedLogs/FindProgressTracker.ts +21 -5
  12. package/src/diagnostics/logs/IndexedLogs/IndexedLogs.ts +10 -4
  13. package/src/diagnostics/logs/IndexedLogs/LogViewer3.tsx +95 -124
  14. package/src/diagnostics/logs/IndexedLogs/RenderSearchStats.tsx +127 -0
  15. package/src/diagnostics/logs/IndexedLogs/bufferSearchFindMatcher.ts +3 -0
  16. package/src/diagnostics/logs/IndexedLogs/moveIndexLogsToPublic.ts +1 -1
  17. package/src/diagnostics/logs/TimeRangeSelector.tsx +11 -2
  18. package/src/diagnostics/logs/errorNotifications2/ErrorNotificationPage.tsx +1 -4
  19. package/src/diagnostics/logs/errorNotifications2/errorNotifications.ts +1 -1
  20. package/src/diagnostics/logs/lifeCycleAnalysis/LifeCyclePage.tsx +946 -0
  21. package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycleMatching.ts +49 -0
  22. package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycleSearch.tsx +553 -0
  23. package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycles.tsx +125 -90
  24. package/src/diagnostics/managementPages.tsx +17 -1
  25. package/src/functional/{limitProcessing.ts → throttleProcessing.ts} +1 -1
  26. package/src/library-components/StartEllipsis.tsx +13 -0
  27. package/src/misc.ts +4 -0
  28. package/src/diagnostics/logs/lifeCycleAnalysis/test.wat +0 -106
  29. package/src/diagnostics/logs/lifeCycleAnalysis/test.wat.d.ts +0 -2
  30. package/src/diagnostics/logs/lifeCycleAnalysis/testHoist.ts +0 -5
package/.cursorrules CHANGED
@@ -8,6 +8,8 @@ Use double quotes.
8
8
 
9
9
  When running a command in the current project, don't "cd" to it. It's redundant, and won't work.
10
10
 
11
+ Use the tool calls in order to modify files. Don't try to run terminal commands to modify files.
12
+
11
13
  Don't use redundant comments. If it's a single line and the function name says the same thing that the comment is going to say, you don't need the comment.
12
14
 
13
15
  When you're inline passing a lambda as an argument, never specify the type. The type should always be inferred. Specifying the type is bad, and it's hard coding, and we don't like hard coding.
@@ -32,10 +34,16 @@ Follow the rule of minimum scoping. If something can be a local variable, it sho
32
34
 
33
35
  Try not to use "null", and instead always use "undefined".
34
36
 
37
+ Use square brackets for arrays, not a generic.
38
+ USE: T[]
39
+ NEVER: Array<T>
40
+
35
41
  Never try to add dynamic pluralization in the UI, just use an s. If you add dynamic pluralization, if the code ever gets localized, all of your changes have to be undone, and you just made localization much harder.
36
42
 
37
43
  Never use the ternary operator. Instead, do this: "x ? y : z" => "x && y || z".
38
44
 
45
+ Never use <h2>, <h3>, etc, they have bad styling.
46
+
39
47
  If are inside an async Event Handlers, you need to use... Querysub.onCommitFinished(() => ...). and put the async code inside the callback. Then when you set state, you need to put the state setting code inside of Querysub.commit(() => ...).
40
48
 
41
49
  The site automatically builds and hotreloads. Just save the files. DO NOT try to run any build scripts, because it automatically builds on save. DO NOT RUN "npm run build". DO NOT TRY TO RUN UNIT TESTS. DO NOT TRY TO RUN TESTS.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "querysub",
3
- "version": "0.394.0",
3
+ "version": "0.395.0",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "note1": "note on node-forge fork, see https://github.com/digitalbazaar/forge/issues/744 for details",
@@ -13,6 +13,13 @@ export type ArchiveT<T> = {
13
13
 
14
14
  export function archiveJSONT<T>(archives: () => Archives): ArchiveT<T> {
15
15
  archives = lazy(archives);
16
+
17
+ let valuesCache = new Map<string, {
18
+ createTime: number;
19
+ size: number;
20
+ value: Buffer;
21
+ }>();
22
+
16
23
  async function get(key: string) {
17
24
  let buffer = await archives().get(key);
18
25
  if (!buffer) return undefined;
@@ -28,15 +35,71 @@ export function archiveJSONT<T>(archives: () => Archives): ArchiveT<T> {
28
35
  return (await archives().find("")).map(value => value.toString());
29
36
  }
30
37
  async function values() {
31
- let keysArray = await keys();
32
- let results: T[] = [];
33
- await Promise.all(keysArray.map(async key => {
34
- let value = await get(key);
35
- if (value) {
36
- results.push(value);
38
+ let infos = await archives().findInfo("");
39
+
40
+ let needsUpdate = false;
41
+ let currentKeys = new Set(infos.map(info => info.path));
42
+ let cachedKeys = new Set(valuesCache.keys());
43
+
44
+ if (currentKeys.size !== cachedKeys.size) {
45
+ needsUpdate = true;
46
+ } else {
47
+ for (let info of infos) {
48
+ let cached = valuesCache.get(info.path);
49
+ if (!cached || cached.createTime !== info.createTime || cached.size !== info.size) {
50
+ needsUpdate = true;
51
+ break;
52
+ }
37
53
  }
38
- }));
39
- return results;
54
+ }
55
+
56
+ if (needsUpdate) {
57
+ let maxRetries = 10;
58
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
59
+ infos = await archives().findInfo("");
60
+
61
+ valuesCache.clear();
62
+ let allFound = true;
63
+
64
+ await Promise.all(infos.map(async info => {
65
+ let buffer = await archives().get(info.path);
66
+ if (!buffer) {
67
+ allFound = false;
68
+ } else {
69
+ valuesCache.set(info.path, {
70
+ createTime: info.createTime,
71
+ size: info.size,
72
+ value: buffer
73
+ });
74
+ }
75
+ }));
76
+
77
+ if (!allFound) continue;
78
+
79
+ let newInfos = await archives().findInfo("");
80
+ if (newInfos.length !== infos.length) continue;
81
+ function anyChanged() {
82
+ for (let i = 0; i < newInfos.length; i++) {
83
+ if (
84
+ newInfos[i].path !== infos[i].path ||
85
+ newInfos[i].createTime !== infos[i].createTime ||
86
+ newInfos[i].size !== infos[i].size
87
+ ) {
88
+ return true;
89
+ }
90
+ }
91
+ }
92
+ if (!anyChanged()) break;
93
+ }
94
+
95
+ if (needsUpdate && valuesCache.size === 0) {
96
+ throw new Error(`Failed to get consistent snapshot of values after ${maxRetries} attempts`);
97
+ }
98
+ }
99
+
100
+ return Array.from(valuesCache.values()).map(cached =>
101
+ JSON.parse(cached.value.toString()) as T
102
+ );
40
103
  }
41
104
  async function entries(): Promise<[string, T][]> {
42
105
  let keysArray = await keys();
@@ -29,6 +29,7 @@ import { PromiseObj } from "../promise";
29
29
  import { ClientWatcher } from "../1-path-client/pathValueClientWatcher";
30
30
  import { auditLog, isDebugLogEnabled } from "./auditLogs";
31
31
  import { logDisk } from "../diagnostics/logs/diskLogger";
32
+ import { isDiskAudit } from "../config";
32
33
 
33
34
 
34
35
  let yargObj = isNodeTrue() && yargs(process.argv)
@@ -1341,6 +1342,12 @@ class PathWatcher {
1341
1342
  if (!obj) continue;
1342
1343
  obj.watchers.delete(callback);
1343
1344
 
1345
+ if (isOwnNodeId(callback)) {
1346
+ auditLog("local UNWATCH PARENT", { path });
1347
+ } else {
1348
+ auditLog("non-local UNWATCH PARENT", { path, watcher: callback });
1349
+ }
1350
+
1344
1351
  if (obj.watchers.size === 0) {
1345
1352
  watchersObj.delete(path);
1346
1353
  this.parentWatchers.delete(path);
@@ -1372,6 +1379,13 @@ class PathWatcher {
1372
1379
  let watchers = this.watchers.get(path);
1373
1380
  if (!watchers) continue;
1374
1381
  watchers.watchers.delete(callback);
1382
+
1383
+ if (isOwnNodeId(callback)) {
1384
+ auditLog("local UNWATCH", { path });
1385
+ } else {
1386
+ auditLog("non-local UNWATCH", { path, watcher: callback });
1387
+ }
1388
+
1375
1389
  if (watchers.watchers.size === 0) {
1376
1390
  this.watchers.delete(path);
1377
1391
 
@@ -1555,6 +1569,11 @@ class PathWatcher {
1555
1569
  callback(changes, parentPaths ?? []);
1556
1570
  }
1557
1571
  } else {
1572
+ if (isDiskAudit()) {
1573
+ for (let change of changes) {
1574
+ auditLog("non-local TRIGGER", { path: change.path, time: change.time.time, watcher });
1575
+ }
1576
+ }
1558
1577
  if (!isCoreQuiet) {
1559
1578
  console.log(`(${Date.now()}) Sending values to client: ${changes.length} (${watcher})`);
1560
1579
  }
@@ -1570,7 +1589,7 @@ class PathWatcher {
1570
1589
  if (isDebugLogEnabled()) {
1571
1590
  for (let pathValue of changes) {
1572
1591
 
1573
- auditLog("SEND VALUE", { path: pathValue.path, time: pathValue.time.time, nodeId: debugNodeId(watcher), transparent: pathValue.isTransparent, canGC: pathValue.canGCValue });
1592
+ auditLog("SEND VALUE", { path: pathValue.path, time: pathValue.time.time, watcher, nodeId: debugNodeId(watcher), transparent: pathValue.isTransparent, canGC: pathValue.canGCValue });
1574
1593
  }
1575
1594
  }
1576
1595
  await PathValueController.nodes[watcher].forwardWrites(
@@ -44,7 +44,7 @@ let formatters: { [formatter in StringFormatters]: (value: unknown) => preact.Co
44
44
  number: (value) => d(value, formatNumber(Number(value))),
45
45
  percent: (value) => d(value, formatPercent(Number(value))),
46
46
  timeSpan: (value) => d(value, formatTime(Number(value))),
47
- date: (value) => d(value, <span title={formatDateTimeDetailed(Number(value))}>{formatVeryNiceDateTime(Number(value))}</span>),
47
+ date: (value) => d(value, <span title={formatDateTimeDetailed(Number(value))}>{formatDateTime(Number(value))}</span>),
48
48
  error: (value) => d(value, <span class={errorMessage}>{String(value)}</span>),
49
49
  toSpaceCase: (value) => d(value, toSpaceCase(String(value))),
50
50
  "<Selector>": (value) => d(value, <Selector {...JSON.parse(String(value).slice("<Selector>".length))} />),
@@ -92,11 +92,10 @@ export class MachinesListPage extends qreact.Component {
92
92
  this.state.isDeleteMode = false;
93
93
  this.state.isDeleting = false;
94
94
  });
95
- } catch (error) {
95
+ } finally {
96
96
  Querysub.commit(() => {
97
97
  this.state.isDeleting = false;
98
98
  });
99
- alert(`Failed to delete machines: ${error instanceof Error ? error.message : String(error)}`);
100
99
  }
101
100
  });
102
101
  }}>
@@ -334,8 +334,13 @@ export class BufferIndex {
334
334
  results.totalBlockCount += indexEntries.length;
335
335
  results.localBlockCount += indexEntries.length;
336
336
 
337
- // Iterate newest-first so the caller gets the most recent matches first.
338
- for (let i = indexEntries.length - 1; i >= 0; i--) {
337
+ // Iterate based on search direction
338
+ const iterateForward = !!params.searchFromStart;
339
+ const startIdx = iterateForward ? 0 : indexEntries.length - 1;
340
+ const endIdx = iterateForward ? indexEntries.length : -1;
341
+ const step = iterateForward ? 1 : -1;
342
+
343
+ for (let i = startIdx; iterateForward ? i < endIdx : i > endIdx; i += step) {
339
344
  if (matchCount >= params.limit || !config.keepIterating()) break;
340
345
  await config.results.limitGroup?.wait();
341
346
  const blockIndex = i;
@@ -380,7 +385,11 @@ export class BufferIndex {
380
385
  results.localBlockCheckedCount++;
381
386
 
382
387
  // Scan all buffers in this block
383
- for (let bufferIndex = buffers.length - 1; bufferIndex >= 0; bufferIndex--) {
388
+ const bufferStartIdx = iterateForward ? 0 : buffers.length - 1;
389
+ const bufferEndIdx = iterateForward ? buffers.length : -1;
390
+ const bufferStep = iterateForward ? 1 : -1;
391
+
392
+ for (let bufferIndex = bufferStartIdx; iterateForward ? bufferIndex < bufferEndIdx : bufferIndex > bufferEndIdx; bufferIndex += bufferStep) {
384
393
  if (matchCount >= params.limit || !config.keepIterating()) break;
385
394
  await config.results.limitGroup?.wait();
386
395
 
@@ -4,7 +4,7 @@ import { formatNumber, formatPercent } from "socket-function/src/formatting/form
4
4
  import { red } from "socket-function/src/formatting/logColors";
5
5
  import { MaybePromise } from "socket-function/src/types";
6
6
  import { TimeFilePathWithSize } from "./IndexedLogs";
7
- import { LimitGroup } from "../../../functional/limitProcessing";
7
+ import { ThrottleGroup } from "../../../functional/throttleProcessing";
8
8
 
9
9
  export const INDEX_EXTENSION = ".index";
10
10
 
@@ -17,6 +17,7 @@ export type SearchParams = {
17
17
  pathOverrides?: TimeFilePathWithSize[];
18
18
  only?: "local" | "public";
19
19
  forceReadProduction?: boolean;
20
+ searchFromStart?: boolean;
20
21
  };
21
22
 
22
23
  export type Unit = number;
@@ -130,6 +131,8 @@ export type IndexedLogResults = {
130
131
  totalBackblazeFiles: number;
131
132
  localFilesSearched: number;
132
133
  backblazeFilesSearched: number;
134
+ totalBackblazeLogs: number;
135
+ backblazeLogsSearched: number;
133
136
 
134
137
  totalBlockCount: number;
135
138
  blockCheckedCount: number;
@@ -158,11 +161,11 @@ export type IndexedLogResults = {
158
161
  totalSearchTime: number;
159
162
 
160
163
  cancel?: boolean;
161
- limitGroup?: LimitGroup;
164
+ limitGroup?: ThrottleGroup;
162
165
  };
163
166
  export function createEmptyIndexedLogResults(): IndexedLogResults {
164
167
  return {
165
- matchCount: 0, reads: [], totalLocalFiles: 0, totalBackblazeFiles: 0, localFilesSearched: 0, backblazeFilesSearched: 0, totalBlockCount: 0, blockCheckedCount: 0, remoteBlockCount: 0, localBlockCount: 0, remoteBlockCheckedCount: 0, localBlockCheckedCount: 0, blocksCheckedCompressedSize: 0, blocksCheckedDecompressedSize: 0, blockErrors: [], fileErrors: [], remoteIndexesSearched: 0, remoteIndexSize: 0, localIndexesSearched: 0, localIndexSize: 0, timeToFirstMatch: -1, fileFindTime: 0, indexSearchTime: 0, blockSearchTime: 0, totalSearchTime: 0, cancel: undefined, limitGroup: undefined,
168
+ matchCount: 0, reads: [], totalLocalFiles: 0, totalBackblazeFiles: 0, localFilesSearched: 0, backblazeFilesSearched: 0, totalBackblazeLogs: 0, backblazeLogsSearched: 0, totalBlockCount: 0, blockCheckedCount: 0, remoteBlockCount: 0, localBlockCount: 0, remoteBlockCheckedCount: 0, localBlockCheckedCount: 0, blocksCheckedCompressedSize: 0, blocksCheckedDecompressedSize: 0, blockErrors: [], fileErrors: [], remoteIndexesSearched: 0, remoteIndexSize: 0, localIndexesSearched: 0, localIndexSize: 0, timeToFirstMatch: -1, fileFindTime: 0, indexSearchTime: 0, blockSearchTime: 0, totalSearchTime: 0, cancel: undefined, limitGroup: undefined,
166
169
  };
167
170
  }
168
171
 
@@ -202,6 +205,8 @@ export function mergeIndexedLogResults(existing: IndexedLogResults, incoming: In
202
205
  reads: Array.from(readsByKey.values()),
203
206
  localFilesSearched: existing.localFilesSearched + incoming.localFilesSearched,
204
207
  backblazeFilesSearched: existing.backblazeFilesSearched + incoming.backblazeFilesSearched,
208
+ totalBackblazeLogs: existing.totalBackblazeLogs + incoming.totalBackblazeLogs,
209
+ backblazeLogsSearched: existing.backblazeLogsSearched + incoming.backblazeLogsSearched,
205
210
  totalBlockCount: existing.totalBlockCount + incoming.totalBlockCount,
206
211
  blockCheckedCount: existing.blockCheckedCount + incoming.blockCheckedCount,
207
212
  blocksCheckedCompressedSize: existing.blocksCheckedCompressedSize + incoming.blocksCheckedCompressedSize,
@@ -381,7 +381,7 @@ export class BufferUnitIndex {
381
381
  }
382
382
  }, `buildHashTableWithLinkedLists`);
383
383
 
384
- console.log(`Hash table filled slots: ${filledSlots} / ${hashTableCapacity} (${formatPercent(filledSlots / hashTableCapacity)} utilization)`);
384
+ console.log(`Hash table filled slots: ${filledSlots} / ${hashTableCapacity} (${formatPercent(filledSlots / hashTableCapacity)} utilization, higher means hash function is good, and no probing is used, so 100% is fine)`);
385
385
 
386
386
  // Step 5: Flatten linked lists and encode to binary
387
387
  // Build data section and hash table by traversing linked lists
@@ -532,13 +532,19 @@ export class BufferUnitIndex {
532
532
 
533
533
  const searchBlock = async (blockIndex: number) => {
534
534
  if (!candidateBlocksSet.has(blockIndex)) return;
535
- // This is kind of a weird thing. Basically, because we search in parallel, we might search out of order. So we can only look at the counts before or at us, as if we match a whole bunch after us, but we should still keep going as our matches are going to take precedence.
535
+ // Check if we should stop iterating based on match counts and direction
536
536
  let stopIterating = () => {
537
- let countBefore = 0;
538
- for (let i = 0; i <= blockIndex; i++) {
539
- countBefore += matchCounts[i];
537
+ let relevantCount = 0;
538
+ if (params.searchFromStart) {
539
+ for (let i = 0; i <= blockIndex; i++) {
540
+ relevantCount += matchCounts[i];
541
+ }
542
+ } else {
543
+ for (let i = blockIndex; i < blockCount; i++) {
544
+ relevantCount += matchCounts[i];
545
+ }
540
546
  }
541
- return countBefore >= params.limit || !keepIterating();
547
+ return relevantCount >= params.limit || !keepIterating();
542
548
  };
543
549
  if (stopIterating()) return;
544
550
 
@@ -561,7 +567,12 @@ export class BufferUnitIndex {
561
567
  let bufferCount = await this.getBufferCountFromBlock(blockReader);
562
568
 
563
569
  // Check each buffer for a match
564
- for (let i = 0; i < bufferCount; i++) {
570
+ const iterateForward = !!params.searchFromStart;
571
+ const startIdx = iterateForward ? 0 : bufferCount - 1;
572
+ const endIdx = iterateForward ? bufferCount : -1;
573
+ const step = iterateForward ? 1 : -1;
574
+
575
+ for (let i = startIdx; iterateForward ? i < endIdx : i > endIdx; i += step) {
565
576
  if (stopIterating()) break;
566
577
  await results.limitGroup?.wait();
567
578
 
@@ -582,9 +593,13 @@ export class BufferUnitIndex {
582
593
  { parallelCount: BufferUnitIndexParallelSearchCount },
583
594
  searchBlock
584
595
  );
585
- // Search first first, as moveLogsToPublic should have made it so this is the newest.
596
+ // Sort blocks by search direction
586
597
  let searchOrder = Array.from(candidateBlocksSet);
587
- sort(searchOrder, x => x);
598
+ if (params.searchFromStart) {
599
+ sort(searchOrder, x => x);
600
+ } else {
601
+ sort(searchOrder, x => -x);
602
+ }
588
603
  await Promise.all(searchOrder.map(runSearchBlock));
589
604
 
590
605
  }, `searchBlocks`);
@@ -112,7 +112,6 @@ export class UnitSet {
112
112
  return Buffer.from(output.buffer);
113
113
  }
114
114
 
115
- @measureFnc
116
115
  static has(data: Buffer, unit: Unit): boolean {
117
116
  const hashTableCount = data.readUInt32LE(0);
118
117
 
@@ -19,8 +19,14 @@ export class FindProgressTracker<T> {
19
19
  if (time < this.config.params.startTime) return false;
20
20
  if (time >= this.config.params.endTime) return false;
21
21
 
22
- if (this.results.length >= this.config.params.limit && time < this.results[0].time) {
23
- return false;
22
+ if (this.config.params.searchFromStart) {
23
+ if (this.results.length >= this.config.params.limit && time > this.results[this.results.length - 1].time) {
24
+ return false;
25
+ }
26
+ } else {
27
+ if (this.results.length >= this.config.params.limit && time < this.results[0].time) {
28
+ return false;
29
+ }
24
30
  }
25
31
 
26
32
  let newObj = { time, result };
@@ -29,7 +35,11 @@ export class FindProgressTracker<T> {
29
35
  this.results.splice(index, 0, newObj);
30
36
 
31
37
  if (this.results.length > this.config.params.limit) {
32
- this.results = this.results.slice(-this.config.params.limit);
38
+ if (this.config.params.searchFromStart) {
39
+ this.results = this.results.slice(0, this.config.params.limit);
40
+ } else {
41
+ this.results = this.results.slice(-this.config.params.limit);
42
+ }
33
43
  }
34
44
 
35
45
  this.config.onResult(result);
@@ -37,8 +47,14 @@ export class FindProgressTracker<T> {
37
47
  }
38
48
 
39
49
  public isSourceRelevant(source: { startTime: number; endTime: number; }): boolean {
40
- if (this.results.length >= this.config.params.limit && source.endTime < this.results[0].time) {
41
- return false;
50
+ if (this.config.params.searchFromStart) {
51
+ if (this.results.length >= this.config.params.limit && source.startTime > this.results[this.results.length - 1].time) {
52
+ return false;
53
+ }
54
+ } else {
55
+ if (this.results.length >= this.config.params.limit && source.endTime < this.results[0].time) {
56
+ return false;
57
+ }
42
58
  }
43
59
  return source.startTime <= this.config.params.endTime && source.endTime >= this.config.params.startTime;
44
60
  }
@@ -22,7 +22,7 @@ import { SocketFunction } from "socket-function/SocketFunction";
22
22
  import { assertIsManagementUser } from "../../managementPages";
23
23
  import { ignoreErrors } from "../../../errors";
24
24
  import { blue } from "socket-function/src/formatting/logColors";
25
- import { LimitGroup } from "../../../functional/limitProcessing";
25
+ import { ThrottleGroup } from "../../../functional/throttleProcessing";
26
26
  import { getAllNodeIds } from "../../../-f-node-discovery/NodeDiscovery";
27
27
  import { NodeCapabilitiesController } from "../../../-g-core-values/NodeCapabilities";
28
28
  import { getLoggers2Async } from "../diskLogger";
@@ -427,7 +427,7 @@ export class IndexedLogs<T> {
427
427
  allResults.set("", results);
428
428
 
429
429
 
430
- results.limitGroup = new LimitGroup({
430
+ results.limitGroup = new ThrottleGroup({
431
431
  maxTimePerBeforeWait: 500,
432
432
  waitTime: 250,
433
433
  });
@@ -518,14 +518,19 @@ export class IndexedLogs<T> {
518
518
  }
519
519
  fileFindTime = Date.now() - fileFindTime;
520
520
 
521
- // Newest first
522
- sort(paths, x => - x.startTime);
521
+ // Sort by time based on search direction
522
+ if (config.params.searchFromStart) {
523
+ sort(paths, x => x.startTime);
524
+ } else {
525
+ sort(paths, x => - x.startTime);
526
+ }
523
527
 
524
528
  let results = config.results;
525
529
  let localPaths = paths.filter(x => x.logCount === undefined);
526
530
  let remotePaths = paths.filter(x => x.logCount !== undefined);
527
531
  results.totalLocalFiles = localPaths.length;
528
532
  results.totalBackblazeFiles = remotePaths.length;
533
+ results.totalBackblazeLogs = remotePaths.reduce((sum, x) => sum + (x.logCount ?? 0), 0);
529
534
  results.fileFindTime = fileFindTime;
530
535
 
531
536
  let progressTracker = new FindProgressTracker<T>({
@@ -551,6 +556,7 @@ export class IndexedLogs<T> {
551
556
  let archives = remote ? backblazeLogs : localLogs;
552
557
  if (remote) {
553
558
  results.backblazeFilesSearched++;
559
+ results.backblazeLogsSearched += path.logCount ?? 0;
554
560
  } else {
555
561
  results.localFilesSearched++;
556
562
  }