private async Task <BoolResult> GetFileWithDedupAsync(OperationContext context, ContentHash contentHash, string path) { VstsBlobIdentifier blobId = ToVstsBlobIdentifier(contentHash.ToBlobIdentifier()); VstsDedupIdentifier dedupId = blobId.ToDedupIdentifier(); try { await TryGatedArtifactOperationAsync <object>( context, contentHash.ToString(), "DownloadToFileAsync", async innerCts => { await DedupStoreClient.DownloadToFileAsync(dedupId, path, null, null, EdgeCache.Allowed, innerCts); return(null); }); } catch (NullReferenceException) // Null reference thrown when DedupIdentifier doesn't exist in VSTS. { return(new BoolResult("DedupIdentifier not found.")); } catch (Exception ex) { return(new BoolResult(ex)); } return(BoolResult.Success); }
/// <inheritdoc /> protected override async Task <PinResult> PinCoreAsync( OperationContext context, ContentHash contentHash, UrgencyHint urgencyHint, Counter retryCounter) { if (!contentHash.HashType.IsValidDedup()) { return(new PinResult($"DedupStore client requires a HashType that supports dedup. Given hash type: {contentHash.HashType}.")); } var pinResult = CheckPinInMemory(contentHash); if (pinResult.Succeeded) { return(pinResult); } VstsBlobIdentifier blobId = ToVstsBlobIdentifier(contentHash.ToBlobIdentifier()); VstsDedupIdentifier dedupId = blobId.ToDedupIdentifier(); if (dedupId.AlgorithmId == Hashing.ChunkDedupIdentifier.ChunkAlgorithmId) { // No need to optimize since pinning a chunk is always a fast operation. return(await PinImplAsync(context, contentHash)); } // Since pinning the whole tree can be an expensive operation, we have optimized how we call it. Depending on the current // keepUntil of the root node, which is unexpensive to check, the operation will behave differently: // The pin operation will be ignored if it is greater than ignorePinThreshold, to reduce amount of calls // The pin operation will be inlined if it is lower than pinInlineThreshold, to make sure that we don't try to use // content that we pin in the background but has expired before we could complete the pin. // The pin operation will be done asynchronously and will return success otherwise. Most calls should follow this // behavior, to avoid waiting on a potentially long operation. We're confident returning a success because we // know that the content is there even though we still have to extend it's keepUntil var keepUntilResult = await CheckNodeKeepUntilAsync(context, dedupId); if (!keepUntilResult.Succeeded) { // Returned a service error. Fail fast. return(new PinResult(keepUntilResult)); } else if (!keepUntilResult.Value.HasValue) { // Content not found. return(new PinResult(PinResult.ResultCode.ContentNotFound)); } var timeLeft = keepUntilResult.Value.Value - DateTime.UtcNow; if (timeLeft > _ignorePinThreshold) { Tracer.Debug(context, $"Pin was skipped because keepUntil has remaining time [{timeLeft}] that is greater than ignorePinThreshold=[{_ignorePinThreshold}]"); _dedupCounters[Counters.PinIgnored].Increment(); return(PinResult.Success); } var pinTask = PinImplAsync(context, contentHash); if (timeLeft < _pinInlineThreshold) { Tracer.Debug(context, $"Pin inlined because keepUntil has remaining time [{timeLeft}] that is less than pinInlineThreshold=[{_pinInlineThreshold}]"); _dedupCounters[Counters.PinInlined].Increment(); return(await pinTask); } pinTask.FireAndForget(context); return(PinResult.Success); }