diff --git a/src/EditorFeatures/Core.Wpf/SymbolSearch/SymbolSearchUpdateEngine.Update.cs b/src/EditorFeatures/Core.Wpf/SymbolSearch/SymbolSearchUpdateEngine.Update.cs index f4716d462da1b6054fabadf0989a74a7d41c5354..a27ac3e6f2ad7cf9c4b8cc135b85634c2acd76e7 100644 --- a/src/EditorFeatures/Core.Wpf/SymbolSearch/SymbolSearchUpdateEngine.Update.cs +++ b/src/EditorFeatures/Core.Wpf/SymbolSearch/SymbolSearchUpdateEngine.Update.cs @@ -90,7 +90,6 @@ private class Updater private readonly SymbolSearchUpdateEngine _service; private readonly string _source; private readonly DirectoryInfo _cacheDirectoryInfo; - private readonly FileInfo _databaseFileInfo; public Updater(SymbolSearchUpdateEngine service, string source, string localSettingsDirectory) { @@ -99,9 +98,6 @@ public Updater(SymbolSearchUpdateEngine service, string source, string localSett _cacheDirectoryInfo = new DirectoryInfo(Path.Combine( localSettingsDirectory, "PackageCache", string.Format(Invariant($"Format{AddReferenceDatabase.TextFileFormatVersion}")))); - - _databaseFileInfo = new FileInfo( - Path.Combine(_cacheDirectoryInfo.FullName, ConvertToFileName(source) + ".txt")); } /// @@ -175,19 +171,25 @@ private async Task UpdateDatabaseInBackgroundWorkerAsync(CancellationT { await CleanCacheDirectoryAsync(cancellationToken).ConfigureAwait(false); - // If we have a local database, then see if it needs to be patched. - // Otherwise download the full database. + // If we have a local database, then see if it needs to be patched. Otherwise download the full + // database. // // (intentionally not wrapped in IOUtilities. If this throws we want to restart). - if (_service._ioService.Exists(_databaseFileInfo)) + // + // Ensure we get a fresh FileInfo for the db. We need to make sure that the data we're querying + // (like .Exists) is up to date and isn't the a cached value from a prior run. + var databaseFileInfo = new FileInfo( + Path.Combine(_cacheDirectoryInfo.FullName, ConvertToFileName(_source) + ".txt")); + + if (_service._ioService.Exists(databaseFileInfo)) { await _service.LogInfoAsync("Local database file exists. Patching local database").ConfigureAwait(false); - return await PatchLocalDatabaseAsync(cancellationToken).ConfigureAwait(false); + return await PatchLocalDatabaseAsync(databaseFileInfo, cancellationToken).ConfigureAwait(false); } else { await _service.LogInfoAsync("Local database file does not exist. Downloading full database").ConfigureAwait(false); - return await DownloadFullDatabaseAsync(cancellationToken).ConfigureAwait(false); + return await DownloadFullDatabaseAsync(databaseFileInfo, cancellationToken).ConfigureAwait(false); } } catch (OperationCanceledException) @@ -226,14 +228,14 @@ private async Task CleanCacheDirectoryAsync(CancellationToken cancellationToken) cancellationToken.ThrowIfCancellationRequested(); } - private async Task DownloadFullDatabaseAsync(CancellationToken cancellationToken) + private async Task DownloadFullDatabaseAsync(FileInfo databaseFileInfo, CancellationToken cancellationToken) { try { var title = string.Format(EditorFeaturesWpfResources.Downloading_IntelliSense_index_for_0, _source); await _service._progressService.OnDownloadFullDatabaseStartedAsync(title).ConfigureAwait(false); - var (succeeded, delay) = await DownloadFullDatabaseWorkerAsync(cancellationToken).ConfigureAwait(false); + var (succeeded, delay) = await DownloadFullDatabaseWorkerAsync(databaseFileInfo, cancellationToken).ConfigureAwait(false); if (succeeded) { await _service._progressService.OnDownloadFullDatabaseSucceededAsync().ConfigureAwait(false); @@ -261,20 +263,21 @@ private async Task DownloadFullDatabaseAsync(CancellationToken cancell } } - private async Task<(bool succeeded, TimeSpan delay)> DownloadFullDatabaseWorkerAsync(CancellationToken cancellationToken) + private async Task<(bool succeeded, TimeSpan delay)> DownloadFullDatabaseWorkerAsync(FileInfo databaseFileInfo, CancellationToken cancellationToken) { var serverPath = Invariant($"Elfie_V{AddReferenceDatabase.TextFileFormatVersion}/Latest.xml"); await _service.LogInfoAsync($"Downloading and processing full database: {serverPath}").ConfigureAwait(false); var element = await DownloadFileAsync(serverPath, cancellationToken).ConfigureAwait(false); - var result = await ProcessFullDatabaseXElementAsync(element, cancellationToken).ConfigureAwait(false); + var result = await ProcessFullDatabaseXElementAsync(databaseFileInfo, element, cancellationToken).ConfigureAwait(false); await _service.LogInfoAsync("Downloading and processing full database completed").ConfigureAwait(false); return result; } - private async Task<(bool succeeded, TimeSpan delay)> ProcessFullDatabaseXElementAsync(XElement element, CancellationToken cancellationToken) + private async Task<(bool succeeded, TimeSpan delay)> ProcessFullDatabaseXElementAsync( + FileInfo databaseFileInfo, XElement element, CancellationToken cancellationToken) { await _service.LogInfoAsync("Processing full database element").ConfigureAwait(false); @@ -315,14 +318,14 @@ private async Task<(bool succeeded, TimeSpan delay)> ProcessFullDatabaseXElement // Write the file out to disk so we'll have it the next time we launch VS. Do this // after we set the in-memory instance so we at least have something to search while // we're waiting to write. - await WriteDatabaseFileAsync(bytes, cancellationToken).ConfigureAwait(false); + await WriteDatabaseFileAsync(databaseFileInfo, bytes, cancellationToken).ConfigureAwait(false); var delay = _service._delayService.UpdateSucceededDelay; await _service.LogInfoAsync($"Processing full database element completed. Update again in {delay}").ConfigureAwait(false); return (succeeded: true, delay); } - private async Task WriteDatabaseFileAsync(byte[] bytes, CancellationToken cancellationToken) + private async Task WriteDatabaseFileAsync(FileInfo databaseFileInfo, byte[] bytes, CancellationToken cancellationToken) { await _service.LogInfoAsync("Writing database file").ConfigureAwait(false); @@ -349,16 +352,16 @@ private async Task WriteDatabaseFileAsync(byte[] bytes, CancellationToken cancel // If we have an existing db file, try to replace it file with the temp file. // Otherwise, just move the temp file into place. - if (_service._ioService.Exists(_databaseFileInfo)) + if (_service._ioService.Exists(databaseFileInfo)) { await _service.LogInfoAsync("Replacing database file").ConfigureAwait(false); - _service._ioService.Replace(tempFilePath, _databaseFileInfo.FullName, destinationBackupFileName: null, ignoreMetadataErrors: true); + _service._ioService.Replace(tempFilePath, databaseFileInfo.FullName, destinationBackupFileName: null, ignoreMetadataErrors: true); await _service.LogInfoAsync("Replace database file completed").ConfigureAwait(false); } else { await _service.LogInfoAsync("Moving database file").ConfigureAwait(false); - _service._ioService.Move(tempFilePath, _databaseFileInfo.FullName); + _service._ioService.Move(tempFilePath, databaseFileInfo.FullName); await _service.LogInfoAsync("Moving database file completed").ConfigureAwait(false); } } @@ -373,13 +376,13 @@ private async Task WriteDatabaseFileAsync(byte[] bytes, CancellationToken cancel await _service.LogInfoAsync("Writing database file completed").ConfigureAwait(false); } - private async Task PatchLocalDatabaseAsync(CancellationToken cancellationToken) + private async Task PatchLocalDatabaseAsync(FileInfo databaseFileInfo, CancellationToken cancellationToken) { await _service.LogInfoAsync("Patching local database").ConfigureAwait(false); await _service.LogInfoAsync("Reading in local database").ConfigureAwait(false); // (intentionally not wrapped in IOUtilities. If this throws we want to restart). - var databaseBytes = _service._ioService.ReadAllBytes(_databaseFileInfo.FullName); + var databaseBytes = _service._ioService.ReadAllBytes(databaseFileInfo.FullName); await _service.LogInfoAsync($"Reading in local database completed. databaseBytes.Length={databaseBytes.Length}").ConfigureAwait(false); // Make a database instance out of those bytes and set is as the current in memory database @@ -394,7 +397,7 @@ private async Task PatchLocalDatabaseAsync(CancellationToken cancellat catch (Exception e) when (_service._reportAndSwallowException(e)) { await _service.LogExceptionAsync(e, "Error creating database from local copy. Downloading full database").ConfigureAwait(false); - return await DownloadFullDatabaseAsync(cancellationToken).ConfigureAwait(false); + return await DownloadFullDatabaseAsync(databaseFileInfo, cancellationToken).ConfigureAwait(false); } var databaseVersion = database.DatabaseVersion; @@ -405,7 +408,7 @@ private async Task PatchLocalDatabaseAsync(CancellationToken cancellat await _service.LogInfoAsync("Downloading and processing patch file: " + serverPath).ConfigureAwait(false); var element = await DownloadFileAsync(serverPath, cancellationToken).ConfigureAwait(false); - var delayUntilUpdate = await ProcessPatchXElementAsync(element, databaseBytes, cancellationToken).ConfigureAwait(false); + var delayUntilUpdate = await ProcessPatchXElementAsync(databaseFileInfo, element, databaseBytes, cancellationToken).ConfigureAwait(false); await _service.LogInfoAsync("Downloading and processing patch file completed").ConfigureAwait(false); await _service.LogInfoAsync("Patching local database completed").ConfigureAwait(false); @@ -427,12 +430,12 @@ private async Task CreateAndSetInMemoryDatabaseAsync(byte[ } private async Task ProcessPatchXElementAsync( - XElement patchElement, byte[] databaseBytes, CancellationToken cancellationToken) + FileInfo databaseFileInfo, XElement patchElement, byte[] databaseBytes, CancellationToken cancellationToken) { try { await _service.LogInfoAsync("Processing patch element").ConfigureAwait(false); - var delayUntilUpdate = await TryProcessPatchXElementAsync(patchElement, databaseBytes, cancellationToken).ConfigureAwait(false); + var delayUntilUpdate = await TryProcessPatchXElementAsync(databaseFileInfo, patchElement, databaseBytes, cancellationToken).ConfigureAwait(false); if (delayUntilUpdate != null) { await _service.LogInfoAsync($"Processing patch element completed. Update again in {delayUntilUpdate.Value}").ConfigureAwait(false); @@ -447,11 +450,11 @@ private async Task CreateAndSetInMemoryDatabaseAsync(byte[ // Fall through and download full database. } - return await DownloadFullDatabaseAsync(cancellationToken).ConfigureAwait(false); + return await DownloadFullDatabaseAsync(databaseFileInfo, cancellationToken).ConfigureAwait(false); } private async Task TryProcessPatchXElementAsync( - XElement patchElement, byte[] databaseBytes, CancellationToken cancellationToken) + FileInfo databaseFileInfo, XElement patchElement, byte[] databaseBytes, CancellationToken cancellationToken) { ParsePatchElement(patchElement, out var upToDate, out var tooOld, out var patchBytes); @@ -477,7 +480,7 @@ private async Task CreateAndSetInMemoryDatabaseAsync(byte[ await CreateAndSetInMemoryDatabaseAsync(finalBytes).ConfigureAwait(false); - await WriteDatabaseFileAsync(finalBytes, cancellationToken).ConfigureAwait(false); + await WriteDatabaseFileAsync(databaseFileInfo, finalBytes, cancellationToken).ConfigureAwait(false); return _service._delayService.UpdateSucceededDelay; }