/// <inheritdoc />
        public Task <GetContentHashListResult> GetContentHashListAsync(
            Context context,
            StrongFingerprint strongFingerprint,
            CancellationToken cts,
            UrgencyHint urgencyHint)
        {
            return(GetContentHashListCall.RunAsync(Tracer.MemoizationStoreTracer, context, strongFingerprint, async() =>
            {
                // Check for pre-fetched data
                ContentHashListWithDeterminism contentHashListWithDeterminism;

                if (ContentHashListWithDeterminismCache.Instance.TryGetValue(
                        CacheNamespace, strongFingerprint, out contentHashListWithDeterminism))
                {
                    Tracer.RecordUseOfPrefetchedContentHashList();
                    FingerprintTracker.Track(
                        strongFingerprint,
                        contentHashListWithDeterminism.Determinism.ExpirationUtc);
                    return new GetContentHashListResult(contentHashListWithDeterminism);
                }

                // No pre-fetched data. Need to query the server.
                ObjectResult <ContentHashListWithCacheMetadata> responseObject =
                    await ContentHashListAdapter.GetContentHashListAsync(context, CacheNamespace, strongFingerprint).ConfigureAwait(false);

                if (!responseObject.Succeeded)
                {
                    return new GetContentHashListResult(responseObject);
                }

                ContentHashListWithCacheMetadata response = responseObject.Data;
                if (response.ContentHashListWithDeterminism.ContentHashList == null)
                {
                    // Miss
                    return new GetContentHashListResult(new ContentHashListWithDeterminism(null, CacheDeterminism.None));
                }

                GetContentHashListResult unpackResult = UnpackContentHashListWithDeterminismAfterGet(response, CacheId);
                if (!unpackResult.Succeeded)
                {
                    return unpackResult;
                }

                SealIfNecessaryAfterGet(context, strongFingerprint, response);
                FingerprintTracker.Track(strongFingerprint, response.GetRawExpirationTimeUtc());
                return new GetContentHashListResult(unpackResult.ContentHashListWithDeterminism);
            }));
        }
Exemplo n.º 2
0
        /// <inheritdoc />
        public Task <BoolResult> IncorporateStrongFingerprintsAsync(
            Context context,
            IEnumerable <Task <StrongFingerprint> > strongFingerprints,
            CancellationToken cts,
            UrgencyHint urgencyHint)
        {
            return(IncorporateStrongFingerprintsCall.RunAsync(Tracer.MemoizationStoreTracer, context, async() =>
            {
                // BuildCache remembers fingerprints for all content that has been fetched or added through it's APIs.
                // However, the build may end up satisfying some fingerprints without using BuildCache. The build or
                // other cache instances in the cache topology can track these "other" fingerprints and ask that the
                // fingerprints be included in the cache session using the incoporate API. BuildCache will extend the
                // expiration of the fingerprints and mapped content, as if they had just been published.
                foreach (var strongFingerprintTask in strongFingerprints)
                {
                    var strongFingerprint = await strongFingerprintTask.ConfigureAwait(false);

                    // The Incorporate API currently does allow passing the expiration, so we can't pass it here.
                    FingerprintTracker.Track(strongFingerprint);
                }

                return BoolResult.Success;
            }));
        }
Exemplo n.º 3
0
        /// <inheritdoc />
        public Task <AddOrGetContentHashListResult> AddOrGetContentHashListAsync(
            Context context,
            StrongFingerprint strongFingerprint,
            ContentHashListWithDeterminism contentHashListWithDeterminism,
            CancellationToken cts,
            UrgencyHint urgencyHint)
        {
            return(AddOrGetContentHashListCall.RunAsync(Tracer.MemoizationStoreTracer, new OperationContext(context, cts), strongFingerprint, async() =>
            {
                // TODO: Split this out into separate implementations for WriteThrough vs. WriteBehind (bug 1365340)
                if (WriteThroughContentSession == null)
                {
                    // Guaranteed content is currently only available for BlobSessions. (bug 144396)
                    ContentAvailabilityGuarantee guarantee =
                        BackingContentSession is BlobContentSession
                                ? ContentAvailabilityGuarantee.AllContentBackedByCache
                                : ContentAvailabilityGuarantee.NoContentBackedByCache;

                    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}] and expirationUtc=[{expirationUtc}]";
                    Tracer.Debug(context, debugString);
                    ObjectResult <ContentHashListWithCacheMetadata> responseObject =
                        await ContentHashListAdapter.AddContentHashListAsync(
                            context,
                            CacheNamespace,
                            strongFingerprint,
                            valueToAdd).ConfigureAwait(false);

                    if (!responseObject.Succeeded)
                    {
                        return new AddOrGetContentHashListResult(responseObject);
                    }

                    ContentHashListWithCacheMetadata response = responseObject.Data;
                    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);
                        FingerprintTracker.Track(strongFingerprint, rawExpiration);
                        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.");
                FingerprintTracker.Track(strongFingerprint, rawExpiration);
                return new AddOrGetContentHashListResult(new ContentHashListWithDeterminism(null, CacheDeterminism.None));
            }));
        }