From 64caa6b55a1319033c261dd2e557098e45c11f43 Mon Sep 17 00:00:00 2001 From: Mario Alberto Arce Date: Thu, 20 Aug 2026 13:42:14 -0600 Subject: [PATCH 1/6] fix(cache): log resolved cache implementations during pipeline setup - Clarify that module-stage logging uses a placeholder logger before the DI container is built - Resolve memory and disk cache services from the completed container during pipeline configuration - Log the concrete implementations actually bound to cache service contracts --- .../CacheFeatureModule.cs | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/Features/PowerCSharp.Feature.Cache/CacheFeatureModule.cs b/src/Features/PowerCSharp.Feature.Cache/CacheFeatureModule.cs index 7e33b5b..746665d 100644 --- a/src/Features/PowerCSharp.Feature.Cache/CacheFeatureModule.cs +++ b/src/Features/PowerCSharp.Feature.Cache/CacheFeatureModule.cs @@ -31,10 +31,14 @@ public void ConfigureServices(IFeatureRegistrationContext context) { // Force-load the abstractions assembly so the CLR can resolve its types during discovery. _ = Assembly.Load("PowerCSharp.Feature.Cache.Abstractions"); - + context.Services.Configure( context.Configuration.GetSection($"PowerFeatures:{Key}")); + // NOTE: context.Logger is NullLogger at this stage (the DI container isn't built yet, so + // no real ILoggerFactory exists). Anything logged here is silently discarded. The + // authoritative "which implementation actually won" diagnostic is logged for real in + // ConfigurePipeline below, once a real logger is available. if (!context.Flags.IsEnabled(FeatureKey)) { context.Logger.LogInformation("Cache feature disabled; registering NoOp cache services."); @@ -45,4 +49,26 @@ public void ConfigureServices(IFeatureRegistrationContext context) context.Services.TryAddSingleton(); context.Services.TryAddSingleton(); } + + /// + /// + /// Diagnostic-only: resolves the services actually bound to and + /// from the fully-built container and logs their concrete + /// types with a real logger. This is the authoritative answer to "which cache implementation + /// is actually active" — unlike constructor-time logs from individual implementations (which + /// can fire for shadowed/overridden registrations too, e.g. under ASP.NET Core's + /// ValidateOnBuild in Development, without ever being the instance that's actually injected). + /// + public void ConfigurePipeline(IFeaturePipelineContext context) + { + var services = context.App.ApplicationServices; + var cache = services.GetRequiredService(); + var diskCache = services.GetRequiredService(); + + context.Logger.LogInformation( + "Cache feature resolved -> ICacheService: {CacheImplementation}; IDiskCacheService: {DiskCacheImplementation}. " + + "A NoOp implementation here means no provider package overrode the safe-off floor for that contract.", + cache.GetType().FullName, + diskCache.GetType().FullName); + } } From 971563c5986dd9f63aea99f78badb8facd8e0a31 Mon Sep 17 00:00:00 2001 From: Mario Alberto Arce Date: Thu, 20 Aug 2026 13:42:36 -0600 Subject: [PATCH 2/6] fix(cache): clarify NoOp cache construction logging - Explain that DI validation may construct the NoOp floor even when a real provider is active - Rename the log message to identify safe-off construction without implying service resolution --- .../NoOp/NoOpCacheService.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Features/PowerCSharp.Feature.Cache.Abstractions/NoOp/NoOpCacheService.cs b/src/Features/PowerCSharp.Feature.Cache.Abstractions/NoOp/NoOpCacheService.cs index 99b08dc..2ad548f 100644 --- a/src/Features/PowerCSharp.Feature.Cache.Abstractions/NoOp/NoOpCacheService.cs +++ b/src/Features/PowerCSharp.Feature.Cache.Abstractions/NoOp/NoOpCacheService.cs @@ -14,7 +14,13 @@ public sealed class NoOpCacheService : ICacheService /// Creates the NoOp cache and logs that caching is inert. public NoOpCacheService(ILogger logger) { - logger.LogInformation("Cache feature is disabled or unconfigured; using NoOp in-memory cache."); + // NOTE: this constructor can run even when a real provider (e.g. BitFaster) is the one + // actually injected as ICacheService — ASP.NET Core's ValidateOnBuild (on by default in + // Development) constructs every registered descriptor, including this shadowed safe-off + // floor, purely to validate the DI graph. Seeing this log does not by itself mean the app + // is using NoOp; check the Cache feature's "Cache feature resolved" diagnostic line for + // the implementation actually bound to ICacheService. + logger.LogInformation("NoOp in-memory cache constructed (ICacheService safe-off floor)."); } /// From f8128d5718751188a46a87cec82cc5e248ff417f Mon Sep 17 00:00:00 2001 From: Mario Alberto Arce Date: Thu, 20 Aug 2026 13:43:02 -0600 Subject: [PATCH 3/6] fix(cache): clarify NoOp disk cache construction logging - Explain that DI validation may construct the NoOp disk cache floor even when a real provider is active - Rename the log message to identify safe-off construction without implying disk cache resolution --- .../NoOp/NoOpDiskCacheService.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Features/PowerCSharp.Feature.Cache.Abstractions/NoOp/NoOpDiskCacheService.cs b/src/Features/PowerCSharp.Feature.Cache.Abstractions/NoOp/NoOpDiskCacheService.cs index 0afbaed..0fd34d1 100644 --- a/src/Features/PowerCSharp.Feature.Cache.Abstractions/NoOp/NoOpDiskCacheService.cs +++ b/src/Features/PowerCSharp.Feature.Cache.Abstractions/NoOp/NoOpDiskCacheService.cs @@ -14,7 +14,11 @@ public sealed class NoOpDiskCacheService : IDiskCacheService /// Creates the NoOp disk cache and logs that disk caching is inert. public NoOpDiskCacheService(ILogger logger) { - logger.LogInformation("Cache feature is disabled or unconfigured; using NoOp disk cache."); + // NOTE: see the matching comment in NoOpCacheService — this can be constructed by + // ASP.NET Core's ValidateOnBuild even when a real provider (e.g. Disk) is the one + // actually injected as IDiskCacheService. Check the Cache feature's "Cache feature + // resolved" diagnostic line for the implementation actually in use. + logger.LogInformation("NoOp disk cache constructed (IDiskCacheService safe-off floor)."); } /// From 02f54e35ca140a38cb0ab350af17b92b11d7884b Mon Sep 17 00:00:00 2001 From: Mario Alberto Arce Date: Thu, 20 Aug 2026 13:43:23 -0600 Subject: [PATCH 4/6] fix(cache): add BitFaster cache initialization diagnostics - Inject a logger into the BitFaster cache service - Log configured cache capacity during initialization - Clarify that constructor execution does not prove the provider is the active DI implementation --- .../BitFasterCacheService.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Features/PowerCSharp.Feature.Cache.BitFaster/BitFasterCacheService.cs b/src/Features/PowerCSharp.Feature.Cache.BitFaster/BitFasterCacheService.cs index f4c3aa1..04c0d6a 100644 --- a/src/Features/PowerCSharp.Feature.Cache.BitFaster/BitFasterCacheService.cs +++ b/src/Features/PowerCSharp.Feature.Cache.BitFaster/BitFasterCacheService.cs @@ -1,6 +1,7 @@ using System.Collections.Concurrent; using System.Diagnostics; using BitFaster.Caching.Lru; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using PowerCSharp.Feature.Cache.Abstractions; @@ -20,10 +21,16 @@ public sealed class BitFasterCacheService : ICacheService private readonly ConcurrentDictionary _metadata = new(); /// Creates the cache with capacity from . - public BitFasterCacheService(IOptions options) + public BitFasterCacheService(IOptions options, ILogger logger) { var capacity = Math.Max(1, options.Value.Capacity); _cache = new ConcurrentLru(capacity); + + logger.LogInformation( + "BitFaster in-memory cache initialized (capacity={Capacity}). This constructor running does not " + + "by itself confirm this instance is the one injected as ICacheService — see the Cache feature's " + + "'Cache feature resolved' diagnostic line for the actually-bound implementation.", + capacity); } /// From f4a791b74120d1e5ff8f5c64c5a4c10bb6fb8f5d Mon Sep 17 00:00:00 2001 From: Mario Alberto Arce Date: Thu, 20 Aug 2026 13:43:55 -0600 Subject: [PATCH 5/6] chore(cache): add logging abstractions dependency - Add Microsoft.Extensions.Logging.Abstractions for BitFaster cache diagnostics --- .../PowerCSharp.Feature.Cache.BitFaster.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Features/PowerCSharp.Feature.Cache.BitFaster/PowerCSharp.Feature.Cache.BitFaster.csproj b/src/Features/PowerCSharp.Feature.Cache.BitFaster/PowerCSharp.Feature.Cache.BitFaster.csproj index 4fc8ff0..250b2d3 100644 --- a/src/Features/PowerCSharp.Feature.Cache.BitFaster/PowerCSharp.Feature.Cache.BitFaster.csproj +++ b/src/Features/PowerCSharp.Feature.Cache.BitFaster/PowerCSharp.Feature.Cache.BitFaster.csproj @@ -19,6 +19,7 @@ + From 204fde44e9696f61398afbf83dc9fad44cb9f7e3 Mon Sep 17 00:00:00 2001 From: Mario Alberto Arce Date: Thu, 20 Aug 2026 13:46:35 -0600 Subject: [PATCH 6/6] chore(cache): bump cache package family version - Update the Cache family version to 1.3.4 - Remove the outdated version rationale comment --- Directory.Build.props | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 07c836a..bbbe319 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -8,9 +8,8 @@ 1.0.2 - - 1.3.2 + + 1.3.4 1.0.0