@@ -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 ( ) ;
@@ -1034,34 +1055,41 @@ public UpdateTimestampsCache UpdateTimestampsCache
1034
1055
1035
1056
public IDictionary < string , ICache > GetAllSecondLevelCacheRegions ( )
1036
1057
{
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 ( ) ) ;
1039
1064
}
1040
1065
1041
1066
public ICache GetSecondLevelCacheRegion ( string regionName )
1042
1067
{
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 ( ) ;
1046
1072
}
1047
1073
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 )
1054
1075
{
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 .
1056
1077
// Currently only GetQueryCache may be run concurrently, and its implementation prevents
1057
1078
// concurrent creation call for the same region, so this will not happen.
1058
1079
// Otherwise the dictionary will have to be changed for using a lazy, see
1059
1080
// https://stackoverflow.com/a/31637510/1178314
1060
1081
var prefix = settings . CacheRegionPrefix ;
1061
1082
if ( ! string . IsNullOrEmpty ( prefix ) )
1062
1083
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 ;
1065
1093
}
1066
1094
1067
1095
/// <summary> Statistics SPI</summary>
@@ -1097,7 +1125,7 @@ public IQueryCache GetQueryCache(string cacheRegion)
1097
1125
updateTimestampsCache ,
1098
1126
settings ,
1099
1127
properties ,
1100
- GetOrBuild ( cr ) ) ) ) . Value ;
1128
+ BuildCache ( cr , QueryCacheType ) ) ) ) . Value ;
1101
1129
}
1102
1130
1103
1131
public void EvictQueries ( )
0 commit comments