Skip to content

Commit 486bc4f

Browse files
committed
WIP Separate cache per tenant (prototype for discussion)
1 parent 9575f61 commit 486bc4f

File tree

9 files changed

+87
-29
lines changed

9 files changed

+87
-29
lines changed

src/NHibernate.Test/BulkManipulation/BulkOperationCleanupActionFixture.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,20 +51,20 @@ public void AfterTransactionCompletionProcess_EvictsFromCache(string querySpaces
5151

5252
if (expectedEntityEvictionCount > 0)
5353
{
54-
_factory.Received(1).EvictEntity(Arg.Is<IEnumerable<string>>(x => x.Count() == expectedEntityEvictionCount));
54+
_factory.Received(1).EvictEntity(Arg.Is<IEnumerable<string>>(x => x.Count() == expectedEntityEvictionCount), null);
5555
}
5656
else
5757
{
58-
_factory.DidNotReceive().EvictEntity(Arg.Any<IEnumerable<string>>());
58+
_factory.DidNotReceive().EvictEntity(Arg.Any<IEnumerable<string>>(), null);
5959
}
6060

6161
if (expectedCollectionEvictionCount > 0)
6262
{
63-
_factory.Received(1).EvictCollection(Arg.Is<IEnumerable<string>>(x => x.Count() == expectedCollectionEvictionCount));
63+
_factory.Received(1).EvictCollection(Arg.Is<IEnumerable<string>>(x => x.Count() == expectedCollectionEvictionCount), null);
6464
}
6565
else
6666
{
67-
_factory.DidNotReceive().EvictCollection(Arg.Any<IEnumerable<string>>());
67+
_factory.DidNotReceive().EvictCollection(Arg.Any<IEnumerable<string>>(), null);
6868
}
6969
}
7070
}

src/NHibernate/Action/BulkOperationCleanupAction.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,15 +135,15 @@ private void EvictCollectionRegions()
135135
{
136136
if (affectedCollectionRoles != null && affectedCollectionRoles.Any())
137137
{
138-
session.Factory.EvictCollection(affectedCollectionRoles);
138+
session.Factory.EvictCollection(affectedCollectionRoles, session.GetTenantIdentifier());
139139
}
140140
}
141141

142142
private void EvictEntityRegions()
143143
{
144144
if (affectedEntityNames != null && affectedEntityNames.Any())
145145
{
146-
session.Factory.EvictEntity(affectedEntityNames);
146+
session.Factory.EvictEntity(affectedEntityNames, session.GetTenantIdentifier());
147147
}
148148
}
149149

src/NHibernate/Engine/ISessionImplementor.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ namespace NHibernate.Engine
2121
// 6.0 TODO: Convert to interface methods, excepted SwitchCacheMode
2222
internal static partial class SessionImplementorExtensions
2323
{
24+
//TODO: REMOVE
25+
internal static string GetTenantIdentifier(this ISessionImplementor session)
26+
{
27+
return "defaultTenant";
28+
}
29+
2430
/// <summary>
2531
/// Instantiate the entity class, initializing with the given identifier
2632
/// </summary>

src/NHibernate/Event/Default/DefaultLoadEventListener.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,7 @@ private object AssembleCacheEntry(CacheEntry entry, object id, IEntityPersister
509509
IEventSource session = @event.Session;
510510
ISessionFactoryImplementor factory = session.Factory;
511511

512+
512513
if (log.IsDebugEnabled())
513514
{
514515
log.Debug("assembling entity from second-level cache: {0}", MessageHelper.InfoString(persister, id, factory));

src/NHibernate/ISessionFactory.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@ public static partial class SessionFactoryExtension
2020
/// </summary>
2121
/// <param name="factory">The session factory.</param>
2222
/// <param name="persistentClasses">The classes of the entities to evict.</param>
23-
public static void Evict(this ISessionFactory factory, IEnumerable<System.Type> persistentClasses)
23+
/// <param name="tenantIdentifier">tenant identifier or null if multitenancy is disabled</param>
24+
public static void Evict(this ISessionFactory factory, IEnumerable<System.Type> persistentClasses, string tenantIdentifier)
2425
{
2526
if (factory is SessionFactoryImpl sfi)
2627
{
27-
sfi.Evict(persistentClasses);
28+
sfi.Evict(persistentClasses, tenantIdentifier);
2829
}
2930
else
3031
{
@@ -44,11 +45,12 @@ public static void Evict(this ISessionFactory factory, IEnumerable<System.Type>
4445
/// </summary>
4546
/// <param name="factory">The session factory.</param>
4647
/// <param name="entityNames">The names of the entities to evict.</param>
47-
public static void EvictEntity(this ISessionFactory factory, IEnumerable<string> entityNames)
48+
/// <param name="tenantIdentifier">tenant identifier</param>
49+
public static void EvictEntity(this ISessionFactory factory, IEnumerable<string> entityNames, string tenantIdentifier)
4850
{
4951
if (factory is SessionFactoryImpl sfi)
5052
{
51-
sfi.EvictEntity(entityNames);
53+
sfi.EvictEntity(entityNames, tenantIdentifier);
5254
}
5355
else
5456
{
@@ -68,11 +70,12 @@ public static void EvictEntity(this ISessionFactory factory, IEnumerable<string>
6870
/// </summary>
6971
/// <param name="factory">The session factory.</param>
7072
/// <param name="roleNames">The names of the collections to evict.</param>
71-
public static void EvictCollection(this ISessionFactory factory, IEnumerable<string> roleNames)
73+
/// <param name="tenantIdentifier">tenant identifier</param>
74+
public static void EvictCollection(this ISessionFactory factory, IEnumerable<string> roleNames, string tenantIdentifier)
7275
{
7376
if (factory is SessionFactoryImpl sfi)
7477
{
75-
sfi.EvictCollection(roleNames);
78+
sfi.EvictCollection(roleNames, tenantIdentifier);
7679
}
7780
else
7881
{

src/NHibernate/Impl/SessionFactoryImpl.cs

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Security;
99
using System.Text;
1010
using NHibernate.Cache;
11+
using NHibernate.Cache.Entry;
1112
using NHibernate.Cfg;
1213
using NHibernate.Connection;
1314
using NHibernate.Context;
@@ -232,7 +233,7 @@ public SessionFactoryImpl(Configuration cfg, IMapping mapping, Settings settings
232233

233234
#region Persisters
234235

235-
var caches = new Dictionary<Tuple<string, string>, ICacheConcurrencyStrategy>();
236+
var caches = new ConcurrentDictionary<Tuple<string, string>, ICacheConcurrencyStrategy>();
236237
entityPersisters = new Dictionary<string, IEntityPersister>();
237238
implementorToEntityName = new Dictionary<System.Type, string>();
238239

@@ -426,25 +427,26 @@ private IQueryCache BuildQueryCache(string queryCacheName)
426427
properties);
427428
}
428429

429-
private ICacheConcurrencyStrategy GetCacheConcurrencyStrategy(
430+
private Func<string, ICacheConcurrencyStrategy> GetCacheConcurrencyStrategy(
430431
string cacheRegion,
431432
string strategy,
432433
bool isMutable,
433-
Dictionary<Tuple<string, string>, ICacheConcurrencyStrategy> caches)
434+
ConcurrentDictionary<Tuple<string, string>, ICacheConcurrencyStrategy> caches)
434435
{
435436
if (strategy == null || !settings.IsSecondLevelCacheEnabled)
436437
return null;
437438

438-
var cacheKey = new Tuple<string, string>(cacheRegion, strategy);
439-
if (caches.TryGetValue(cacheKey, out var cache))
440-
return cache;
439+
return tenantIdentifier =>
440+
{
441+
var cache = caches.GetOrAdd(
442+
new Tuple<string, string>(tenantIdentifier + "|" + cacheRegion, strategy),
443+
tuple => CacheFactory.CreateCache(tuple.Item2, GetCache(tuple.Item2)));
441444

442-
cache = CacheFactory.CreateCache(strategy, GetCache(cacheRegion));
443-
caches.Add(cacheKey, cache);
444-
if (isMutable && strategy == CacheFactory.ReadOnly)
445-
log.Warn("read-only cache configured for mutable: {0}", name);
445+
if (isMutable && strategy == CacheFactory.ReadOnly)
446+
log.Warn("read-only cache configured for mutable: {0}", name);
446447

447-
return cache;
448+
return cache;
449+
};
448450
}
449451

450452
public EventListeners EventListeners
@@ -931,11 +933,11 @@ public void Evict(System.Type persistentClass)
931933
}
932934
}
933935

934-
public void Evict(IEnumerable<System.Type> persistentClasses)
936+
public void Evict(IEnumerable<System.Type> persistentClasses, string tenantIdentifier)
935937
{
936938
if (persistentClasses == null)
937939
throw new ArgumentNullException(nameof(persistentClasses));
938-
EvictEntity(persistentClasses.Select(x => x.FullName));
940+
EvictEntity(persistentClasses.Select(x => x.FullName), tenantIdentifier);
939941
}
940942

941943
public void EvictEntity(string entityName)
@@ -951,12 +953,12 @@ public void EvictEntity(string entityName)
951953
}
952954
}
953955

954-
public void EvictEntity(IEnumerable<string> entityNames)
956+
public void EvictEntity(IEnumerable<string> entityNames, string tenantIdentifier)
955957
{
956958
if (entityNames == null)
957959
throw new ArgumentNullException(nameof(entityNames));
958960

959-
foreach (var cacheGroup in entityNames.Select(GetEntityPersister).Where(x => x.HasCache).GroupBy(x => x.Cache))
961+
foreach (var cacheGroup in entityNames.Select(GetEntityPersister).Where(x => x.HasCache).GroupBy(x => x.GetCache(tenantIdentifier)))
960962
{
961963
if (log.IsDebugEnabled())
962964
{
@@ -1022,12 +1024,12 @@ public void EvictCollection(string roleName)
10221024
}
10231025
}
10241026

1025-
public void EvictCollection(IEnumerable<string> roleNames)
1027+
public void EvictCollection(IEnumerable<string> roleNames, string tenantIdentifier)
10261028
{
10271029
if (roleNames == null)
10281030
throw new ArgumentNullException(nameof(roleNames));
10291031

1030-
foreach (var cacheGroup in roleNames.Select(GetCollectionPersister).Where(x => x.HasCache).GroupBy(x => x.Cache))
1032+
foreach (var cacheGroup in roleNames.Select(GetCollectionPersister).Where(x => x.HasCache).GroupBy(x => x.GetCache(tenantIdentifier)))
10311033
{
10321034
if (log.IsDebugEnabled())
10331035
{

src/NHibernate/Persister/Collection/ICollectionPersister.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,5 +304,11 @@ public static int GetBatchSize(this ICollectionPersister persister)
304304

305305
return 1;
306306
}
307+
308+
public static ICacheConcurrencyStrategy GetCache(this ICollectionPersister persister, string tenantIdentifier)
309+
{
310+
//TODO
311+
throw new NotImplementedException();
312+
}
307313
}
308314
}

src/NHibernate/Persister/Entity/IEntityPersister.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,29 @@
1313

1414
namespace NHibernate.Persister.Entity
1515
{
16+
17+
//TODO 6.0: Make base for ICollectionPersister and IPersister
18+
/// <summary>
19+
/// Base interface for cacheable persisters
20+
/// </summary>
21+
public interface ICacheablePersister
22+
{
23+
/// <summary>
24+
/// Get the cache
25+
/// </summary>
26+
/// <param name="tenantIdentifier">tenantIdentifier or null if multi-tenancy is disabled</param>
27+
/// <returns></returns>
28+
ICacheConcurrencyStrategy GetCache(string tenantIdentifier);
29+
30+
/// <summary> Get the cache structure</summary>
31+
ICacheEntryStructure CacheEntryStructure { get;}
32+
33+
/// <summary>
34+
/// Is this persister cacheable
35+
/// </summary>
36+
bool HasCache { get; }
37+
}
38+
1639
public struct EntityPersister
1740
{
1841
/// <summary> The property name of the "special" identifier property in HQL</summary>
@@ -640,5 +663,11 @@ public static void AfterInitialize(this IEntityPersister persister, object entit
640663
persister.AfterInitialize(entity, true, session);
641664
#pragma warning restore 618
642665
}
666+
667+
public static ICacheConcurrencyStrategy GetCache(this IEntityPersister persister, string tenantIdentifier)
668+
{
669+
//TODO
670+
throw new NotImplementedException();
671+
}
643672
}
644673
}

src/NHibernate/Persister/PersisterFactory.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Text;
44
using NHibernate.Cache;
55
using NHibernate.Engine;
6+
using NHibernate.Impl;
67
using NHibernate.Mapping;
78
using NHibernate.Persister.Collection;
89
using NHibernate.Persister.Entity;
@@ -58,6 +59,11 @@ public static IEntityPersister CreateClassPersister(PersistentClass model, ICach
5859
}
5960
}
6061

62+
public static IEntityPersister CreateClassPersister(PersistentClass model, Func<string, ICacheConcurrencyStrategy> cache, SessionFactoryImpl factory, IMapping mapping)
63+
{
64+
throw new NotImplementedException();
65+
}
66+
6167
public static ICollectionPersister CreateCollectionPersister(Mapping.Collection model, ICacheConcurrencyStrategy cache,
6268
ISessionFactoryImplementor factory)
6369
{
@@ -76,6 +82,11 @@ public static ICollectionPersister CreateCollectionPersister(Mapping.Collection
7682
}
7783
}
7884

85+
public static ICollectionPersister CreateCollectionPersister(Mapping.Collection model, Func<string, ICacheConcurrencyStrategy> cache, SessionFactoryImpl factory)
86+
{
87+
throw new NotImplementedException();
88+
}
89+
7990
/// <summary>
8091
/// Creates a specific Persister - could be a built in or custom persister.
8192
/// </summary>

0 commit comments

Comments
 (0)