예제 #1
0
        public void SaveExternalCopyFailure(Uri iconUrl)
        {
            if (_externalIconCopyResults == null)
            {
                throw new InvalidOperationException("Object was not initialized");
            }

            Set(iconUrl, ExternalIconCopyResult.Fail(iconUrl, _failCacheTime));
        }
예제 #2
0
        public async Task <Uri> SaveExternalIcon(Uri originalIconUrl, Uri storageUrl, IStorage mainDestinationStorage, IStorage cacheStorage, CancellationToken cancellationToken)
        {
            if (_externalIconCopyResults == null)
            {
                throw new InvalidOperationException("Object was not initialized");
            }

            var uriSemaphore = GetUriSemaphore(originalIconUrl);

            // Attempting to copy to the same location from multiple sources at the same time will throw,
            // so we'll guard the copy attempt with semaphore.
            // We'll guard the whole operation so we wouln't even try to copy items to cache more than once.
            if (!await uriSemaphore.WaitAsync(TimeSpan.Zero, cancellationToken))
            {
                _logger.LogInformation("Failed to enter the semaphore for {IconUrl} immediately, starting to wait", originalIconUrl);
                await uriSemaphore.WaitAsync(cancellationToken);
            }
            try
            {
                if (_externalIconCopyResults.TryGetValue(originalIconUrl, out var copyResult))
                {
                    if (copyResult.IsCopySucceeded)
                    {
                        return(copyResult.StorageUrl);
                    }

                    // if we have failure stored, we'll try to replace it with success,
                    // now that we've seen one.
                }

                var cacheStoragePath = GetCachePath(originalIconUrl);
                var cacheUrl         = cacheStorage.ResolveUri(cacheStoragePath);

                _logger.LogInformation("Going to store {IconUrl} in cache from {StorageUrl} to {CacheUrl}",
                                       originalIconUrl.AbsoluteUri,
                                       storageUrl.AbsoluteUri,
                                       cacheUrl.AbsoluteUri);

                await mainDestinationStorage.CopyAsync(storageUrl, cacheStorage, cacheUrl, null, cancellationToken);

                // Technically, we could get away without storing the success in the dictionary,
                // but then each get attempt from the cache would result in HTTP request to cache
                // storage that drastically reduces usefulness of the cache (we trade one HTTP request
                // for another).
                Set(originalIconUrl, ExternalIconCopyResult.Success(originalIconUrl, cacheUrl));
                return(cacheUrl);
            }
            finally
            {
                uriSemaphore.Release();
            }
        }
        private async Task <bool> TryTakeFromCache(Uri iconUrl, ExternalIconCopyResult cachedResult, IStorage iconCacheStorage, IStorage destinationStorage, CatalogCommitItem item, CancellationToken cancellationToken)
        {
            var targetStoragePath = GetTargetStorageIconPath(item);

            if (cachedResult.IsCopySucceeded)
            {
                _logger.LogInformation("Seen {IconUrl} before, will copy from {CachedLocation}",
                                       iconUrl,
                                       cachedResult.StorageUrl);
                var storageUrl     = cachedResult.StorageUrl;
                var destinationUrl = destinationStorage.ResolveUri(targetStoragePath);
                if (storageUrl == destinationUrl)
                {
                    // We came across the package that initially caused the icon to be added to the cache.
                    // Skipping it.
                    return(true);
                }
                try
                {
                    await Retry.IncrementalAsync(
                        async() => await iconCacheStorage.CopyAsync(storageUrl, destinationStorage, destinationUrl, null, cancellationToken),
                        e => { _logger.LogWarning(0, e, "Exception while copying from cache {StorageUrl}", storageUrl); return(true); },
                        MaxBlobStorageCopyAttempts,
                        initialWaitInterval : TimeSpan.FromSeconds(5),
                        waitIncrement : TimeSpan.FromSeconds(1));
                }
                catch (Exception e)
                {
                    _logger.LogWarning(0, e, "Copy from cache failed after {NumRetries} attempts. Falling back to copy from external URL. {StorageUrl}",
                                       MaxBlobStorageCopyAttempts,
                                       storageUrl);
                    _iconCopyResultCache.Clear(iconUrl);
                    return(false);
                }
            }
            else
            {
                _logger.LogInformation("Previous copy attempt failed, skipping {IconUrl} for {PackageId} {PackageVersion}",
                                       iconUrl,
                                       item.PackageIdentity.Id,
                                       item.PackageIdentity.Version);
                await _iconProcessor.DeleteIconAsync(destinationStorage, targetStoragePath, cancellationToken, item.PackageIdentity.Id, item.PackageIdentity.Version.ToNormalizedString());
            }
            return(true);
        }
예제 #4
0
 private void Set(Uri iconUrl, ExternalIconCopyResult newItem)
 {
     _externalIconCopyResults.AddOrUpdate(iconUrl, newItem, (_, v) => v.IsCopySucceeded ? v : newItem); // will only overwrite failure results
 }