/// <summary>
        /// Attempts to retrieve an item from memory cache
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="dto"></param>
        /// <returns></returns>
        private bool TryGetFromMemoryCache <T>(string key, ref CacheResponseDto <T> dto)
        {
            Log(eCacheEvent.KeyValueGotFromMemoryAttempt, key);

            #region Fast uncloned cache
            if (_fastMemcache.Contains(key))
            {
                dto.Data = (T)_fastMemcache.Get(key);
                dto.IsFromInMemoryCache = true;
                Log(eCacheEvent.KeyValueGotFromMemory, key);
                return(true);
            }
            #endregion


            byte[] buffer = null;
            if ((buffer = (byte[])_memoryCache.Get(key)) != null)
            {
                dto.Data = FromByteBuffer <T>(buffer, true).Object;
                dto.IsFromInMemoryCache = true;
                Log(eCacheEvent.KeyValueGotFromMemory, key);
                return(true);
            }
            else
            {
                return(false);
            }
        }
        /// <summary>
        /// Retrieves an item from the cache, if possible.
        /// It'll lazily init the cache asynchronously and return null if necessary.
        /// It will always return in a timely manner; e.g., without waiting for a cache connection
        /// It will never propagate an exception, unless the throwExceptions flag is set on the constructor
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task <CacheResponseDto <T> > GetWithMetaDataAsync <T>(string key)
        {
            key = CleanKey(key);
            var dto = new CacheResponseDto <T>().StartTiming();

            try
            {
                if (!TryGetFromMemoryCache(key, ref dto)) // try memory cache first
                {
                    if (IsRedisAccessible())              // Couldn't get it from memory, so try redis
                    {
                        var byteBuffer = await _cacheDatabase.StringGetAsync(key);

                        if (!byteBuffer.IsNullOrEmpty)
                        {
                            Log(eCacheEvent.KeyValueGotFromCentralAttempt, key);
                            PostProcessRedisBuffer(key, ref dto, byteBuffer);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                if (_config.IsExceptionPropagationEnabled)
                {
                    throw;
                }
                Log(ex);
                dto.Exception = ex;
            }
            return(dto.StopTiming());
        }
        /// <summary>
        /// Deserializes a redis buffer, puts it into the DTO, puts it into the memory cache
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="dto"></param>
        /// <param name="byteBuffer"></param>
        private void PostProcessRedisBuffer <T>(string key, ref CacheResponseDto <T> dto, byte[] byteBuffer)
        {
            var item = FromByteBuffer <T>(byteBuffer, true);

            if (item != null)
            {
                dto.Data = item.Object;
            }
            dto.IsFromCentralCacheServer = true;
            if (item != null)
            {
                Log(eCacheEvent.KeyValueGotFromCentral, key);
            }
            // Put it into the in-memory cache
            if (item != null)
            {
                SetInMemoryCache(key, byteBuffer, item.Properties.Get("ExpirationUtc") as DateTime?);
                PutInFastCache(key, item.Object);
            }
        }
        /// <summary>
        /// Retrieves an item from the cache, if possible.
        /// It'll lazily init the cache asynchronously and return null if necessary.
        /// It will always return in a timely manner; e.g., without waiting for a cache connection
        /// It will never propagate an exception, unless the throwExceptions flag is set on the constructor
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public CacheResponseDto <T> GetWithMetaData <T>(string key)
        {
            key = CleanKey(key);
            var dto = new CacheResponseDto <T>().StartTiming();

            try
            {
                if (!TryGetFromMemoryCache(key, ref dto))
                {
                    if (IsRedisAccessible())
                    {
                        var byteBuffer = _cacheDatabase.StringGet(key);
                        PostProcessRedisBuffer(key, ref dto, byteBuffer);
                    }
                }
            }
            catch (Exception ex)
            {
                Log(ex);
                dto.Exception = ex;
            }
            return(dto.StopTiming());
        }