dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.16k stars 9.92k forks source link

HybridCache can create multiple times the same key on Redis #56043

Open blastrock opened 3 months ago

blastrock commented 3 months ago

Is there an existing issue for this?

Describe the bug

The HybridCache documentation says:

The HybridCache service ensures that only one concurrent caller for a given key executes the factory method, and all other callers wait for the result of that execution.

But this mechanism seems to only rely on a local memory cache. This property isn't ensured if multiple instances of a service try to create the same key.

Expected Behavior

If multiple services try to create the same key, I expect the property to still hold true. The second service should wait for the first one to complete instead of retrying to create it.

Steps To Reproduce

Here is a simple program that will create a key (given as an argument) on a redis cache: https://github.com/blastrock/hybridcacheissue/blob/master/Program.cs

  1. Start a redis instance
  2. Run dotnet run test1, it will print "Will get" and "Creating" and block
  3. Run dotnet run test1, it will print the same thing.

I expect the second run to not call the creation delegate and wait for the first one to complete.

Exceptions (if any)

No response

.NET Version

8.0.300

Anything else?

.NET SDK:
 Version:           8.0.300
 Commit:            326f6e68b2
 Workload version:  8.0.300-manifests.c1c70047
 MSBuild version:   17.10.4+10fbfbf2e

Runtime Environment:
 OS Name:     debian
 OS Version:  
 OS Platform: Linux
 RID:         linux-x64
 Base Path:   /usr/share/dotnet/sdk/8.0.300/

.NET workloads installed:
There are no installed workloads to display.

Host:
  Version:      8.0.5
  Architecture: x64
  Commit:       087e15321b

.NET SDKs installed:
  8.0.300 [/usr/share/dotnet/sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 8.0.5 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 7.0.19 [/usr/share/dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 8.0.5 [/usr/share/dotnet/shared/Microsoft.NETCore.App]

Other architectures found:
  None

Environment variables:
  Not set

global.json file:
  Not found

Learn more:
  https://aka.ms/dotnet/info

Download .NET:
  https://aka.ms/dotnet/download
Xor-el commented 3 months ago

@blastrock I think what you are looking for would only be possible using some sort of distributed lock mechanism like RedLock when using Redis. However, I do not think that is within the scope of HybridCache.

mgravell commented 3 months ago

This is something that has been discussed, but it is not going to be implemented in the initial release. It is, however, something that we're happy to consider on the list for future versions. It will probably need a new optional L2 API to support it - the existing IDistributedCache backend simply doesn't have the concept of distributed locks, or an atomic "set if not exists and tell me whether it succeeded" (which is the minimum needed for such)

blastrock commented 3 months ago

Thank you for the answers, we ended up using RedLock.

In the meantime, maybe the documentation should be updated to reflect this limitation. In our use-case, we really don't want to make requests twice, and the documentation suggested HybridCache was the right tool for the job.