Пример #1
0
        /// <summary>
        /// SetAsync
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="utcTicks"></param>
        /// <param name="options"></param>
        /// <param name="token"></param>
        /// <returns></returns>
        /// <exception cref="CacheException"></exception>
        public async Task <bool> SetAsync(string key, byte[] value, UtcNowTicks utcTicks, DistributedCacheEntryOptions options, CancellationToken token = default)
        {
            token.ThrowIfCancellationRequested();

            IDatabase database = await GetDefaultDatabaseAsync().ConfigureAwait(false);

            if (options.AbsoluteExpirationRelativeToNow.HasValue)
            {
                options.AbsoluteExpiration = TimeUtil.UtcNow + options.AbsoluteExpirationRelativeToNow;
            }

            long?absoluteExpireUnixSeconds = options.AbsoluteExpiration?.ToUnixTimeSeconds();
            long?slideSeconds = (long?)(options.SlidingExpiration?.TotalSeconds);

            try
            {
                RedisResult redisResult = await database.ScriptEvaluateAsync(GetDefaultLoadLuas().LoadedSetWithTimestampLua, new RedisKey[] { GetRealKey("", key) },
                                                                             new RedisValue[]
                {
                    absoluteExpireUnixSeconds ?? -1,
                    slideSeconds ?? -1,
                    GetInitialExpireSeconds(absoluteExpireUnixSeconds, slideSeconds) ?? -1,
                    value,
                    utcTicks.Ticks
                }).ConfigureAwait(false);

                int rt = (int)redisResult;

                if (rt == 1)
                {
                    return(true);
                }
                else if (rt == 8)
                {
                    _logger.LogWarning("检测到,Cache Invalidation Concurrency冲突,已被阻止. {key}, {Timestamp}", key, utcTicks);
                }
                else if (rt == 9)
                {
                    _logger.LogWarning("检测到,Cache Update Concurrency冲突,已被阻止. {key}, {Timestamp}", key, utcTicks);
                }

                return(false);
            }
            catch (RedisServerException ex) when(ex.Message.StartsWith("NOSCRIPT", StringComparison.InvariantCulture))
            {
                _logger.LogError(ex, "NOSCRIPT, will try again.");

                InitLoadedLuas();

                return(await SetAsync(key, value, utcTicks, options, token).ConfigureAwait(false));
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "分析这个");

                throw Exceptions.Unkown(key, null, ex);
            }
        }
Пример #2
0
        /// <summary>
        /// CacheAsideAsync
        /// </summary>
        /// <param name="cacheItem"></param>
        /// <param name="dbRetrieve"></param>
        /// <param name="cache"></param>
        /// <param name="memoryLockManager"></param>
        /// <param name="database"></param>
        /// <param name="logger"></param>
        /// <returns></returns>
        /// <exception cref="CacheException"></exception>
        /// <exception cref="RepositoryException"></exception>
        public static async Task <TResult?> CacheAsideAsync <TResult>(
            CachedItem <TResult> cacheItem, Func <IDatabaseReader, Task <TResult> > dbRetrieve,
            ICache cache, IMemoryLockManager memoryLockManager, IDatabase database, ILogger logger)
            where TResult : class
        {
            //Cache First
            TResult?result = await cacheItem.GetFromAsync(cache).ConfigureAwait(false);

            if (result != null)
            {
                return(result);
            }

            using var @lock = memoryLockManager.Lock(cacheItem.ResourceType, cacheItem.CacheKey, Consts.OccupiedTime, Consts.PatienceTime);

            if (@lock.IsAcquired)
            {
                //Double Check
                result = await cacheItem.GetFromAsync(cache).ConfigureAwait(false);

                if (result != null)
                {
                    return(result);
                }

                TResult dbRt = await dbRetrieve(database).ConfigureAwait(false);

                UtcNowTicks now = TimeUtil.UtcNowTicks;


                // 如果TResult是集合类型,可能会存入空集合。而在EntityCache中是不会存入空集合的。
                //这样设计是合理的,因为EntityCache是按Entity角度,存入的Entity会复用,就像一个KVStore一样,而CachedItem纯粹是一个查询结果,不思考查询结果的内容。
                if (dbRt != null)
                {
                    UpdateCache(cacheItem.Value(dbRt).Timestamp(now), cache);
                    logger.LogInformation($"缓存 Missed. Entity:{cacheItem.GetType().Name}, CacheKey:{cacheItem.CacheKey}");
                }
                else
                {
                    logger.LogInformation($"查询到空值. Entity:{cacheItem.GetType().Name}, CacheKey:{cacheItem.CacheKey}");
                }

                return(dbRt);
            }
            else
            {
                logger.LogCritical($"锁未能占用. Entity:{cacheItem.GetType().Name}, CacheKey:{cacheItem.CacheKey}, Lock Status:{@lock.Status}");

                return(await dbRetrieve(database).ConfigureAwait(false));
            }
        }
Пример #3
0
        /// <summary>
        /// 返回是否找到了
        /// </summary>
        /// <param name="key"></param>
        /// <param name="timestampInUnixMilliseconds"></param>
        /// <param name="token"></param>
        /// <returns></returns>
        /// <exception cref="CacheException"></exception>
        public async Task <bool> RemoveAsync(string key, UtcNowTicks utcTicks, CancellationToken token = default)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }

            token.ThrowIfCancellationRequested();

            IDatabase database = await GetDefaultDatabaseAsync().ConfigureAwait(false);

            try
            {
                RedisResult redisResult = await database.ScriptEvaluateAsync(
                    GetDefaultLoadLuas().LoadedRemoveWithTimestampLua,
                    new RedisKey[] { GetRealKey("", key) },
                    new RedisValue[]
                {
                    utcTicks.Ticks,
                    _invalidationVersionExpirySeconds
                }).ConfigureAwait(false);

                return((int)redisResult == 1);
            }
            catch (RedisServerException ex) when(ex.Message.StartsWith("NOSCRIPT", StringComparison.InvariantCulture))
            {
                _logger.LogError(ex, "NOSCRIPT, will try again.");

                InitLoadedLuas();

                return(await RemoveAsync(key, utcTicks, token).ConfigureAwait(false));
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "分析这个");

                throw Exceptions.Unkown(key, null, ex);
            }
        }
Пример #4
0
        public async Task CacheTimestamp_Timestamp_TestAsync(int?absoluteSecondsRelativeToNow, int?slidingSeconds)
        {
            DistributedCacheEntryOptions entryOptions = new DistributedCacheEntryOptions();

            entryOptions.AbsoluteExpirationRelativeToNow = absoluteSecondsRelativeToNow == null ? null : (TimeSpan?)TimeSpan.FromSeconds(absoluteSecondsRelativeToNow.Value);
            entryOptions.SlidingExpiration = slidingSeconds == null ? null : (TimeSpan?)TimeSpan.FromSeconds(slidingSeconds.Value);

            IDatabase database = _redisConnection.GetDatabase(_databaseNumber);

            Book book = Mocker.MockOne();

            await AddToDatabaeAsync(new Book[] { book }).ConfigureAwait(false);

            UtcNowTicks utcNowTicks  = TimeUtil.UtcNowTicks;
            UtcNowTicks utcNowTicks2 = TimeUtil.UtcNowTicks;
            UtcNowTicks utcNowTicks3 = TimeUtil.UtcNowTicks;

            utcNowTicks2.Ticks -= 10000;
            utcNowTicks3.Ticks += 10000;

            string oldName = book.Name;
            await _cache.SetAsync(nameof(Book) + book.Id.ToString(), book, utcNowTicks, entryOptions).ConfigureAwait(false);

            book.Name += "22222";

            await _cache.SetAsync(nameof(Book) + book.Id.ToString(), book, utcNowTicks2, entryOptions).ConfigureAwait(false);

            Book cached = await _cache.GetAsync <Book>(nameof(Book) + book.Id.ToString());

            Assert.True(cached.Name == oldName);

            await _cache.SetAsync(nameof(Book) + book.Id.ToString(), book, utcNowTicks3, entryOptions).ConfigureAwait(false);

            Book cached2 = await _cache.GetAsync <Book>(nameof(Book) + book.Id.ToString());

            Assert.True(cached2.Name == book.Name);
        }
Пример #5
0
        public CachedItem <TResult> Timestamp(UtcNowTicks utcTicks)
        {
            UtcTikcs = utcTicks;

            return(this);
        }
Пример #6
0
        /// <summary>
        /// SetStringAsync
        /// </summary>
        /// <param name="cache"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="utcTicks"></param>
        /// <param name="options"></param>
        /// <param name="token"></param>
        /// <returns></returns>
        /// <exception cref="CacheException"></exception>
        public static async Task SetStringAsync(this ICache cache, string key, string value, UtcNowTicks utcTicks, DistributedCacheEntryOptions options, CancellationToken token = default)
        {
            try
            {
                byte[] bytes = await SerializeUtil.PackAsync(value).ConfigureAwait(false);

                await cache.SetAsync(key, bytes, utcTicks, options, token).ConfigureAwait(false);
            }
            catch (Exception ex) when(ex is not CacheException)
            {
                throw Exceptions.Unkown(key: key, value: value, innerException: ex);
            }
        }
Пример #7
0
 /// <summary>
 /// SetIntAsync
 /// </summary>
 /// <param name="cache"></param>
 /// <param name="key"></param>
 /// <param name="value"></param>
 /// <param name="utcTicks"></param>
 /// <param name="options"></param>
 /// <param name="token"></param>
 /// <returns></returns>
 /// <exception cref="CacheException"></exception>
 public static Task SetIntAsync(this ICache cache, string key, int value, UtcNowTicks utcTicks, DistributedCacheEntryOptions options, CancellationToken token = default)
 {
     return(cache.SetStringAsync(key, value.ToString(GlobalSettings.Culture), utcTicks, options, token));
 }