@@ -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 ( ) ;
@@ -1034,34 +1055,41 @@ public UpdateTimestampsCache UpdateTimestampsCache
10341055
10351056 public IDictionary < string , ICache > GetAllSecondLevelCacheRegions ( )
10361057 {
1037- // ToArray creates a moment in time snapshot
1038- return allCacheRegions . ToArray ( ) . ToDictionary ( kv => kv . Key , kv => kv . Value ) ;
1058+ return
1059+ allCachePerRegionThenType
1060+ // ToArray creates a moment in time snapshot
1061+ . ToArray ( )
1062+ // Caches are not unique per region, take the first one.
1063+ . ToDictionary ( kv => kv . Key , kv => kv . Value . Values . First ( ) ) ;
10391064 }
10401065
10411066 public ICache GetSecondLevelCacheRegion ( string regionName )
10421067 {
1043- ICache result ;
1044- allCacheRegions . TryGetValue ( regionName , out result ) ;
1045- return result ;
1068+ if ( ! allCachePerRegionThenType . TryGetValue ( regionName , out var result ) )
1069+ return null ;
1070+ // Caches are not unique per region, take the first one.
1071+ return result . Values . First ( ) ;
10461072 }
10471073
1048- /// <summary>
1049- /// Get an existing <see cref="ICache"/> or build a new one if not already existing.
1050- /// </summary>
1051- /// <param name="cacheRegion">The (unprefixed) cache region of the cache to get or build.</param>
1052- /// <returns>A cache.</returns>
1053- private ICache GetOrBuild ( string cacheRegion )
1074+ private ICache BuildCache ( string cacheRegion , string type )
10541075 {
1055- // If run concurrently for the same region, this may built many caches for the same region.
1076+ // If run concurrently for the same region and type , this may built many caches for the same region and type .
10561077 // Currently only GetQueryCache may be run concurrently, and its implementation prevents
10571078 // concurrent creation call for the same region, so this will not happen.
10581079 // Otherwise the dictionary will have to be changed for using a lazy, see
10591080 // https://stackoverflow.com/a/31637510/1178314
10601081 var prefix = settings . CacheRegionPrefix ;
10611082 if ( ! string . IsNullOrEmpty ( prefix ) )
10621083 cacheRegion = prefix + '.' + cacheRegion ;
1063- return allCacheRegions . GetOrAdd ( cacheRegion ,
1064- cr => settings . CacheProvider . BuildCache ( cr , properties ) ) ;
1084+ var cachesPerType = allCachePerRegionThenType . GetOrAdd ( cacheRegion , cr => new ConcurrentDictionary < string , ICache > ( ) ) ;
1085+ var cache = settings . CacheProvider . BuildCache ( cacheRegion , properties ) ;
1086+ if ( ! cachesPerType . TryAdd ( type , cache ) )
1087+ {
1088+ cache . Destroy ( ) ;
1089+ throw new InvalidOperationException ( $ "A cache has already been built for region { cacheRegion } and type { type } .") ;
1090+ }
1091+
1092+ return cache ;
10651093 }
10661094
10671095 /// <summary> Statistics SPI</summary>
@@ -1097,7 +1125,7 @@ public IQueryCache GetQueryCache(string cacheRegion)
10971125 updateTimestampsCache ,
10981126 settings ,
10991127 properties ,
1100- GetOrBuild ( cr ) ) ) ) . Value ;
1128+ BuildCache ( cr , QueryCacheType ) ) ) ) . Value ;
11011129 }
11021130
11031131 public void EvictQueries ( )
0 commit comments