Skip to content

Commit 64ac33e

Browse files
committed
Address comments from PR
- Rename queryStorageLocation -> queryStorageDir - Extract scrubber to its own module - Add more comments - Rename source -> cancellationSource - Ensure cancellatinSource is disposed
1 parent 7785dfe commit 64ac33e

8 files changed

Lines changed: 189 additions & 143 deletions

File tree

extensions/ql-vscode/src/contextual/locationFinder.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export interface FullLocationLink extends LocationLink {
2828
* @param dbm The database manager
2929
* @param uriString The selected source file and location
3030
* @param keyType The contextual query type to run
31+
* @param queryStorageDir The directory to store the query results
3132
* @param progress A progress callback
3233
* @param token A CancellationToken
3334
* @param filter A function that will filter extraneous results
@@ -38,7 +39,7 @@ export async function getLocationsForUriString(
3839
dbm: DatabaseManager,
3940
uriString: string,
4041
keyType: KeyType,
41-
queryStorageLocation: string,
42+
queryStorageDir: string,
4243
progress: ProgressCallback,
4344
token: CancellationToken,
4445
filter: (src: string, dest: string) => boolean
@@ -70,7 +71,7 @@ export async function getLocationsForUriString(
7071
qs,
7172
db,
7273
initialInfo,
73-
queryStorageLocation,
74+
queryStorageDir,
7475
progress,
7576
token,
7677
templates

extensions/ql-vscode/src/contextual/templateProvider.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export class TemplateQueryDefinitionProvider implements DefinitionProvider {
4242
private cli: CodeQLCliServer,
4343
private qs: QueryServerClient,
4444
private dbm: DatabaseManager,
45-
private queryStorageLocation: string,
45+
private queryStorageDir: string,
4646
) {
4747
this.cache = new CachedOperation<LocationLink[]>(this.getDefinitions.bind(this));
4848
}
@@ -70,7 +70,7 @@ export class TemplateQueryDefinitionProvider implements DefinitionProvider {
7070
this.dbm,
7171
uriString,
7272
KeyType.DefinitionQuery,
73-
this.queryStorageLocation,
73+
this.queryStorageDir,
7474
progress,
7575
token,
7676
(src, _dest) => src === uriString
@@ -86,7 +86,7 @@ export class TemplateQueryReferenceProvider implements ReferenceProvider {
8686
private cli: CodeQLCliServer,
8787
private qs: QueryServerClient,
8888
private dbm: DatabaseManager,
89-
private queryStorageLocation: string,
89+
private queryStorageDir: string,
9090
) {
9191
this.cache = new CachedOperation<FullLocationLink[]>(this.getReferences.bind(this));
9292
}
@@ -119,7 +119,7 @@ export class TemplateQueryReferenceProvider implements ReferenceProvider {
119119
this.dbm,
120120
uriString,
121121
KeyType.DefinitionQuery,
122-
this.queryStorageLocation,
122+
this.queryStorageDir,
123123
progress,
124124
token,
125125
(src, _dest) => src === uriString
@@ -140,7 +140,7 @@ export class TemplatePrintAstProvider {
140140
private cli: CodeQLCliServer,
141141
private qs: QueryServerClient,
142142
private dbm: DatabaseManager,
143-
private queryStorageLocation: string,
143+
private queryStorageDir: string,
144144
) {
145145
this.cache = new CachedOperation<QueryWithDb>(this.getAst.bind(this));
146146
}
@@ -221,7 +221,7 @@ export class TemplatePrintAstProvider {
221221
this.qs,
222222
db,
223223
initialInfo,
224-
this.queryStorageLocation,
224+
this.queryStorageDir,
225225
progress,
226226
token,
227227
templates

extensions/ql-vscode/src/extension.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -436,13 +436,13 @@ async function activateWithInstalledDistribution(
436436
ctx.subscriptions.push(queryHistoryConfigurationListener);
437437
const showResults = async (item: FullCompletedQueryInfo) =>
438438
showResultsForCompletedQuery(item, WebviewReveal.Forced);
439-
const queryStorageLocation = path.join(ctx.globalStorageUri.fsPath, 'queries');
440-
await fs.ensureDir(queryStorageLocation);
439+
const queryStorageDir = path.join(ctx.globalStorageUri.fsPath, 'queries');
440+
await fs.ensureDir(queryStorageDir);
441441

442442
const qhm = new QueryHistoryManager(
443443
qs,
444444
dbm,
445-
queryStorageLocation,
445+
queryStorageDir,
446446
ctx,
447447
queryHistoryConfigurationListener,
448448
showResults,
@@ -519,7 +519,7 @@ async function activateWithInstalledDistribution(
519519
qs,
520520
databaseItem,
521521
initialInfo,
522-
queryStorageLocation,
522+
queryStorageDir,
523523
progress,
524524
source.token,
525525
);
@@ -996,16 +996,16 @@ async function activateWithInstalledDistribution(
996996
void logger.log('Registering jump-to-definition handlers.');
997997
languages.registerDefinitionProvider(
998998
{ scheme: archiveFilesystemProvider.zipArchiveScheme },
999-
new TemplateQueryDefinitionProvider(cliServer, qs, dbm, queryStorageLocation)
999+
new TemplateQueryDefinitionProvider(cliServer, qs, dbm, queryStorageDir)
10001000
);
10011001

10021002
languages.registerReferenceProvider(
10031003
{ scheme: archiveFilesystemProvider.zipArchiveScheme },
1004-
new TemplateQueryReferenceProvider(cliServer, qs, dbm, queryStorageLocation)
1004+
new TemplateQueryReferenceProvider(cliServer, qs, dbm, queryStorageDir)
10051005
);
10061006

10071007
const astViewer = new AstViewer();
1008-
const templateProvider = new TemplatePrintAstProvider(cliServer, qs, dbm, queryStorageLocation);
1008+
const templateProvider = new TemplatePrintAstProvider(cliServer, qs, dbm, queryStorageDir);
10091009

10101010
ctx.subscriptions.push(astViewer);
10111011
ctx.subscriptions.push(commandRunnerWithProgress('codeQL.viewAst', async (
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import * as fs from 'fs-extra';
2+
import * as os from 'os';
3+
import * as path from 'path';
4+
import { Disposable, ExtensionContext } from 'vscode';
5+
import { logger } from './logging';
6+
7+
const LAST_SCRUB_TIME_KEY = 'lastScrubTime';
8+
9+
type Counter = {
10+
increment: () => void;
11+
};
12+
13+
/**
14+
* Registers an interval timer that will periodically check for queries old enought
15+
* to be deleted.
16+
*
17+
* Note that this scrubber will clean all queries from all workspaces. It should not
18+
* run too often and it should only run from one workspace at a time.
19+
*
20+
* Generally, `wakeInterval` should be significantly shorter than `throttleTime`.
21+
*
22+
* @param wakeInterval How often to check to see if the job should run.
23+
* @param throttleTime How often to actually run the job.
24+
* @param maxQueryTime The maximum age of a query before is ready for deletion.
25+
* @param queryDirectory The directory containing all queries.
26+
* @param ctx The extension context.
27+
*/
28+
export function registerQueryHistoryScubber(
29+
wakeInterval: number,
30+
throttleTime: number,
31+
maxQueryTime: number,
32+
queryDirectory: string,
33+
ctx: ExtensionContext,
34+
35+
// optional counter to keep track of how many times the scrubber has run
36+
counter?: Counter
37+
): Disposable {
38+
const deregister = setInterval(scrubber, wakeInterval, throttleTime, maxQueryTime, queryDirectory, ctx, counter);
39+
40+
return {
41+
dispose: () => {
42+
clearInterval(deregister);
43+
}
44+
};
45+
}
46+
47+
async function scrubber(
48+
throttleTime: number,
49+
maxQueryTime: number,
50+
queryDirectory: string,
51+
ctx: ExtensionContext,
52+
counter?: Counter
53+
) {
54+
const lastScrubTime = ctx.globalState.get<number>(LAST_SCRUB_TIME_KEY);
55+
const now = Date.now();
56+
57+
// If we have never scrubbed before, or if the last scrub was more than `throttleTime` ago,
58+
// then scrub again.
59+
if (lastScrubTime === undefined || now - lastScrubTime >= throttleTime) {
60+
await ctx.globalState.update(LAST_SCRUB_TIME_KEY, now);
61+
62+
let scrubCount = 0; // total number of directories deleted
63+
try {
64+
counter?.increment();
65+
void logger.log('Scrubbing query directory. Removing old queries.');
66+
if (!(await fs.pathExists(queryDirectory))) {
67+
void logger.log(`Cannot scrub. Query directory does not exist: ${queryDirectory}`);
68+
return;
69+
}
70+
71+
const baseNames = await fs.readdir(queryDirectory);
72+
const errors: string[] = [];
73+
for (const baseName of baseNames) {
74+
const dir = path.join(queryDirectory, baseName);
75+
const scrubResult = await scrubDirectory(dir, now, maxQueryTime);
76+
if (scrubResult.errorMsg) {
77+
errors.push(scrubResult.errorMsg);
78+
}
79+
if (scrubResult.deleted) {
80+
scrubCount++;
81+
}
82+
}
83+
84+
if (errors.length) {
85+
throw new Error(os.EOL + errors.join(os.EOL));
86+
}
87+
} catch (e) {
88+
void logger.log(`Error while scrubbing queries: ${e}`);
89+
} finally {
90+
void logger.log(`Scrubbed ${scrubCount} old queries.`);
91+
}
92+
}
93+
}
94+
95+
async function scrubDirectory(dir: string, now: number, maxQueryTime: number): Promise<{
96+
errorMsg?: string,
97+
deleted: boolean
98+
}> {
99+
const timestampFile = path.join(dir, 'timestamp');
100+
try {
101+
let deleted = true;
102+
if (!(await fs.stat(dir)).isDirectory()) {
103+
void logger.log(` ${dir} is not a directory. Deleting.`);
104+
await fs.remove(dir);
105+
} else if (!(await fs.pathExists(timestampFile))) {
106+
void logger.log(` ${dir} has no timestamp file. Deleting.`);
107+
await fs.remove(dir);
108+
} else if (!(await fs.stat(timestampFile)).isFile()) {
109+
void logger.log(` ${timestampFile} is not a file. Deleting.`);
110+
await fs.remove(dir);
111+
} else {
112+
const timestampText = await fs.readFile(timestampFile, 'utf8');
113+
const timestamp = parseInt(timestampText, 10);
114+
115+
if (Number.isNaN(timestamp)) {
116+
void logger.log(` ${dir} has invalid timestamp '${timestampText}'. Deleting.`);
117+
await fs.remove(dir);
118+
} else if (now - timestamp > maxQueryTime) {
119+
void logger.log(` ${dir} is older than ${maxQueryTime / 1000} seconds. Deleting.`);
120+
await fs.remove(dir);
121+
} else {
122+
void logger.log(` ${dir} is not older than ${maxQueryTime / 1000} seconds. Keeping.`);
123+
deleted = false;
124+
}
125+
}
126+
return {
127+
deleted
128+
};
129+
} catch (err) {
130+
return {
131+
errorMsg: ` Could not delete '${dir}': ${err}`,
132+
deleted: false
133+
};
134+
}
135+
}

0 commit comments

Comments
 (0)