You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
hello, i was trying the hybrid cache and noticed that when I'm inside the factory of the new hybridcache in GetOrCreateAsync and tried to access httpcontext but it was null.
this is when I'm inside the factory and try to access httpcontext to get user Identity or empty Guid and as you can see its empty
and after I'm out of the cache in the same request I can access it and its filled:
Reproduction Steps
injecting IHttpContextAccessor and then try to access it inside factory of GetOrCreateAsync
Expected behavior
i was expecting it to be filled current user identity
Actual behavior
and because httpcontextaccessor was null i get empty guid
so i think its a bug. Currently im not using cache aside pattern for hybrid cache by setting the option like:
options: new HybridCacheEntryOptions { Flags = HybridCacheEntryFlags.DisableUnderlyingData }
so it only bring me the cached data
I made this work-around extension but I hope it will be fixed soon.
publicstaticclassHybridCacheExtensions{// Work-around for hybrid cache not transporting the execution context (see: https://github.com/dotnet/extensions/issues/5648)internalstaticValueTask<T>GetOrCreateInExecutionContextAsync<T>(thisHybridCachecache,stringkey,Func<CancellationToken,ValueTask<T>>factory,HybridCacheEntryOptions?options=null,IEnumerable<string>?tags=null,CancellationTokencancellationToken=default)=>cache.GetOrCreateAsync(key,ExecutionContext.Capture(),(context,token)=>{ExecutionContext.Restore(context);returnfactory(token);},options,tags,cancellationToken);internalstaticValueTask<T>GetOrCreateInExecutionContextAsync<T,TState>(thisHybridCachecache,stringkey,TStatestate,Func<TState,CancellationToken,ValueTask<T>>factory,HybridCacheEntryOptions?options=null,IEnumerable<string>?tags=null,CancellationTokencancellationToken=default)=>cache.GetOrCreateAsync(key,(Context:ExecutionContext.Capture(),OriginalState:state),(state,token)=>{ExecutionContext.Restore(state.Context);returnfactory(state.OriginalState,token);},options,tags,cancellationToken);}
Reacted by Jacob Slusser and Kenzo Yuri
Reacted by Pourya Nofallah, valdis iljuconoks, Max Reble, Tyler Burnett, 박현우, Denis Frolov and SylvainRollinetSoftcom
This approach kind of half works.
When there is no cached data, It executes factory method and it works fine within correct ExecutionContext,
But when only GET is executed, sadly, it doesn't work.
Although, thanks to @nielsheeren
It's a shame this doesn't seem to be taken seriously. If the downstream work being done by the factory method is significant, this can at minimum: orphan a tremendous amount of telemetry breaking the call graph, at worse case: break the application which needs access to current request.
EDIT: Telemetry doesn't flow because Activity.Current doesn't either.
Reacted by Artem Balianytsia, Denis Frolov, magahl-swegon, Emanuel Paul and Alex Tsoi
Also initialize CacheService and add it to .NET DI resolver.
This will trigger command of RedisGET and SET operations.
Now, Send another HTTP request to the same endpoint with the same city name.
This will trigger command of RedisGET operation only, and no SET operation.
Expected
Another trace is added and redis related traces should be appended on the second traces.
Actual
Second trace is added but Redis related traces are appended on the first trace, not the second one.
This unexpected behavior is observed, and it increase Duration column time span of the first trace, when it first one begins and end when the second one ends.
If I send more requests, the Duration column time span of the first trace will increase more and more.
Are there any updates or plans to address this issue?
We are seeing the same behavior, but in our case it also affects OpenTelemetry trace propagation.
We use HybridCache with Redis as the distributed (L2) cache. When GetOrCreateAsync enters the shared/stampede-protected work, Activity.Current is no longer propagated. As a result:
Redis operations performed internally by HybridCache (HGET/HMGET) start new traces instead of being children of the calling operation.
The factory callback also loses the original Activity.Current, so database operations executed from the factory are disconnected from the original trace.
For the factory we can work around this by capturing Activity.Current.Context before calling GetOrCreateAsync, passing the ActivityContext through TState, and explicitly creating an activity with that parent inside the factory.
However, this cannot fix the L2 Redis operation because it happens internally in HybridCache before the factory is invoked.
This results in traces roughly like:
original trace
└─ application cache operation
separate trace
└─ Redis HMGET
while both operations are part of the same logical GetOrCreateAsync call.
Is preserving tracing context (or otherwise providing OpenTelemetry-friendly correlation for the shared HybridCache operation) something that is planned to be addressed?
Activity
PouryaNoufallah96 commentedon Nov 28, 2024
?
KalleOlaviNiemitalo commentedon Dec 17, 2024
I guess it's caused by the use of ThreadPool.UnsafeQueueWorkItem here:
extensions/src/Libraries/Microsoft.Extensions.Caching.Hybrid/Internal/DefaultHybridCache.StampedeStateT.cs
Lines 55 to 59 in 16acdbf
which is called from here:
extensions/src/Libraries/Microsoft.Extensions.Caching.Hybrid/Internal/DefaultHybridCache.cs
Lines 156 to 163 in 16acdbf
so the
underlyingDataCallbackgets called in a thread-pool work item to which the ExecutionContext does not propagate.In your application, I think you can work around it like this:
i.e. capture the ExecutionContext and make the factory restore it; but this will cost additional closure and delegate allocations.
PouryaNoufallah96 commentedon Dec 18, 2024
so i think its a bug. Currently im not using cache aside pattern for hybrid cache by setting the option like:
options: new HybridCacheEntryOptions { Flags = HybridCacheEntryFlags.DisableUnderlyingData }
so it only bring me the cached data
nielsheeren commentedon Apr 11, 2025
I made this work-around extension but I hope it will be fixed soon.
BOBx5 commentedon Jul 4, 2025
This approach kind of half works.
When there is no cached data, It executes factory method and it works fine within correct ExecutionContext,
But when only GET is executed, sadly, it doesn't work.
Although, thanks to @nielsheeren
jacobslusser commentedon Sep 10, 2025
It's a shame this doesn't seem to be taken seriously. If the downstream work being done by the factory method is significant, this can at minimum: orphan a tremendous amount of telemetry breaking the call graph, at worse case: break the application which needs access to current request.
EDIT: Telemetry doesn't flow because
Activity.Currentdoesn't either.denisyfrolov commentedon Dec 3, 2025
+1 We really need this one to get fixed
jodydonetti commentedon Dec 8, 2025
Hi @denisyfrolov
As a (temporary?) fix you may look into using the FusionCache based implementation of HybridCache, see here.
It also has a backplane to keep the cache coherent even in a multi-node environment, and that also works with the HybridCache impl.
It's a 100% compatible drop-in impl replacement, no code changes required except for 1 line during setup.
Hope this helps.
NB: FusionCache creator here, just to be clear.
PeterRygeBestseller commentedon Feb 25, 2026
+1
I just ran into this bug as well. Would be nice if it gets fixed.
altso commentedon Mar 24, 2026
An alternative workaround is not to pass
CancellationToken.extensions/src/Libraries/Microsoft.Extensions.Caching.Hybrid/Internal/DefaultHybridCache.cs
Lines 181 to 192 in 890dbc4
altso commentedon Mar 25, 2026
@BOBx5 can you please clarify?
BOBx5 commentedon Mar 26, 2026
@altso
Now I moved to ZiggyCreatures.FusionCache (thanks to @jodydonetti).
Note
I did run my API solution with .NET Aspire.
In Aspire, I could see OpenTelemetry Traces.
CacheServicewhich usesHybridCachefromMicrosoft.Extensions.Caching.Hybrid:ICacheServicethrough DI.CacheServiceand add it to .NET DI resolver.GETandSEToperations.GEToperation only, and noSEToperation.Durationcolumn time span of the first trace, when it first one begins and end when the second one ends.Durationcolumn time span of the first trace will increase more and more.altso commentedon Mar 26, 2026
Thank you @BOBx5! I really appreciate the detailed response.
HybridCache#5568 as a duplicate of this issuechertby commentedon Oct 5, 2026
Hi,
Are there any updates or plans to address this issue?
We are seeing the same behavior, but in our case it also affects OpenTelemetry trace propagation.
We use
HybridCachewith Redis as the distributed (L2) cache. WhenGetOrCreateAsyncenters the shared/stampede-protected work,Activity.Currentis no longer propagated. As a result:HybridCache(HGET/HMGET) start new traces instead of being children of the calling operation.Activity.Current, so database operations executed from the factory are disconnected from the original trace.For the factory we can work around this by capturing
Activity.Current.Contextbefore callingGetOrCreateAsync, passing theActivityContextthroughTState, and explicitly creating an activity with that parent inside the factory.However, this cannot fix the L2 Redis operation because it happens internally in
HybridCachebefore the factory is invoked.This results in traces roughly like:
while both operations are part of the same logical
GetOrCreateAsynccall.Is preserving tracing context (or otherwise providing OpenTelemetry-friendly correlation for the shared HybridCache operation) something that is planned to be addressed?
Thanks!
svick commentedon Oct 7, 2026
@chertby I don't think we'll be able to fix this in .Net 11 (it's too close to release), but I want to look at this for .Net 12.