From 42c7ce68fecfde9f3871bb9a87032def84ce0b1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Wed, 5 Jun 2024 21:05:42 +0200 Subject: [PATCH] Improve the performance of the FusionCacheProvider (#249) --- .../Internals/Provider/FusionCacheProvider.cs | 40 ++++++++---- .../DependencyInjectionTests.cs | 62 +++++++++++++++---- 2 files changed, 77 insertions(+), 25 deletions(-) diff --git a/src/ZiggyCreatures.FusionCache/Internals/Provider/FusionCacheProvider.cs b/src/ZiggyCreatures.FusionCache/Internals/Provider/FusionCacheProvider.cs index 7b54922f..ca07492d 100644 --- a/src/ZiggyCreatures.FusionCache/Internals/Provider/FusionCacheProvider.cs +++ b/src/ZiggyCreatures.FusionCache/Internals/Provider/FusionCacheProvider.cs @@ -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 _caches; public FusionCacheProvider(IEnumerable defaultCaches, IEnumerable 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) diff --git a/tests/ZiggyCreatures.FusionCache.Tests/DependencyInjectionTests.cs b/tests/ZiggyCreatures.FusionCache.Tests/DependencyInjectionTests.cs index d36606dc..5d1881be 100644 --- a/tests/ZiggyCreatures.FusionCache.Tests/DependencyInjectionTests.cs +++ b/tests/ZiggyCreatures.FusionCache.Tests/DependencyInjectionTests.cs @@ -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(); + + var cache1 = cacheProvider.GetDefaultCache(); + var cache2 = cacheProvider.GetDefaultCache(); + var cache3 = serviceProvider.GetRequiredService(); + + Assert.NotNull(cache1); + Assert.NotNull(cache2); + Assert.NotNull(cache3); + Assert.Equal(cache1, cache2); + Assert.Equal(cache2, cache3); + } + [Fact] public void CanUseMultipleNamedCachesAndConfigureThem() { @@ -508,7 +535,7 @@ public void CanUseMultipleNamedCachesAndConfigureThem() using var serviceProvider = services.BuildServiceProvider(); - var cacheProvider = serviceProvider.GetService()!; + var cacheProvider = serviceProvider.GetRequiredService(); var fooCache = cacheProvider.GetCache("FooCache"); var barCache = cacheProvider.GetCache("BarCache"); @@ -574,7 +601,7 @@ public void CanUseDefaultCacheWithMultipleNamedCaches() using var serviceProvider = services.BuildServiceProvider(); - var cacheProvider = serviceProvider.GetService()!; + var cacheProvider = serviceProvider.GetRequiredService(); var fooCache = cacheProvider.GetCache("FooCache"); var barCache = cacheProvider.GetCache("BarCache"); @@ -613,7 +640,7 @@ public void CanUsePostSetupActions() using var serviceProvider = services.BuildServiceProvider(); - var cacheProvider = serviceProvider.GetService()!; + var cacheProvider = serviceProvider.GetRequiredService(); var cache = cacheProvider.GetDefaultCache(); @@ -645,7 +672,7 @@ public void CanResetPostSetupActions() using var serviceProvider = services.BuildServiceProvider(); - var cacheProvider = serviceProvider.GetService()!; + var cacheProvider = serviceProvider.GetRequiredService(); var cache = cacheProvider.GetDefaultCache(); @@ -663,7 +690,7 @@ public void DontThrowWhenRequestingAnUnregisteredCache() using var serviceProvider = services.BuildServiceProvider(); - var cacheProvider = serviceProvider.GetService()!; + var cacheProvider = serviceProvider.GetRequiredService(); Assert.Null(cacheProvider.GetCacheOrNull("BarCache")); } @@ -678,7 +705,7 @@ public void DefaultCacheIsTheSameWhenRequestedInDifferentWays() using var serviceProvider = services.BuildServiceProvider(); - var cacheProvider = serviceProvider.GetService()!; + var cacheProvider = serviceProvider.GetRequiredService(); Assert.Equal(cacheProvider.GetDefaultCache(), serviceProvider.GetService()); } @@ -693,7 +720,7 @@ public void ThrowsOrNotWhenRequestingUnregisteredNamedCaches() using var serviceProvider = services.BuildServiceProvider(); - var cacheProvider = serviceProvider.GetService()!; + var cacheProvider = serviceProvider.GetRequiredService(); Assert.Throws(() => { @@ -717,6 +744,15 @@ public void ThrowsOrNotWhenRequestingUnregisteredNamedCaches() var maybeBarCache = cacheProvider.GetCacheOrNull("Bar"); Assert.Null(maybeBarCache); + + Assert.Throws(() => + { + // NO DEFAULT CACHE REGISTERED -> THROWS + _ = cacheProvider.GetDefaultCache(); + }); + + // NO DEFAULT CACHE REGISTERED -> RETURNS NULL + var defaultCache = cacheProvider.GetDefaultCacheOrNull(); } [Fact] @@ -728,7 +764,7 @@ public void ThrowsOrNotWhenRequestingUnregisteredDefaultCache() using var serviceProvider = services.BuildServiceProvider(); - var cacheProvider = serviceProvider.GetService()!; + var cacheProvider = serviceProvider.GetRequiredService(); Assert.Throws(() => { @@ -753,10 +789,11 @@ public void CacheInstancesAreAlwaysTheSame() using var serviceProvider = services.BuildServiceProvider(); - var cacheProvider = serviceProvider.GetService()!; + var cacheProvider = serviceProvider.GetRequiredService(); var defaultCache1 = cacheProvider.GetDefaultCache(); var defaultCache2 = cacheProvider.GetDefaultCache(); + var defaultCache3 = serviceProvider.GetRequiredService(); var fooCache1 = cacheProvider.GetCache("Foo"); var fooCache2 = cacheProvider.GetCache("Foo"); @@ -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); } @@ -787,7 +825,7 @@ public void DifferentNamedCachesDoNotShareTheSameMemoryCacheByDefault() using var serviceProvider = services.BuildServiceProvider(); - var cacheProvider = serviceProvider.GetService()!; + var cacheProvider = serviceProvider.GetRequiredService(); var defaultCache = cacheProvider.GetDefaultCache(); var fooCache = cacheProvider.GetCache("FooCache"); @@ -826,7 +864,7 @@ public void DifferentNamedCachesCanShareTheSameMemoryCacheWithCollisions() using var serviceProvider = services.BuildServiceProvider(); - var cacheProvider = serviceProvider.GetService()!; + var cacheProvider = serviceProvider.GetRequiredService(); var defaultCache = cacheProvider.GetDefaultCache(); var fooCache = cacheProvider.GetCache("FooCache"); @@ -865,7 +903,7 @@ public void DifferentNamedCachesCanShareTheSameMemoryCacheWithoutCollisions() using var serviceProvider = services.BuildServiceProvider(); - var cacheProvider = serviceProvider.GetService()!; + var cacheProvider = serviceProvider.GetRequiredService(); var defaultCache = cacheProvider.GetDefaultCache(); var fooCache = cacheProvider.GetCache("FooCache");