diff --git a/src/Features/Core/Portable/SymbolSearch/Windows/SymbolSearchUpdateEngine.Update.cs b/src/Features/Core/Portable/SymbolSearch/Windows/SymbolSearchUpdateEngine.Update.cs index 56644a35aae5e..46cff0e9104ef 100644 --- a/src/Features/Core/Portable/SymbolSearch/Windows/SymbolSearchUpdateEngine.Update.cs +++ b/src/Features/Core/Portable/SymbolSearch/Windows/SymbolSearchUpdateEngine.Update.cs @@ -510,23 +510,22 @@ private async Task DownloadFileAsync(string serverPath, CancellationTo { cancellationToken.ThrowIfCancellationRequested(); - var resultOpt = await TryDownloadFileAsync(client, cancellationToken).ConfigureAwait(false); - if (resultOpt == null) + var (element, delay) = await TryDownloadFileAsync(client, cancellationToken).ConfigureAwait(false); + if (element == null) { - var delay = _service._delayService.CachePollDelay; await LogInfoAsync($"File not downloaded. Trying again in {delay}", cancellationToken).ConfigureAwait(false); await Task.Delay(delay, cancellationToken).ConfigureAwait(false); } else { // File was downloaded. - return resultOpt; + return element; } } } /// Returns 'null' if download is not available and caller should keep polling. - private async Task TryDownloadFileAsync(IFileDownloader fileDownloader, CancellationToken cancellationToken) + private async Task<(XElement element, TimeSpan delay)> TryDownloadFileAsync(IFileDownloader fileDownloader, CancellationToken cancellationToken) { await LogInfoAsync("Read file from client", cancellationToken).ConfigureAwait(false); @@ -535,7 +534,7 @@ private async Task TryDownloadFileAsync(IFileDownloader fileDownloader if (stream == null) { await LogInfoAsync("Read file completed. Client returned no data", cancellationToken).ConfigureAwait(false); - return null; + return (element: null, _service._delayService.CachePollDelay); } await LogInfoAsync("Read file completed. Client returned data", cancellationToken).ConfigureAwait(false); @@ -551,11 +550,26 @@ private async Task TryDownloadFileAsync(IFileDownloader fileDownloader XmlResolver = null }; - using var reader = XmlReader.Create(stream, settings); + // This code must always succeed. If it does not, that means that either the server reported bogus data + // to the file-downloader, or the file-downloader is serving us bogus data. In other event, there is + // something wrong with those components, and we should both report the issue to Watson, and stop doing + // the update. + try + { + using var reader = XmlReader.Create(stream, settings); - var result = XElement.Load(reader); - await LogInfoAsync("Converting data to XElement completed", cancellationToken).ConfigureAwait(false); - return result; + var element = XElement.Load(reader); + await LogInfoAsync("Converting data to XElement completed", cancellationToken).ConfigureAwait(false); + return (element, delay: default); + } + catch (Exception e) when (_service._reportAndSwallowExceptionUnlessCanceled(e, cancellationToken)) + { + // We retrieved bytes from the server, but we couldn't make parse it an xml. out of it. That's very + // bad. Just trying again one minute later isn't going to help. We need to wait until there is + // good data on the server for us to download. + await LogInfoAsync($"Unable to parse file as XElement", cancellationToken).ConfigureAwait(false); + return (element: null, _service._delayService.CatastrophicFailureDelay); + } } private async Task RepeatIOAsync(Func action, CancellationToken cancellationToken)