/// <summary> /// Whether or not an existing guarantee is stronger than a provided guarantee. /// </summary> public static bool HasStrongerGuarantee( this ContentAvailabilityGuarantee existingGuarantee, ContentAvailabilityGuarantee otherGuarantee) { // behaviour: this.HasStrongerGuarantee(that) // returns true if this has content backed by cache and that is not. // returns false if they are equal or if the other has content backed by cache // and this does not. return(existingGuarantee < otherGuarantee); }
/// <inheritdoc /> protected async Task <AddOrGetContentHashListResult> AddOrGetContentHashListAsync( Context context, StrongFingerprint strongFingerprint, ContentHashListWithDeterminism contentHashListWithDeterminism, ContentAvailabilityGuarantee guarantee) { try { DateTime expirationUtc = FingerprintTracker.GenerateNewExpiration(); var valueToAdd = new ContentHashListWithCacheMetadata( contentHashListWithDeterminism, expirationUtc, guarantee); Tracer.Debug( context, $"Adding contentHashList=[{valueToAdd.ContentHashListWithDeterminism.ContentHashList}] determinism=[{valueToAdd.ContentHashListWithDeterminism.Determinism}] to VSTS with contentAvailabilityGuarantee=[{valueToAdd.ContentGuarantee}] and expirationUtc=[{expirationUtc}]"); var contentHashListResponseObject = await ContentHashListAdapter.AddContentHashListAsync(context, CacheNamespace, strongFingerprint, valueToAdd).ConfigureAwait(false); if (!contentHashListResponseObject.Succeeded) { return(new AddOrGetContentHashListResult(contentHashListResponseObject)); } var contentHashListResponse = contentHashListResponseObject.Data; var inconsistencyErrorMessage = CheckForResponseInconsistency(contentHashListResponse); if (inconsistencyErrorMessage != null) { return(new AddOrGetContentHashListResult(inconsistencyErrorMessage)); } ContentHashList contentHashListToReturn = UnpackContentHashListAfterAdd( contentHashListWithDeterminism.ContentHashList, contentHashListResponse); CacheDeterminism determinismToReturn = UnpackDeterminism(contentHashListResponse, CacheId); if (guarantee == ContentAvailabilityGuarantee.AllContentBackedByCache && !determinismToReturn.IsDeterministic) { return(new AddOrGetContentHashListResult( "Inconsistent BuildCache service response. Unbacked values should never override backed values.")); } FingerprintTracker.Track(strongFingerprint, contentHashListResponse.GetRawExpirationTimeUtc()); return(new AddOrGetContentHashListResult(new ContentHashListWithDeterminism(contentHashListToReturn, determinismToReturn))); } catch (Exception e) { return(new AddOrGetContentHashListResult(e)); } }
/// <summary> /// Initializes a new instance of the <see cref="BlobContentHashListWithCacheMetadata"/> class. /// </summary> public BlobContentHashListWithCacheMetadata( BlobContentHashListWithDeterminism contentHashListWithDeterminism, DateTime?contentHashListExpirationUtc, ContentAvailabilityGuarantee contentGuarantee, byte[] hashOfExistingContentHashList = null) { if (contentHashListExpirationUtc != null && contentHashListExpirationUtc.Value.Kind != DateTimeKind.Utc) { throw new ArgumentException("Time to live must be an absolute UTC date time."); } ContentHashListWithDeterminism = contentHashListWithDeterminism; ContentHashListExpirationUtc = contentHashListExpirationUtc; ContentGuarantee = contentGuarantee; HashOfExistingContentHashList = hashOfExistingContentHashList; }
/// <summary> /// Initializes a new instance of the <see cref="DistributedContentSession{T}"/> class. /// </summary> public DistributedContentSession( string name, IContentSession inner, IContentLocationStore contentLocationStore, ContentAvailabilityGuarantee contentAvailabilityGuarantee, DistributedContentCopier <T> contentCopier, byte[] localMachineLocation, PinCache pinCache = null, ContentTrackerUpdater contentTrackerUpdater = null, DistributedContentStoreSettings settings = default) : base( name, inner, contentLocationStore, contentAvailabilityGuarantee, contentCopier, localMachineLocation, pinCache: pinCache, contentTrackerUpdater: contentTrackerUpdater, settings) { }
/// <summary> /// Initializes a new instance of the <see cref="DistributedContentSession{T}"/> class. /// </summary> public DistributedContentSession( string name, IContentSession inner, IContentLocationStore contentLocationStore, ContentAvailabilityGuarantee contentAvailabilityGuarantee, DistributedContentCopier <T> contentCopier, MachineLocation localMachineLocation, PinCache pinCache = null, ContentTrackerUpdater contentTrackerUpdater = null, DistributedContentStoreSettings settings = default) : base( name, inner, contentLocationStore, contentAvailabilityGuarantee, contentCopier, localMachineLocation, pinCache: pinCache, contentTrackerUpdater: contentTrackerUpdater, settings) { _putFileGate = new SemaphoreSlim(settings.MaximumConcurrentPutFileOperations); }
/// <inheritdoc /> public Task <AddOrGetContentHashListResult> AddOrGetContentHashListAsync( Context context, StrongFingerprint strongFingerprint, ContentHashListWithDeterminism contentHashListWithDeterminism, CancellationToken cts, UrgencyHint urgencyHint) { return(new OperationContext(context, cts).PerformOperationAsync( Tracer.MemoizationStoreTracer, async() => { // TODO: Split this out into separate implementations for WriteThrough vs. WriteBehind (bug 1365340) if (WriteThroughContentSession == null) { ContentAvailabilityGuarantee guarantee = ManuallyExtendContentLifetime ? ContentAvailabilityGuarantee.NoContentBackedByCache : ContentAvailabilityGuarantee.AllContentBackedByCache; return await AddOrGetContentHashListAsync( context, strongFingerprint, contentHashListWithDeterminism, guarantee).ConfigureAwait(false); } // Ensure that the content exists somewhere before trying to add if (!await EnsureContentIsAvailableAsync( context, contentHashListWithDeterminism.ContentHashList.Hashes, cts, urgencyHint).ConfigureAwait(false)) { return new AddOrGetContentHashListResult( "Referenced content must exist in the cache before a new content hash list is added."); } DateTime expirationUtc = FingerprintTracker.GenerateNewExpiration(); var valueToAdd = new ContentHashListWithCacheMetadata( contentHashListWithDeterminism, expirationUtc, ContentAvailabilityGuarantee.NoContentBackedByCache); DateTime?rawExpiration = null; const int addLimit = 3; for (int addAttempts = 0; addAttempts < addLimit; addAttempts++) { var debugString = $"Adding contentHashList=[{valueToAdd.ContentHashListWithDeterminism.ContentHashList}] " + $"determinism=[{valueToAdd.ContentHashListWithDeterminism.Determinism}] to VSTS with " + $"contentAvailabilityGuarantee=[{valueToAdd.ContentGuarantee}], expirationUtc=[{expirationUtc}], forceUpdate=[{ForceUpdateOnAddContentHashList}]"; Tracer.Debug(context, debugString); Result <ContentHashListWithCacheMetadata> responseObject = await ContentHashListAdapter.AddContentHashListAsync( context, CacheNamespace, strongFingerprint, valueToAdd, forceUpdate: ForceUpdateOnAddContentHashList).ConfigureAwait(false); if (!responseObject.Succeeded) { return new AddOrGetContentHashListResult(responseObject); } ContentHashListWithCacheMetadata response = responseObject.Value; var inconsistencyErrorMessage = CheckForResponseInconsistency(response); if (inconsistencyErrorMessage != null) { return new AddOrGetContentHashListResult(inconsistencyErrorMessage); } rawExpiration = response.GetRawExpirationTimeUtc(); ContentHashList contentHashListToReturn = UnpackContentHashListAfterAdd(contentHashListWithDeterminism.ContentHashList, response); CacheDeterminism determinismToReturn = UnpackDeterminism(response, CacheId); bool needToUpdateExistingValue = await CheckNeedToUpdateExistingValueAsync( context, response, contentHashListToReturn, cts, urgencyHint).ConfigureAwait(false); if (!needToUpdateExistingValue) { SealIfNecessaryAfterUnbackedAddOrGet(context, strongFingerprint, contentHashListWithDeterminism, response); await TrackFingerprintAsync(context, strongFingerprint, rawExpiration).ConfigureAwait(false); return new AddOrGetContentHashListResult( new ContentHashListWithDeterminism(contentHashListToReturn, determinismToReturn)); } var hashOfExistingContentHashList = response.HashOfExistingContentHashList; Tracer.Debug(context, $"Attempting to replace unbacked value with hash {hashOfExistingContentHashList.ToHex()}"); valueToAdd = new ContentHashListWithCacheMetadata( contentHashListWithDeterminism, expirationUtc, ContentAvailabilityGuarantee.NoContentBackedByCache, hashOfExistingContentHashList ); } Tracer.Warning( context, $"Lost the AddOrUpdate race {addLimit} times against unbacked values. Returning as though the add succeeded for now."); await TrackFingerprintAsync(context, strongFingerprint, rawExpiration).ConfigureAwait(false); return new AddOrGetContentHashListResult(new ContentHashListWithDeterminism(null, CacheDeterminism.None)); }, traceOperationStarted: true, extraStartMessage: $"StrongFingerprint=({strongFingerprint}), ForceUpdate=({ForceUpdateOnAddContentHashList}) {contentHashListWithDeterminism.ToTraceString()}", extraEndMessage: _ => $"StrongFingerprint=({strongFingerprint}), ForceUpdate=({ForceUpdateOnAddContentHashList}) {contentHashListWithDeterminism.ToTraceString()}")); }