@@ -95,8 +95,8 @@ public void HandleEntityNotFound(string entityName, object id)
95
95
private static readonly IIdentifierGenerator UuidGenerator = new UUIDHexGenerator ( ) ;
96
96
97
97
[ 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 > > ( ) ;
100
100
101
101
[ NonSerialized ]
102
102
private readonly IDictionary < string , IClassMetadata > classMetadata ;
@@ -148,6 +148,8 @@ public void HandleEntityNotFound(string entityName, object id)
148
148
[ NonSerialized ]
149
149
private readonly IQueryCache queryCache ;
150
150
151
+ private const string QueryCacheType = "QueryCache" ;
152
+
151
153
[ NonSerialized ]
152
154
private readonly ConcurrentDictionary < string , Lazy < IQueryCache > > queryCaches ;
153
155
[ NonSerialized ]
@@ -232,6 +234,7 @@ public SessionFactoryImpl(Configuration cfg, IMapping mapping, Settings settings
232
234
233
235
#region Persisters
234
236
237
+ var caches = new Dictionary < Tuple < string , string > , ICacheConcurrencyStrategy > ( ) ;
235
238
entityPersisters = new Dictionary < string , IEntityPersister > ( ) ;
236
239
implementorToEntityName = new Dictionary < System . Type , string > ( ) ;
237
240
@@ -240,14 +243,11 @@ public SessionFactoryImpl(Configuration cfg, IMapping mapping, Settings settings
240
243
foreach ( PersistentClass model in cfg . ClassMappings )
241
244
{
242
245
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 ,
248
249
model . IsMutable ,
249
- settings ,
250
- GetOrBuild ) ;
250
+ caches ) ;
251
251
var cp = PersisterFactory . CreateClassPersister ( model , cache , this , mapping ) ;
252
252
entityPersisters [ model . EntityName ] = cp ;
253
253
classMeta [ model . EntityName ] = cp . ClassMetadata ;
@@ -263,13 +263,11 @@ public SessionFactoryImpl(Configuration cfg, IMapping mapping, Settings settings
263
263
collectionPersisters = new Dictionary < string , ICollectionPersister > ( ) ;
264
264
foreach ( Mapping . Collection model in cfg . CollectionMappings )
265
265
{
266
- var cacheRegion = model . CacheRegionName ;
267
- var cache = CacheFactory . CreateCache (
266
+ var cache = GetCacheConcurrencyStrategy (
267
+ model . CacheRegionName ,
268
268
model . CacheConcurrencyStrategy ,
269
- cacheRegion ,
270
269
model . Owner . IsMutable ,
271
- settings ,
272
- GetOrBuild ) ;
270
+ caches ) ;
273
271
var persister = PersisterFactory . CreateCollectionPersister ( model , cache , this ) ;
274
272
collectionPersisters [ model . Role ] = persister ;
275
273
IType indexType = persister . IndexType ;
@@ -371,16 +369,17 @@ public SessionFactoryImpl(Configuration cfg, IMapping mapping, Settings settings
371
369
372
370
if ( settings . IsQueryCacheEnabled )
373
371
{
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 ) ) ;
376
374
var queryCacheName = typeof ( StandardQueryCache ) . FullName ;
377
375
queryCache = settings . QueryCacheFactory . GetQueryCache (
378
376
queryCacheName ,
379
377
updateTimestampsCache ,
380
378
settings ,
381
379
properties ,
382
- GetOrBuild ( queryCacheName ) ) ;
380
+ BuildCache ( queryCacheName , QueryCacheType ) ) ;
383
381
queryCaches = new ConcurrentDictionary < string , Lazy < IQueryCache > > ( ) ;
382
+ queryCaches . TryAdd ( queryCacheName , new Lazy < IQueryCache > ( ( ) => queryCache ) ) ;
384
383
}
385
384
else
386
385
{
@@ -417,6 +416,30 @@ public SessionFactoryImpl(Configuration cfg, IMapping mapping, Settings settings
417
416
entityNotFoundDelegate = enfd ;
418
417
}
419
418
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
+
420
443
public EventListeners EventListeners
421
444
{
422
445
get { return eventListeners ; }
@@ -844,8 +867,6 @@ public void Close()
844
867
845
868
if ( settings . IsQueryCacheEnabled )
846
869
{
847
- queryCache . Destroy ( ) ;
848
-
849
870
foreach ( var cache in queryCaches . Values )
850
871
{
851
872
cache . Value . Destroy ( ) ;
@@ -995,34 +1016,41 @@ public UpdateTimestampsCache UpdateTimestampsCache
995
1016
996
1017
public IDictionary < string , ICache > GetAllSecondLevelCacheRegions ( )
997
1018
{
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 ( ) ) ;
1000
1025
}
1001
1026
1002
1027
public ICache GetSecondLevelCacheRegion ( string regionName )
1003
1028
{
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 ( ) ;
1007
1033
}
1008
1034
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 )
1015
1036
{
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 .
1017
1038
// Currently only GetQueryCache may be run concurrently, and its implementation prevents
1018
1039
// concurrent creation call for the same region, so this will not happen.
1019
1040
// Otherwise the dictionary will have to be changed for using a lazy, see
1020
1041
// https://stackoverflow.com/a/31637510/1178314
1021
1042
var prefix = settings . CacheRegionPrefix ;
1022
1043
if ( ! string . IsNullOrEmpty ( prefix ) )
1023
1044
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 ;
1026
1054
}
1027
1055
1028
1056
/// <summary> Statistics SPI</summary>
@@ -1058,7 +1086,7 @@ public IQueryCache GetQueryCache(string cacheRegion)
1058
1086
updateTimestampsCache ,
1059
1087
settings ,
1060
1088
properties ,
1061
- GetOrBuild ( cr ) ) ) ) . Value ;
1089
+ BuildCache ( cr , QueryCacheType ) ) ) ) . Value ;
1062
1090
}
1063
1091
1064
1092
public void EvictQueries ( )
0 commit comments