Пример #1
0
        public void TestSingleLock()
        {
            var resources = Mocker.MockResourcesWithThree();

            using (var @lock = _lockManager.Lock("Test", resources, TimeSpan.FromSeconds(30)))
            {
                Assert.True(@lock.IsAcquired);
            }
        }
Пример #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
        public static async Task <IEnumerable <TEntity> > CacheAsideAsync <TEntity>(string dimensionKeyName, IEnumerable dimensionKeyValues, Func <IDatabaseReader, Task <IEnumerable <TEntity> > > dbRetrieve,
                                                                                    IDatabase database, ICache cache, IMemoryLockManager memoryLockManager, ILogger logger) where TEntity : Entity, new()
        {
            if (!ICache.IsEntityEnabled <TEntity>())
            {
                return(await dbRetrieve(database).ConfigureAwait(false));
            }

            try
            {
                (IEnumerable <TEntity>?cachedEntities, bool allExists) = await cache.GetEntitiesAsync <TEntity>(dimensionKeyName, dimensionKeyValues).ConfigureAwait(false);

                if (allExists)
                {
                    logger.LogDebug("Cache中全部存在,返回. Entity: {EntityType}", typeof(TEntity).Name);
                    return(cachedEntities !);
                }
            }
            catch (Exception ex)
            {
                //有可能实体定义发生改变,导致缓存读取出错
                logger.LogError2(ex, $"读取缓存出错,缓存可能已经被删除,继续读取数据库,dimensionKeyName:{dimensionKeyName}, dimensionKeyValues:{SerializeUtil.ToJson(dimensionKeyValues)}");
            }

            //常规做法是先获取锁(参看历史)。
            //但如果仅从当前dimension来锁的话,有可能被别人从其他dimension操作同一个entity,
            //所以这里改变常规做法,先做database retrieve

            //以上是针对无version版本cache的。现在不用担心从其他dimension操作同一个entity了,cache会自带version来判断。
            //而且如果刚开始很多请求直接打到数据库上,数据库撑不住,还是得加锁。
            //但可以考虑加单机本版的锁就可,这个锁主要为了降低数据库压力,不再是为了数据一致性(带version的cache自己解决)。
            //所以可以使用单机版本的锁即可。一个主机同时放一个db请求,还是没问题的。

            List <string> resources = new List <string>();

            foreach (object dimensionKeyValue in dimensionKeyValues)
            {
                resources.Add(dimensionKeyName + dimensionKeyValue.ToString());
            }

            using var @lock = memoryLockManager.Lock(typeof(TEntity).Name, resources, Consts.OccupiedTime, Consts.PatienceTime);

            if (@lock.IsAcquired)
            {
                try
                {
                    //Double check
                    (IEnumerable <TEntity>?cachedEntities, bool allExists) = await cache.GetEntitiesAsync <TEntity>(dimensionKeyName, dimensionKeyValues).ConfigureAwait(false);

                    if (allExists)
                    {
                        logger.LogDebug("Cache中全部存在,返回. Entity: {EntityType}", typeof(TEntity).Name);
                        return(cachedEntities !);
                    }
                }
                catch (Exception ex)
                {
                    //有可能实体定义发生改变,导致缓存读取出错
                    logger.LogError2(ex, $"!!!这里是读取缓存的DoubleCheck,这里不应该出现,缓存可能已经被删除,继续读取数据库,dimensionKeyName:{dimensionKeyName}, dimensionKeyValues:{SerializeUtil.ToJson(dimensionKeyValues)}");
                }

                IEnumerable <TEntity> entities = await dbRetrieve(database).ConfigureAwait(false);

                if (entities.IsNotNullOrEmpty())
                {
                    UpdateCache(entities, cache);

                    logger.LogInformation($"缓存 Missed. Entity:{typeof(TEntity).Name}, DimensionKeyName:{dimensionKeyName}, DimensionKeyValues:{dimensionKeyValues.ToJoinedString(",")}");
                }
                else
                {
                    logger.LogInformation($"查询到空值. Entity:{typeof(TEntity).Name}, DimensionKeyName:{dimensionKeyName}, DimensionKeyValues:{dimensionKeyValues.ToJoinedString(",")}");
                }

                return(entities);
            }
            else
            {
                logger.LogError($"锁未能占用. Entity:{typeof(TEntity).Name}, dimensionKeyName:{dimensionKeyName},dimensionKeyValues:{dimensionKeyValues.ToJoinedString(",")}, Lock Status:{@lock.Status}");

                return(await dbRetrieve(database).ConfigureAwait(false));
            }
        }