/// <inheritdoc /> public Task <Possible <ContentAvailabilityBatchResult, Failure> > TryLoadAvailableContentAsync(IReadOnlyList <ContentHash> hashes) { return(Task.Run <Possible <ContentAvailabilityBatchResult, Failure> >( () => { lock (m_lock) { bool allAvailable = true; var results = new ContentAvailabilityResult[hashes.Count]; for (int i = 0; i < hashes.Count; i++) { CacheEntry entry; bool available = m_content.TryGetValue(hashes[i], out entry) && entry.Sites != CacheSites.None; long bytesTransferredRemotely = 0; if (available) { if ((entry.Sites & CacheSites.Local) == 0) { ExceptionUtilities.HandleRecoverableIOException( () => { // Copy to local side. bytesTransferredRemotely = Download(hashes[i]); }, ex => throw new BuildXLException(I($"Failed to load content '{hashes[i]}' from remote site"), ex)); } entry.Sites |= CacheSites.Local; } results[i] = new ContentAvailabilityResult( hashes[i], isAvailable: available, bytesTransferred: bytesTransferredRemotely, sourceCache: nameof(MockArtifactContentCache)); allAvailable &= available; } return new ContentAvailabilityBatchResult( ReadOnlyArray <ContentAvailabilityResult> .FromWithoutCopy(results), allContentAvailable: allAvailable); } })); }
/// <inheritdoc /> public async Task <Possible <ContentAvailabilityBatchResult, Failure> > TryLoadAvailableContentAsync(IReadOnlyList <ContentHash> hashes) { // TODO: These conversions are silly. CasHash[] casHashes = new CasHash[hashes.Count]; for (int i = 0; i < casHashes.Length; i++) { casHashes[i] = new CasHash(new global::BuildXL.Cache.Interfaces.Hash(hashes[i])); } Possible <ICacheSession, Failure> maybeOpen = m_cache.Get(nameof(TryLoadAvailableContentAsync)); if (!maybeOpen.Succeeded) { return(maybeOpen.Failure); } Possible <string, Failure>[] multiMaybePinned = await maybeOpen.Result.PinToCasAsync(casHashes); Contract.Assume(multiMaybePinned != null); Contract.Assume(multiMaybePinned.Length == casHashes.Length); var results = new ContentAvailabilityResult[casHashes.Length]; Failure aggregateFailure = null; bool allContentAvailable = true; for (int i = 0; i < casHashes.Length; i++) { Possible <string, Failure> maybePinned = multiMaybePinned[i]; if (maybePinned.Succeeded) { // TODO: This doesn't indicate what is local / how much was transfered. From the similar BuildCache adapter: // long transferred = successfulResult.TransferredBytes; // TODO: This API will fail just because content isn't available; see below for that case. results[i] = new ContentAvailabilityResult(hashes[i], isAvailable: true, bytesTransferred: 0, sourceCache: maybePinned.Result); BuildXL.Storage.Tracing.Logger.Log.StorageCacheContentPinned(Events.StaticContext, casHashes[i].ToString(), maybePinned.Result); } else if (maybePinned.Failure is NoCasEntryFailure) { // TODO: As above: this API will fail just because content isn't available, and that case isn't distinguishable (at least by looking at the interface alone). // That's not reasonable for implementations in general, such as BuildCache (or anything else with weak LRU; failures should not be 'normal' // By contract, IArtifactContentCache defines available vs. unavailable on the (successful) result object, as established here. // Note that we have idenitifed the content-miss case by reflecting; but that is quite fragile and requires some not-yet-prescribed co-operation from the implementations as to failure type. allContentAvailable = false; results[i] = new ContentAvailabilityResult(hashes[i], isAvailable: false, bytesTransferred: 0, sourceCache: "ContentMiss"); } else { if (aggregateFailure == null) { aggregateFailure = maybePinned.Failure.Annotate("Retrieval failed for one or more content blobs by hash"); } // We return only an aggregate error from this function. To prevent erasing information, we log each error returned from // the cache here (the caller may choose to also log the aggregate failure. BuildXL.Storage.Tracing.Logger.Log.StorageCacheCopyLocalError( Events.StaticContext, hashes[i].ToString(), maybePinned.Failure.DescribeIncludingInnerFailures()); } } if (aggregateFailure == null) { return(new ContentAvailabilityBatchResult( ReadOnlyArray <ContentAvailabilityResult> .FromWithoutCopy(results), allContentAvailable: allContentAvailable)); } else { return(aggregateFailure); } }