Skip to content

Commit

Permalink
Improve the performance of the FusionCacheProvider (#249)
Browse files Browse the repository at this point in the history
  • Loading branch information
0xced authored Jun 5, 2024
1 parent a4a43c2 commit 42c7ce6
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,43 @@ namespace ZiggyCreatures.Caching.Fusion.Internals.Provider;
internal sealed class FusionCacheProvider
: IFusionCacheProvider
{
private readonly IFusionCache? _defaultCache;
private readonly LazyNamedCache[] _lazyNamedCaches;
private readonly Dictionary<string, LazyNamedCache?> _caches;

public FusionCacheProvider(IEnumerable<IFusionCache> defaultCaches, IEnumerable<LazyNamedCache> lazyNamedCaches)
{
_defaultCache = defaultCaches.LastOrDefault();
_lazyNamedCaches = lazyNamedCaches.ToArray();
_caches = [];
foreach (var group in lazyNamedCaches.GroupBy(g => g.CacheName))
{
if (group.Count() == 1)
{
// ONLY 1 CACHE -> ADD IT
_caches[group.Key] = group.First();
}
else
{
// MORE THAN 1 CACHE -> ADD NULL
// NOTE: THIS WILL SIGNAL THAT THERE WERE MULTIPLE ONES AND, SINCE
// THEY WILL NOT BE ACCESSIBLE ANYWAY, WILL SAVE SOME MEMORY
_caches[group.Key] = null;
}
}

var defaultCache = defaultCaches.LastOrDefault();
if (defaultCache is not null)
{
_caches[defaultCache.CacheName] = new LazyNamedCache(defaultCache.CacheName, defaultCache);
}
}

public IFusionCache? GetCacheOrNull(string cacheName)
{
if (cacheName == FusionCacheOptions.DefaultCacheName)
return _defaultCache;
if (_caches.TryGetValue(cacheName, out var item) == false)
return null;

var matchingLazyNamedCaches = _lazyNamedCaches.Where(x => x.CacheName == cacheName).ToArray();

if (matchingLazyNamedCaches.Length == 1)
return matchingLazyNamedCaches[0].Cache;

if (matchingLazyNamedCaches.Length > 1)
if (item is null)
throw new InvalidOperationException($"Multiple FusionCache registrations have been found with the provided name ({cacheName})");

return null;
return item.Cache;
}

public IFusionCache GetCache(string cacheName)
Expand Down
62 changes: 50 additions & 12 deletions tests/ZiggyCreatures.FusionCache.Tests/DependencyInjectionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,33 @@ public void ThrowsIfMissingSerializerWhenUsingDistributedCache()
});
}

[Fact]
public void CanRegisterMultipleDefaultCaches()
{
// NOTE: EVEN THOUGH IT'S POSSIBLE TO REGISTER MULTIPLE DEFAULT CACHES, IT'S NOT RECOMMENDED,
// AND IT'S NOT POSSIBLE TO USE THEM IN A MEANINGFUL WAY, AS THE LAST ONE REGISTERED WILL BE THE ONE USED.
// THIS FOLLOWS THE STANDARD BEHAVIOR OF MICROSOFT'S DI CONTAINER.

var services = new ServiceCollection();

services.AddFusionCache();
services.AddFusionCache();

using var serviceProvider = services.BuildServiceProvider();

var cacheProvider = serviceProvider.GetRequiredService<IFusionCacheProvider>();

var cache1 = cacheProvider.GetDefaultCache();
var cache2 = cacheProvider.GetDefaultCache();
var cache3 = serviceProvider.GetRequiredService<IFusionCache>();

Assert.NotNull(cache1);
Assert.NotNull(cache2);
Assert.NotNull(cache3);
Assert.Equal(cache1, cache2);
Assert.Equal(cache2, cache3);
}

[Fact]
public void CanUseMultipleNamedCachesAndConfigureThem()
{
Expand Down Expand Up @@ -508,7 +535,7 @@ public void CanUseMultipleNamedCachesAndConfigureThem()

using var serviceProvider = services.BuildServiceProvider();

var cacheProvider = serviceProvider.GetService<IFusionCacheProvider>()!;
var cacheProvider = serviceProvider.GetRequiredService<IFusionCacheProvider>();

var fooCache = cacheProvider.GetCache("FooCache");
var barCache = cacheProvider.GetCache("BarCache");
Expand Down Expand Up @@ -574,7 +601,7 @@ public void CanUseDefaultCacheWithMultipleNamedCaches()

using var serviceProvider = services.BuildServiceProvider();

var cacheProvider = serviceProvider.GetService<IFusionCacheProvider>()!;
var cacheProvider = serviceProvider.GetRequiredService<IFusionCacheProvider>();

var fooCache = cacheProvider.GetCache("FooCache");
var barCache = cacheProvider.GetCache("BarCache");
Expand Down Expand Up @@ -613,7 +640,7 @@ public void CanUsePostSetupActions()

using var serviceProvider = services.BuildServiceProvider();

var cacheProvider = serviceProvider.GetService<IFusionCacheProvider>()!;
var cacheProvider = serviceProvider.GetRequiredService<IFusionCacheProvider>();

var cache = cacheProvider.GetDefaultCache();

Expand Down Expand Up @@ -645,7 +672,7 @@ public void CanResetPostSetupActions()

using var serviceProvider = services.BuildServiceProvider();

var cacheProvider = serviceProvider.GetService<IFusionCacheProvider>()!;
var cacheProvider = serviceProvider.GetRequiredService<IFusionCacheProvider>();

var cache = cacheProvider.GetDefaultCache();

Expand All @@ -663,7 +690,7 @@ public void DontThrowWhenRequestingAnUnregisteredCache()

using var serviceProvider = services.BuildServiceProvider();

var cacheProvider = serviceProvider.GetService<IFusionCacheProvider>()!;
var cacheProvider = serviceProvider.GetRequiredService<IFusionCacheProvider>();

Assert.Null(cacheProvider.GetCacheOrNull("BarCache"));
}
Expand All @@ -678,7 +705,7 @@ public void DefaultCacheIsTheSameWhenRequestedInDifferentWays()

using var serviceProvider = services.BuildServiceProvider();

var cacheProvider = serviceProvider.GetService<IFusionCacheProvider>()!;
var cacheProvider = serviceProvider.GetRequiredService<IFusionCacheProvider>();

Assert.Equal(cacheProvider.GetDefaultCache(), serviceProvider.GetService<IFusionCache>());
}
Expand All @@ -693,7 +720,7 @@ public void ThrowsOrNotWhenRequestingUnregisteredNamedCaches()

using var serviceProvider = services.BuildServiceProvider();

var cacheProvider = serviceProvider.GetService<IFusionCacheProvider>()!;
var cacheProvider = serviceProvider.GetRequiredService<IFusionCacheProvider>();

Assert.Throws<InvalidOperationException>(() =>
{
Expand All @@ -717,6 +744,15 @@ public void ThrowsOrNotWhenRequestingUnregisteredNamedCaches()
var maybeBarCache = cacheProvider.GetCacheOrNull("Bar");

Assert.Null(maybeBarCache);

Assert.Throws<InvalidOperationException>(() =>
{
// NO DEFAULT CACHE REGISTERED -> THROWS
_ = cacheProvider.GetDefaultCache();
});

// NO DEFAULT CACHE REGISTERED -> RETURNS NULL
var defaultCache = cacheProvider.GetDefaultCacheOrNull();
}

[Fact]
Expand All @@ -728,7 +764,7 @@ public void ThrowsOrNotWhenRequestingUnregisteredDefaultCache()

using var serviceProvider = services.BuildServiceProvider();

var cacheProvider = serviceProvider.GetService<IFusionCacheProvider>()!;
var cacheProvider = serviceProvider.GetRequiredService<IFusionCacheProvider>();

Assert.Throws<InvalidOperationException>(() =>
{
Expand All @@ -753,10 +789,11 @@ public void CacheInstancesAreAlwaysTheSame()

using var serviceProvider = services.BuildServiceProvider();

var cacheProvider = serviceProvider.GetService<IFusionCacheProvider>()!;
var cacheProvider = serviceProvider.GetRequiredService<IFusionCacheProvider>();

var defaultCache1 = cacheProvider.GetDefaultCache();
var defaultCache2 = cacheProvider.GetDefaultCache();
var defaultCache3 = serviceProvider.GetRequiredService<IFusionCache>();

var fooCache1 = cacheProvider.GetCache("Foo");
var fooCache2 = cacheProvider.GetCache("Foo");
Expand All @@ -765,6 +802,7 @@ public void CacheInstancesAreAlwaysTheSame()
var barCache2 = cacheProvider.GetCache("Bar");

Assert.Same(defaultCache1, defaultCache2);
Assert.Same(defaultCache2, defaultCache3);
Assert.Same(fooCache1, fooCache2);
Assert.Same(barCache1, barCache2);
}
Expand All @@ -787,7 +825,7 @@ public void DifferentNamedCachesDoNotShareTheSameMemoryCacheByDefault()

using var serviceProvider = services.BuildServiceProvider();

var cacheProvider = serviceProvider.GetService<IFusionCacheProvider>()!;
var cacheProvider = serviceProvider.GetRequiredService<IFusionCacheProvider>();

var defaultCache = cacheProvider.GetDefaultCache();
var fooCache = cacheProvider.GetCache("FooCache");
Expand Down Expand Up @@ -826,7 +864,7 @@ public void DifferentNamedCachesCanShareTheSameMemoryCacheWithCollisions()

using var serviceProvider = services.BuildServiceProvider();

var cacheProvider = serviceProvider.GetService<IFusionCacheProvider>()!;
var cacheProvider = serviceProvider.GetRequiredService<IFusionCacheProvider>();

var defaultCache = cacheProvider.GetDefaultCache();
var fooCache = cacheProvider.GetCache("FooCache");
Expand Down Expand Up @@ -865,7 +903,7 @@ public void DifferentNamedCachesCanShareTheSameMemoryCacheWithoutCollisions()

using var serviceProvider = services.BuildServiceProvider();

var cacheProvider = serviceProvider.GetService<IFusionCacheProvider>()!;
var cacheProvider = serviceProvider.GetRequiredService<IFusionCacheProvider>();

var defaultCache = cacheProvider.GetDefaultCache();
var fooCache = cacheProvider.GetCache("FooCache");
Expand Down

0 comments on commit 42c7ce6

Please sign in to comment.