/// <summary>
        /// This method is used to keep incoming threads cycling while the first thread retrieves the entity from
        /// the persistence store.
        /// </summary>
        /// <param name="cacheHolder">The current cache holder.</param>
        /// <param name="traceId">The resource traceid</param>
        /// <param name="status">The final status.</param>
        /// <returns></returns>
        private void Gatekeeper(EntityCacheHolder <K, E> cacheHolder, Guid traceId, out ResourceRequestResult status, int attempts = 5)
        {
            int retryStart = Environment.TickCount;

            while (cacheHolder.State != EntityCacheHolderState.Completed && --attempts > 0)
            {
                cacheHolder.Wait();
                mResourceConsumer.Retry(traceId, retryStart, ResourceRetryReason.Timeout);
                retryStart = Environment.TickCount;
            }

            if (cacheHolder.State == EntityCacheHolderState.Completed)
            {
                status = ResourceRequestResult.Success;
            }
            else if (attempts <= 0)
            {
                status = ResourceRequestResult.RetryExceeded;
            }
            else
            {
                status = ResourceRequestResult.TaskCancelled;
            }
        }
        /// <summary>
        /// This method retrieves the cache holder from the store. If the cache holder is not present then it
        /// tries to retrieve it from the underlying store.
        /// </summary>
        /// <param name="key">The key to retrieve.</param>
        /// <returns>The cache holder.</returns>
        protected async Task <EntityCacheHolder <K, E> > TryGetCacheHolder(K key)
        {
            var traceId = mResourceConsumer.Start("TryGetCacheHolder", Guid.NewGuid());
            ResourceRequestResult    status = ResourceRequestResult.Unknown;
            EntityCacheHolder <K, E> cacheHolder;
            int start = StatisticsInternal.ActiveIncrement();

            try
            {
                while (true)
                {
                    int retryStart = Environment.TickCount;

                    //Get the cache holder, and if it already exists, see if you need to wait.
                    if (!mEntities.TryGetValue(key, out cacheHolder))
                    {
                        var temp = new EntityCacheHolder <K, E>(key, Policy.EntityDefaultTTL);
                        if (mEntities.TryAdd(key, temp))
                        {
                            //Ok, the entity is not in the cache, so let's go and get the entity.
                            cacheHolder = temp;
                            break;
                        }
                    }
                    else
                    {
                        Gatekeeper(cacheHolder, traceId, out status);
                        return(cacheHolder);
                    }

                    mResourceConsumer.Retry(traceId, retryStart, ResourceRetryReason.Timeout);
                }

                try
                {
                    //Ok, read the entity
                    var result = await Read(key);

                    if (result != null && !result.IsFaulted && (result.ResponseCode == 200 || result.ResponseCode == 404))
                    {
                        cacheHolder.Load(result.Entity, result.ResponseCode);
                        status = ResourceRequestResult.Success;
                    }
                    else
                    {
                        cacheHolder.Cancel();
                        status = ResourceRequestResult.TaskCancelled;
                    }
                }
                catch (Exception ex)
                {
                    cacheHolder.Cancel();
                    status = ResourceRequestResult.TaskCancelled;
                    Collector?.LogException(string.Format("Error requesting {0} - {1} ", typeof(E).Name, key.ToString()), ex);
                }
                finally
                {
                    if (cacheHolder.State == EntityCacheHolderState.Cancelled)
                    {
                        mEntities.TryRemove(key, out cacheHolder);
                    }

                    //Make sure that any waiting requests are released.
                    cacheHolder.Release();
                }
            }
            finally
            {
                StatisticsInternal.ActiveDecrement(start);
                if (status != ResourceRequestResult.Success)
                {
                    StatisticsInternal.ErrorIncrement();
                }

                mResourceConsumer.End(traceId, start, status);
            }

            return(cacheHolder);
        }