Add cache of query schemas to results view (#3862)

* Add cache of query schemas to results view

* Update changelog

* Don't call bqrsInfo with a page size of 0

* Update extensions/ql-vscode/CHANGELOG.md

Co-authored-by: Andrew Eisenberg <aeisenberg@github.com>

---------

Co-authored-by: Andrew Eisenberg <aeisenberg@github.com>
This commit is contained in:
Alexander Eyers-Taylor
2024-12-13 17:05:00 +00:00
committed by GitHub
parent 2c7021be0e
commit 07e9e44310
4 changed files with 31 additions and 2 deletions

View File

@@ -4,6 +4,7 @@
- Add a palette command that allows importing all databases directly inside of a parent folder. [#3797](https://github.com/github/vscode-codeql/pull/3797)
- Only use VS Code telemetry settings instead of using `codeQL.telemetry.enableTelemetry` [#3853](https://github.com/github/vscode-codeql/pull/3853)
- Improve the performance of the results view with large numbers of results. [#3862](https://github.com/github/vscode-codeql/pull/3862)
## 1.16.1 - 6 November 2024

View File

@@ -7,6 +7,7 @@ export class CachedOperation<S extends unknown[], U> {
private readonly operation: (t: string, ...args: S) => Promise<U>;
private readonly cached: Map<string, U>;
private readonly lru: string[];
private generation: number;
private readonly inProgressCallbacks: Map<
string,
Array<[(u: U) => void, (reason?: Error) => void]>
@@ -17,6 +18,7 @@ export class CachedOperation<S extends unknown[], U> {
private cacheSize = 100,
) {
this.operation = operation;
this.generation = 0;
this.lru = [];
this.inProgressCallbacks = new Map<
string,
@@ -46,7 +48,7 @@ export class CachedOperation<S extends unknown[], U> {
inProgressCallback.push([resolve, reject]);
});
}
const origGeneration = this.generation;
// Otherwise compute the new value, but leave a callback to allow sharing work
const callbacks: Array<[(u: U) => void, (reason?: Error) => void]> = [];
this.inProgressCallbacks.set(t, callbacks);
@@ -54,6 +56,11 @@ export class CachedOperation<S extends unknown[], U> {
const result = await this.operation(t, ...args);
callbacks.forEach((f) => f[0](result));
this.inProgressCallbacks.delete(t);
if (this.generation !== origGeneration) {
// Cache was reset in the meantime so don't trust this
// result enough to cache it.
return result;
}
if (this.lru.length > this.cacheSize) {
const toRemove = this.lru.shift()!;
this.cached.delete(toRemove);
@@ -69,4 +76,11 @@ export class CachedOperation<S extends unknown[], U> {
this.inProgressCallbacks.delete(t);
}
}
reset() {
this.cached.clear();
this.lru.length = 0;
this.generation++;
this.inProgressCallbacks.clear();
}
}

View File

@@ -75,6 +75,7 @@ import type { App } from "../common/app";
import type { Disposable } from "../common/disposable-object";
import type { RawResultSet } from "../common/raw-result-types";
import type { BqrsResultSetSchema } from "../common/bqrs-cli-types";
import { CachedOperation } from "../language-support/contextual/cached-operation";
/**
* results-view.ts
@@ -177,6 +178,8 @@ export class ResultsView extends AbstractWebview<
// Event listeners that should be disposed of when the view is disposed.
private disposableEventListeners: Disposable[] = [];
private schemaCache: CachedOperation<[], BqrsResultSetSchema[]>;
constructor(
app: App,
private databaseManager: DatabaseManager,
@@ -206,6 +209,10 @@ export class ResultsView extends AbstractWebview<
}
}),
);
this.schemaCache = new CachedOperation(
this.getResultSetSchemasImpl.bind(this),
);
}
public getCommands(): ResultsViewCommands {
@@ -420,6 +427,7 @@ export class ResultsView extends AbstractWebview<
);
return;
}
this.schemaCache.reset();
// Notify the webview that it should expect new results.
await this.postMessage({ t: "resultsUpdating" });
await this._displayedQuery.completedQuery.updateSortState(
@@ -610,6 +618,12 @@ export class ResultsView extends AbstractWebview<
selectedTable = "",
): Promise<BqrsResultSetSchema[]> {
const resultsPath = completedQuery.getResultsPath(selectedTable);
return this.schemaCache.get(resultsPath);
}
private async getResultSetSchemasImpl(
resultsPath: string,
): Promise<BqrsResultSetSchema[]> {
const schemas = await this.cliServer.bqrsInfo(
resultsPath,
PAGE_SIZE.getValue(),

View File

@@ -244,7 +244,7 @@ export class QueryEvaluationInfo extends QueryOutputDir {
*/
async chooseResultSet(cliServer: CodeQLCliServer) {
const resultSets = (
await cliServer.bqrsInfo(this.resultsPaths.resultsPath, 0)
await cliServer.bqrsInfo(this.resultsPaths.resultsPath)
)["result-sets"];
if (!resultSets.length) {
return undefined;