Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve the performance of the FusionCacheProvider #249

Merged
merged 8 commits into from
Jun 5, 2024
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
Loading