@@ -95,8 +95,8 @@ public void HandleEntityNotFound(string entityName, object id)
9595 private static readonly IIdentifierGenerator UuidGenerator = new UUIDHexGenerator ( ) ;
9696
9797 [ NonSerialized ]
98- private readonly ConcurrentDictionary < string , ICache > allCacheRegions =
99- new ConcurrentDictionary < string , ICache > ( ) ;
98+ private readonly ConcurrentDictionary < string , ConcurrentDictionary < string , ICache > > allCachePerRegionThenType =
99+ new ConcurrentDictionary < string , ConcurrentDictionary < string , ICache > > ( ) ;
100100
101101 [ NonSerialized ]
102102 private readonly IDictionary < string , IClassMetadata > classMetadata ;
@@ -148,6 +148,8 @@ public void HandleEntityNotFound(string entityName, object id)
148148 [ NonSerialized ]
149149 private readonly IQueryCache queryCache ;
150150
151+ private const string QueryCacheType = "QueryCache" ;
152+
151153 [ NonSerialized ]
152154 private readonly ConcurrentDictionary < string , Lazy < IQueryCache > > queryCaches ;
153155 [ NonSerialized ]
@@ -232,6 +234,7 @@ public SessionFactoryImpl(Configuration cfg, IMapping mapping, Settings settings
232234
233235 #region Persisters
234236
237+ var caches = new Dictionary < Tuple < string , string > , ICacheConcurrencyStrategy > ( ) ;
235238 entityPersisters = new Dictionary < string , IEntityPersister > ( ) ;
236239 implementorToEntityName = new Dictionary < System . Type , string > ( ) ;
237240
@@ -240,14 +243,11 @@ public SessionFactoryImpl(Configuration cfg, IMapping mapping, Settings settings
240243 foreach ( PersistentClass model in cfg . ClassMappings )
241244 {
242245 model . PrepareTemporaryTables ( mapping , settings . Dialect ) ;
243- var cacheRegion = model . RootClazz . CacheRegionName ;
244- var strategy = model . CacheConcurrencyStrategy ;
245- var cache = CacheFactory . CreateCache (
246- strategy ,
247- cacheRegion ,
246+ var cache = GetCacheConcurrencyStrategy (
247+ model . RootClazz . CacheRegionName ,
248+ model . CacheConcurrencyStrategy ,
248249 model . IsMutable ,
249- settings ,
250- GetOrBuild ) ;
250+ caches ) ;
251251 var cp = PersisterFactory . CreateClassPersister ( model , cache , this , mapping ) ;
252252 entityPersisters [ model . EntityName ] = cp ;
253253 classMeta [ model . EntityName ] = cp . ClassMetadata ;
@@ -263,13 +263,11 @@ public SessionFactoryImpl(Configuration cfg, IMapping mapping, Settings settings
263263 collectionPersisters = new Dictionary < string , ICollectionPersister > ( ) ;
264264 foreach ( Mapping . Collection model in cfg . CollectionMappings )
265265 {
266- var cacheRegion = model . CacheRegionName ;
267- var cache = CacheFactory . CreateCache (
266+ var cache = GetCacheConcurrencyStrategy (
267+ model . CacheRegionName ,
268268 model . CacheConcurrencyStrategy ,
269- cacheRegion ,
270269 model . Owner . IsMutable ,
271- settings ,
272- GetOrBuild ) ;
270+ caches ) ;
273271 var persister = PersisterFactory . CreateCollectionPersister ( model , cache , this ) ;
274272 collectionPersisters [ model . Role ] = persister ;
275273 IType indexType = persister . IndexType ;
@@ -371,16 +369,17 @@ public SessionFactoryImpl(Configuration cfg, IMapping mapping, Settings settings
371369
372370 if ( settings . IsQueryCacheEnabled )
373371 {
374- var updateTimestampsCacheName = typeof ( StandardQueryCache ) . Name ;
375- updateTimestampsCache = new UpdateTimestampsCache ( GetOrBuild ( updateTimestampsCacheName ) ) ;
372+ var updateTimestampsCacheName = typeof ( UpdateTimestampsCache ) . Name ;
373+ updateTimestampsCache = new UpdateTimestampsCache ( BuildCache ( updateTimestampsCacheName , updateTimestampsCacheName ) ) ;
376374 var queryCacheName = typeof ( StandardQueryCache ) . FullName ;
377375 queryCache = settings . QueryCacheFactory . GetQueryCache (
378376 queryCacheName ,
379377 updateTimestampsCache ,
380378 settings ,
381379 properties ,
382- GetOrBuild ( queryCacheName ) ) ;
380+ BuildCache ( queryCacheName , QueryCacheType ) ) ;
383381 queryCaches = new ConcurrentDictionary < string , Lazy < IQueryCache > > ( ) ;
382+ queryCaches . TryAdd ( queryCacheName , new Lazy < IQueryCache > ( ( ) => queryCache ) ) ;
384383 }
385384 else
386385 {
@@ -417,6 +416,30 @@ public SessionFactoryImpl(Configuration cfg, IMapping mapping, Settings settings
417416 entityNotFoundDelegate = enfd ;
418417 }
419418
419+ private ICacheConcurrencyStrategy GetCacheConcurrencyStrategy (
420+ string cacheRegion ,
421+ string strategy ,
422+ bool isMutable ,
423+ Dictionary < Tuple < string , string > , ICacheConcurrencyStrategy > caches )
424+ {
425+ var cacheKey = new Tuple < string , string > ( cacheRegion , strategy ) ;
426+ if ( ! caches . TryGetValue ( cacheKey , out var cache ) )
427+ {
428+ cache = CacheFactory . CreateCache (
429+ strategy ,
430+ cacheRegion ,
431+ settings ,
432+ BuildCache ) ;
433+ if ( cache != null )
434+ caches . Add ( cacheKey , cache ) ;
435+ }
436+
437+ if ( cache != null && isMutable && strategy == CacheFactory . ReadOnly )
438+ log . Warn ( "read-only cache configured for mutable: {0}" , name ) ;
439+
440+ return cache ;
441+ }
442+
420443 public EventListeners EventListeners
421444 {
422445 get { return eventListeners ; }
@@ -844,8 +867,6 @@ public void Close()
844867
845868 if ( settings . IsQueryCacheEnabled )
846869 {
847- queryCache . Destroy ( ) ;
848-
849870 foreach ( var cache in queryCaches . Values )
850871 {
851872 cache . Value . Destroy ( ) ;
@@ -995,34 +1016,41 @@ public UpdateTimestampsCache UpdateTimestampsCache
9951016
9961017 public IDictionary < string , ICache > GetAllSecondLevelCacheRegions ( )
9971018 {
998- // ToArray creates a moment in time snapshot
999- return allCacheRegions . ToArray ( ) . ToDictionary ( kv => kv . Key , kv => kv . Value ) ;
1019+ return
1020+ allCachePerRegionThenType
1021+ // ToArray creates a moment in time snapshot
1022+ . ToArray ( )
1023+ // Caches are not unique per region, take the first one.
1024+ . ToDictionary ( kv => kv . Key , kv => kv . Value . Values . First ( ) ) ;
10001025 }
10011026
10021027 public ICache GetSecondLevelCacheRegion ( string regionName )
10031028 {
1004- ICache result ;
1005- allCacheRegions . TryGetValue ( regionName , out result ) ;
1006- return result ;
1029+ if ( ! allCachePerRegionThenType . TryGetValue ( regionName , out var result ) )
1030+ return null ;
1031+ // Caches are not unique per region, take the first one.
1032+ return result . Values . First ( ) ;
10071033 }
10081034
1009- /// <summary>
1010- /// Get an existing <see cref="ICache"/> or build a new one if not already existing.
1011- /// </summary>
1012- /// <param name="cacheRegion">The (unprefixed) cache region of the cache to get or build.</param>
1013- /// <returns>A cache.</returns>
1014- private ICache GetOrBuild ( string cacheRegion )
1035+ private ICache BuildCache ( string cacheRegion , string type )
10151036 {
1016- // If run concurrently for the same region, this may built many caches for the same region.
1037+ // If run concurrently for the same region and type , this may built many caches for the same region and type .
10171038 // Currently only GetQueryCache may be run concurrently, and its implementation prevents
10181039 // concurrent creation call for the same region, so this will not happen.
10191040 // Otherwise the dictionary will have to be changed for using a lazy, see
10201041 // https://stackoverflow.com/a/31637510/1178314
10211042 var prefix = settings . CacheRegionPrefix ;
10221043 if ( ! string . IsNullOrEmpty ( prefix ) )
10231044 cacheRegion = prefix + '.' + cacheRegion ;
1024- return allCacheRegions . GetOrAdd ( cacheRegion ,
1025- cr => settings . CacheProvider . BuildCache ( cr , properties ) ) ;
1045+ var cachesPerType = allCachePerRegionThenType . GetOrAdd ( cacheRegion , cr => new ConcurrentDictionary < string , ICache > ( ) ) ;
1046+ var cache = settings . CacheProvider . BuildCache ( cacheRegion , properties ) ;
1047+ if ( ! cachesPerType . TryAdd ( type , cache ) )
1048+ {
1049+ cache . Destroy ( ) ;
1050+ throw new InvalidOperationException ( $ "A cache has already been built for region { cacheRegion } and type { type } .") ;
1051+ }
1052+
1053+ return cache ;
10261054 }
10271055
10281056 /// <summary> Statistics SPI</summary>
@@ -1058,7 +1086,7 @@ public IQueryCache GetQueryCache(string cacheRegion)
10581086 updateTimestampsCache ,
10591087 settings ,
10601088 properties ,
1061- GetOrBuild ( cr ) ) ) ) . Value ;
1089+ BuildCache ( cr , QueryCacheType ) ) ) ) . Value ;
10621090 }
10631091
10641092 public void EvictQueries ( )
0 commit comments