/// <summary>
        /// Attempts to get the cache entry which was last stored for the given fingerprint
        /// </summary>
        public static async Task <Possible <CacheEntry?> > TryGetLatestCacheEntryAsync(
            this ITwoPhaseFingerprintStore store,
            LoggingContext loggingContext,
            ContentFingerprint fingerprint,
            DateTime?time = null)
        {
            // This method starts at a leaf time node and traverses up until  a parent node
            // is found which has a corresponding cache entry. Then the children of that parent
            // node are iterated recursively (latest in time first) to find the node that closest in time
            // to the specified time parameter. This yields the cache entry added most recently.
            var node = new TimeTreeNode(time);

            while (node != null)
            {
                var getResult = await store.TryGetTemporalCacheEntryAsync(loggingContext, node, fingerprint);

                if (!getResult.Succeeded)
                {
                    return(getResult);
                }
                else if (getResult.Result != null)
                {
                    return(await TryGetLatestChildCacheEntryAsync(loggingContext, node, store, fingerprint, getResult));
                }

                node = node.Parent;
            }

            return((CacheEntry?)null);
        }
        public static async Task <Possible <Unit> > TryPublishTemporalCacheEntryAsync(
            this ITwoPhaseFingerprintStore store,
            LoggingContext loggingContext,
            ContentFingerprint fingerprint,
            CacheEntry entry,
            DateTime?time = null)
        {
            // Attempt to store the cache entry into all the time buckets
            var node  = new TimeTreeNode(time);
            var tasks = new List <Task <Possible <CacheEntryPublishResult, Failure> > >();

            while (node != null)
            {
                tasks.Add(store.TryPublishNodeTemporalCacheEntryAsync(
                              loggingContext,
                              node,
                              fingerprint,
                              entry));

                node = node.Parent;
            }

            await Task.WhenAll(tasks);

            foreach (var task in tasks)
            {
                if (!task.Result.Succeeded)
                {
                    return(task.Result.Failure);
                }
            }

            return(Unit.Void);
        }
        private static async Task <Possible <CacheEntryPublishResult, Failure> > TryPublishNodeTemporalCacheEntryAsync(
            this ITwoPhaseFingerprintStore store,
            LoggingContext loggingContext,
            TimeTreeNode node,
            ContentFingerprint fingerprint,
            CacheEntry entry)
        {
            // Unblock the caller
            await Task.Yield();

            var result = await store.TryPublishCacheEntryAsync(
                new WeakContentFingerprint(fingerprint.Hash),
                s_dummyPathSetHash,
                node.NodeFingerprint,
                entry);

            Logger.Log.TemporalCacheEntryTrace(loggingContext, I($"Publishing temporal cache entry: Node='{node}', Fingerprint='{fingerprint}' MetadataHash='{entry.MetadataHash}' Success='{result.Succeeded}'"));

            if (result.Succeeded)
            {
                Logger.Log.TemporalCacheEntryTrace(loggingContext, I($"Published temporal cache entry: Node='{node}', Fingerprint='{fingerprint}' Status='{result.Result.Status}'"));
            }
            else
            {
                Logger.Log.TemporalCacheEntryTrace(loggingContext, I($"Failed publish of temporal cache entry: Node='{node}', Fingerprint='{fingerprint}' Failure:='{result.Failure.DescribeIncludingInnerFailures()}'"));
            }

            return(result);
        }
Esempio n. 4
0
        private static async Task <Possible <CacheEntry?, Failure> > TryGetTemporalCacheEntryAsync(
            this ITwoPhaseFingerprintStore store,
            LoggingContext loggingContext,
            TimeTreeNode node,
            ContentFingerprint fingerprint)
        {
            // Unblock the caller
            await Task.Yield();

            var result = await store.TryGetCacheEntryAsync(
                new WeakContentFingerprint(fingerprint.Hash),
                s_dummyPathSetHash,
                node.NodeFingerprint);

            if (result.Succeeded)
            {
                if (result.Result?.MetadataHash != null)
                {
                    Logger.Log.TemporalCacheEntryTrace(loggingContext, I($"Successfully retrieved temporal cache entry: Node='{node}', Fingerprint='{fingerprint}' MetadataHash='{result.Result?.MetadataHash}'"));
                }
                else
                {
                    Logger.Log.TemporalCacheEntryTrace(loggingContext, I($"Query for temporal cache entry returned no results: Node='{node}', Fingerprint='{fingerprint}'"));
                }
            }
            else
            {
                Logger.Log.TemporalCacheEntryTrace(loggingContext, I($"Failed retrieval of temporal cache entry: Node='{node}', Fingerprint='{fingerprint}' Failure:='{result.Failure.DescribeIncludingInnerFailures()}'"));
            }

            return(result);
        }
Esempio n. 5
0
        public static async Task <Possible <Unit> > TryPublishTemporalCacheEntryAsync(
            this ITwoPhaseFingerprintStore store,
            LoggingContext loggingContext,
            ContentFingerprint fingerprint,
            CacheEntry entry,
            DateTime?time = null)
        {
            // Attempt to store the cache entry into all the time buckets
            var node  = new TimeTreeNode(time);
            var tasks = new List <Task <Possible <CacheEntryPublishResult, Failure> > >();

            while (node != null)
            {
                tasks.Add(store.TryPublishNodeTemporalCacheEntryAsync(
                              loggingContext,
                              node,
                              fingerprint,
                              entry));

                // Don't store full cache entry for parent nodes since time tree addition
                // will trigger pins (and consequently downloads) of content for conflicting entries
                entry = new CacheEntry(entry.MetadataHash, entry.OriginatingCache, ArrayView <ContentHash> .Empty);

                node = node.Parent;
            }

            await Task.WhenAll(tasks);

            foreach (var task in tasks)
            {
                if (!task.Result.Succeeded)
                {
                    return(task.Result.Failure);
                }
            }

            return(Unit.Void);
        }
        /// <summary>
        /// Attempts to get the cache entry for the child which comes latest in time
        /// </summary>
        private static async Task <Possible <CacheEntry?> > TryGetLatestChildCacheEntryAsync(
            LoggingContext loggingContext,
            TimeTreeNode node,
            ITwoPhaseFingerprintStore store,
            ContentFingerprint fingerprint,
            Possible <CacheEntry?> fallbackResult)
        {
            var childNodes = node.Children;
            var children   = childNodes.Select(childNode => store.TryGetTemporalCacheEntryAsync(loggingContext, childNode, fingerprint)).ToList();

            var childResults = await Task.WhenAll(children);

            // In reverse order (i.e. later in time), check children to see which
            // one has results
            for (int i = childResults.Length - 1; i >= 0; i--)
            {
                var childResult = childResults[i];
                var childNode   = childNodes[i];
                if (!childResult.Succeeded)
                {
                    continue;
                }

                if (childResult.Result != null)
                {
                    return(await TryGetLatestChildCacheEntryAsync(
                               loggingContext,
                               childNode,
                               store,
                               fingerprint,
                               childResult));
                }
            }

            return(fallbackResult);
        }