Ejemplo n.º 1
0
        /// <inheritdoc />
        public Task <AddOrGetContentHashListResult> AddOrGetContentHashListAsync(Context context, StrongFingerprint strongFingerprint, ContentHashListWithDeterminism contentHashListWithDeterminism, CancellationToken cts, UrgencyHint urgencyHint)
        {
            return(WithOperationContext(
                       context,
                       cts,
                       operationContext =>
            {
                return operationContext.PerformOperationAsync(
                    DistributedTracer,
                    async() =>
                {
                    // Metadata cache assumes no guarantees about the fingerprints added, hence invalidate the cache and serve the request using backing store
                    var cacheResult = await MetadataCache.DeleteFingerprintAsync(context, strongFingerprint);
                    if (!cacheResult.Succeeded)
                    {
                        context.Error($"Error while removing fingerprint {strongFingerprint} from metadata cache. Result: {cacheResult}.");
                    }

                    return await _innerCacheSession.AddOrGetContentHashListAsync(context, strongFingerprint, contentHashListWithDeterminism, cts, urgencyHint);
                },
                    traceOperationStarted: true,
                    extraStartMessage: $"StrongFingerprint=({strongFingerprint}) {contentHashListWithDeterminism.ToTraceString()}",
                    extraEndMessage: _ => $"StrongFingerprint=({strongFingerprint}) {contentHashListWithDeterminism.ToTraceString()}");
            }));
        }
Ejemplo n.º 2
0
        public Task <BoolResult> PublishContentHashListAsync(
            Context context,
            StrongFingerprint fingerprint,
            ContentHashListWithDeterminism contentHashList,
            CancellationToken token)
        {
            Contract.Check(_publisher != null)?.Assert("Startup should be run before attempting to publish.");

            var operationContext = new OperationContext(context, token);

            Tracer.Debug(operationContext, $"Enqueueing publish request for StrongFingerprint=[{fingerprint}], CHL=[{contentHashList.ToTraceString()}]");

            return(_publishingGate.GatedOperationAsync(
                       (timeSpentWaiting, gateCount) =>
            {
                ContentHashList?hashListInRemote = null;
                return operationContext.PerformOperationAsync(
                    Tracer,
                    async() =>
                {
                    // Make sure to push the blob in the selector if it exists.
                    var hashesToPush = new List <ContentHash>(contentHashList.ContentHashList.Hashes);
                    if (!fingerprint.Selector.ContentHash.IsZero())
                    {
                        hashesToPush.Add(fingerprint.Selector.ContentHash);
                    }

                    var remotePinResults = await Task.WhenAll(await _publisher.PinAsync(operationContext, hashesToPush, token));
                    var missingFromRemote = remotePinResults
                                            .Where(r => !r.Item.Succeeded)
                                            .Select(r => hashesToPush[r.Index])
                                            .ToArray();

                    if (missingFromRemote.Length > 0)
                    {
                        await PushToRemoteAsync(operationContext, missingFromRemote).ThrowIfFailure();
                    }

                    var addOrGetResult = await _publisher.AddOrGetContentHashListAsync(operationContext, fingerprint, contentHashList, token).ThrowIfFailure();
                    hashListInRemote = addOrGetResult.ContentHashListWithDeterminism.ContentHashList;

                    return BoolResult.Success;
                },
                    traceOperationStarted: false,
                    extraEndMessage: result =>
                    $"Added=[{result.Succeeded && hashListInRemote is null}], " +
                    $"StrongFingerprint=[{fingerprint}], " +
                    $"ContentHashList=[{contentHashList.ToTraceString()}], " +
                    $"TimeSpentWaiting=[{timeSpentWaiting}], " +
                    $"GateCount=[{gateCount}]");
            },
                       token));
        }
Ejemplo n.º 3
0
 /// <summary>
 /// Performs a compare exchange operation on metadata, while ensuring all invariants are kept. If the
 /// fingerprint is not present, then it is inserted.
 /// </summary>
 public Task <Result <bool> > CompareExchange(
     OperationContext context,
     StrongFingerprint strongFingerprint,
     string expectedReplacementToken,
     ContentHashListWithDeterminism expected,
     ContentHashListWithDeterminism replacement)
 {
     return(context.PerformOperationAsync(
                Tracer,
                () => CompareExchangeCore(context, strongFingerprint, expectedReplacementToken, expected, replacement),
                extraStartMessage: $"StrongFingerprint=({strongFingerprint}) expected=[{expected.ToTraceString()}] replacement=[{replacement.ToTraceString()}]",
                extraEndMessage: _ => $"StrongFingerprint=({strongFingerprint})  expected=[{expected.ToTraceString()}] replacement=[{replacement.ToTraceString()}]"));
 }
Ejemplo n.º 4
0
        /// <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()}"));
        }
Ejemplo n.º 5
0
 /// <summary>
 /// Performs a compare exchange operation on metadata, while ensuring all invariants are kept. If the
 /// fingerprint is not present, then it is inserted.
 /// </summary>
 public Task <Result <bool> > CompareExchange(
     OperationContext context,
     StrongFingerprint strongFingerprint,
     string expectedReplacementToken,
     ContentHashListWithDeterminism expected,
     ContentHashListWithDeterminism replacement)
 {
     return(context.PerformOperationWithTimeoutAsync(
                Tracer,
                nestedContext => CompareExchangeCore(nestedContext, strongFingerprint, expectedReplacementToken, expected, replacement),
                extraStartMessage: $"StrongFingerprint=[{strongFingerprint}] ExpectedReplacementToken=[{expectedReplacementToken}] Expected=[{expected.ToTraceString()}] Replacement=[{replacement.ToTraceString()}]",
                extraEndMessage: result => $"StrongFingerprint=[{strongFingerprint}] ExpectedReplacementToken=[{expectedReplacementToken}] Expected=[{expected.ToTraceString()}] Replacement=[{replacement.ToTraceString()}] Exchanged=[{result.GetValueOrDefault(false)}]",
                timeout: _timeout));
 }
        /// <inheritdoc />
        public Task <AddOrGetContentHashListResult> AddOrGetContentHashListAsync(Context context, StrongFingerprint strongFingerprint, ContentHashListWithDeterminism contentHashListWithDeterminism, CancellationToken cts, UrgencyHint urgencyHint = UrgencyHint.Nominal)
        {
            var contentHashes = contentHashListWithDeterminism.ContentHashList.Hashes;

            return(PerformOperationAsync(
                       context,
                       cts,
                       (ctx, client) => client.AddOrGetContentHashListAsync(ctx, strongFingerprint, contentHashListWithDeterminism),
                       counter: _memoizationCounters[MemoizationStoreCounters.AddOrGetContentHashList],
                       retryCounter: _memoizationCounters[MemoizationStoreCounters.AddOrGetContentHashListRetries],
                       additionalStartMessage: $"StrongFingerprint=({strongFingerprint}) {contentHashListWithDeterminism.ToTraceString()}",
                       additionalStopMessage: $"StrongFingerprint=({strongFingerprint}) {contentHashListWithDeterminism.ToTraceString()}"));
        }