Fix duplicate variant analysis results downloads

This adds a new file `repo_states.json` which tracks the download status
of all repositories of a variant analysis. We will write this file when
a download has completed and skip a repository download if the repo
state is marked as `succeeded`. This should prevent duplicate downloads.

This will still queue all repositories, even those which have already
been downloaded. However, I expect the actual cost in the download
method to be negligible since it's just an in-memory check.
This commit is contained in:
Koen Vlaswinkel
2022-10-31 13:01:58 +01:00
parent c36ce4867e
commit 47045f23c3

View File

@@ -29,6 +29,8 @@ import * as fs from 'fs-extra';
export class VariantAnalysisManager extends DisposableObject implements VariantAnalysisViewManager<VariantAnalysisView> {
private static readonly REPO_STATES_FILENAME = 'repo_states.json';
private readonly _onVariantAnalysisAdded = this.push(new EventEmitter<VariantAnalysis>());
public readonly onVariantAnalysisAdded = this._onVariantAnalysisAdded.event;
private readonly _onVariantAnalysisStatusUpdated = this.push(new EventEmitter<VariantAnalysis>());
@@ -43,6 +45,8 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
private static readonly maxConcurrentDownloads = 3;
private readonly queue = new PQueue({ concurrency: VariantAnalysisManager.maxConcurrentDownloads });
private readonly repoStates = new Map<number, Record<number, VariantAnalysisScannedRepositoryState>>();
constructor(
private readonly ctx: ExtensionContext,
private readonly storagePath: string,
@@ -63,6 +67,15 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
this._onVariantAnalysisRemoved.fire(variantAnalysis);
} else {
await this.setVariantAnalysis(variantAnalysis);
try {
const repoStates = await fs.readJson(this.getRepoStatesStoragePath(variantAnalysis.id));
this.repoStates.set(variantAnalysis.id, repoStates);
} catch (e) {
// Ignore this error, we simply might not have downloaded anything yet
this.repoStates.set(variantAnalysis.id, {});
}
if (status === QueryStatus.InProgress) {
// In this case, last time we checked, the query was still in progress.
// We need to setup the monitor to check for completion.
@@ -148,6 +161,8 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
await this.prepareStorageDirectory(variantAnalysis.id);
this.repoStates.set(variantAnalysis.id, {});
this._onVariantAnalysisAdded.fire(variantAnalysis);
}
@@ -162,6 +177,14 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
private async onRepoStateUpdated(variantAnalysisId: number, repoState: VariantAnalysisScannedRepositoryState): Promise<void> {
await this.getView(variantAnalysisId)?.updateRepoState(repoState);
let repoStates = this.repoStates.get(variantAnalysisId);
if (!repoStates) {
repoStates = {};
this.repoStates.set(variantAnalysisId, repoStates);
}
repoStates[repoState.repositoryId] = repoState;
}
public async monitorVariantAnalysis(
@@ -176,6 +199,10 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
variantAnalysisSummary: VariantAnalysisApiResponse,
cancellationToken: CancellationToken
): Promise<void> {
if (this.repoStates.get(variantAnalysisSummary.id)?.[scannedRepo.repository.id]?.downloadStatus === VariantAnalysisScannedRepositoryDownloadStatus.Succeeded) {
return;
}
const repoState = {
repositoryId: scannedRepo.repository.id,
downloadStatus: VariantAnalysisScannedRepositoryDownloadStatus.Pending,
@@ -215,6 +242,8 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
repoState.downloadStatus = VariantAnalysisScannedRepositoryDownloadStatus.Succeeded;
await this.onRepoStateUpdated(variantAnalysisSummary.id, repoState);
await fs.outputJson(this.getRepoStatesStoragePath(variantAnalysisSummary.id), this.repoStates.get(variantAnalysisSummary.id));
}
public async enqueueDownload(
@@ -236,6 +265,13 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
);
}
private getRepoStatesStoragePath(variantAnalysisId: number): string {
return path.join(
this.getVariantAnalysisStorageLocation(variantAnalysisId),
VariantAnalysisManager.REPO_STATES_FILENAME
);
}
/**
* Prepares a directory for storing results for a variant analysis.
* This directory contains a timestamp file, which will be