querysub 0.636.0 → 0.637.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "querysub",
3
- "version": "0.636.0",
3
+ "version": "0.637.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",
@@ -79,7 +79,7 @@
79
79
  "node-forge": "https://github.com/sliftist/forge#e618181b469b07bdc70b968b0391beb8ef5fecd6",
80
80
  "pako": "^2.1.0",
81
81
  "peggy": "^5.0.6",
82
- "sliftutils": "^1.7.106",
82
+ "sliftutils": "^1.7.107",
83
83
  "socket-function": "^1.2.31",
84
84
  "terser": "^5.31.0",
85
85
  "typenode": "^6.6.1",
@@ -6,7 +6,7 @@ import { Querysub } from "../../../4-querysub/Querysub";
6
6
  import { t } from "../../../2-proxy/schema2";
7
7
  import { isDefined } from "../../../misc";
8
8
  import { formatNumber, formatTime, formatDateTime } from "socket-function/src/formatting/format";
9
- import { timeInSecond } from "socket-function/src/misc";
9
+ import { timeInMinute, timeInSecond } from "socket-function/src/misc";
10
10
  import type { LogFileInfo } from "sliftutils/storage/StreamingLogs";
11
11
  import { MachineServiceController } from "../../machineSchema";
12
12
  import { getStorageServers } from "./storageServers";
@@ -163,19 +163,51 @@ export class StorageLogsPage extends qreact.Component {
163
163
  return selected;
164
164
  }
165
165
 
166
- private search() {
166
+ /** fresh drops the log file listings and the per-file search cache first, so everything searched is refetched. */
167
+ private search(fresh?: boolean) {
167
168
  if (this.state.searching) return;
168
- // State and synced reads are captured HERE, in the tracked part, before the async work
169
+ // State and synced reads are captured HERE, in the tracked part, before the async work (and before the invalidation below, which would turn the listings back into loading)
169
170
  let listings = this.getListings();
170
- let files = this.getSelectedFiles(listings).map(file => ({ url: file.url, name: file.name }));
171
+ let cachedFiles = this.getSelectedFiles(listings).map(file => ({ url: file.url, name: file.name }));
172
+ let urls = listings.map(listing => listing.url);
171
173
  let query = storageLogSearchParam.value;
172
174
  let { startTime, endTime, searchFromStart } = getTimeRange();
175
+ let synced = StorageLogsSynced(SocketFunction.browserNodeId());
176
+ if (fresh) {
177
+ invalidateStorageLogSearchCache();
178
+ synced.getServerLogFiles.resetAll();
179
+ }
173
180
  this.state.searching = true;
174
181
  this.state.error = "";
175
182
  this.state.doneFiles = 0;
176
- this.state.totalFiles = files.length;
183
+ this.state.totalFiles = fresh ? 0 : cachedFiles.length;
177
184
  this.state.currentFile = "";
185
+ // Cleared up front, so anything on screen during the search is never stale results from the previous one
186
+ this.state.rows = [];
187
+ this.state.fields = [];
188
+ this.state.failedFiles = [];
189
+ this.state.durationTime = 0;
190
+ this.state.resultsFetchTime = 0;
178
191
  Querysub.onCommitFinished(async () => {
192
+ let files = cachedFiles;
193
+ if (fresh) {
194
+ // The selection has to wait for the fresh listings; a server that fails costs only its own files (its error also shows on its listing row, which refetches the same way)
195
+ files = [];
196
+ await Promise.all(urls.map(async url => {
197
+ try {
198
+ let listing = await synced.getServerLogFiles.promise(url);
199
+ for (let file of listing.files) {
200
+ if (!fileOverlapsRange(file, startTime, endTime)) continue;
201
+ files.push({ url, name: file.name });
202
+ }
203
+ } catch (e) {
204
+ console.error(`Listing log files on ${url} failed, continuing with the other servers:`, (e as Error).stack ?? e);
205
+ }
206
+ }));
207
+ Querysub.commit(() => {
208
+ this.state.totalFiles = files.length;
209
+ });
210
+ }
179
211
  try {
180
212
  let result = await searchStorageLogs({
181
213
  files,
@@ -234,7 +266,7 @@ export class StorageLogsPage extends qreact.Component {
234
266
  let selectedBytes = selectedFiles.reduce((sum, file) => sum + file.size, 0);
235
267
  let loadedListings = listings.filter(listing => listing.fetchTime);
236
268
  let oldestListingTime = loadedListings.length && Math.min(...loadedListings.map(listing => listing.fetchTime || 0)) || 0;
237
- let now = Querysub.nowDelayed(timeInSecond);
269
+ let now = Querysub.nowDelayed(timeInMinute);
238
270
 
239
271
  // Defaults show unless explicitly toggled off, plus whatever was explicitly toggled on
240
272
  let selectedSet = new Set<string>();
@@ -328,6 +360,15 @@ export class StorageLogsPage extends qreact.Component {
328
360
  >
329
361
  {this.state.searching && `Searching ${this.state.doneFiles}/${this.state.totalFiles}...` || "Search"}
330
362
  </Button>
363
+ <Button
364
+ flavor="large"
365
+ hue={120}
366
+ title="Invalidates the log file listings and the cached search results, then searches"
367
+ onClick={() => this.search(true)}
368
+ className={this.state.searching && css.opacity(0.5) || undefined}
369
+ >
370
+ Fresh Search
371
+ </Button>
331
372
  </div>
332
373
  <div className={css.vbox(4)}>
333
374
  {listings.map(listing => {