Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Microsoft Distrubted Redis Cache - Getting keys based on pattern

We are working with the Microsoft Distrbuted Cache implementation for .NET core. See https://docs.microsoft.com/en-us/aspnet/core/performance/caching/distributed?view=aspnetcore-2.1 for more information.

Now we can get an key by the following code.

var cacheKey = "application:customer:1234:profile";
var profile = _distributedCache.GetString(cacheKey);

What i want to do is tho do the following:

var cacheKey = "application:customer:1234:*";
var customerData = _distributedCache.GetString(cacheKey);

So that we can get the following keys with this pattern:

  • application:customer:1234:Profile
  • application:customer:1234:Orders
  • application:customer:1234:Invoices
  • application:customer:1234:Payments

Could not get this work with any wildcard or without an wild card. Is there an solution without implementing another Redis nuget package?

like image 589
C. Molendijk Avatar asked Oct 25 '18 08:10

C. Molendijk


2 Answers

This isn't supported via the IDistributeCache interface. It's designed to get/set a specific key, not return a range of keys. If you need to do something like this, you'll need to drop down into the underlying store, i.e. Redis. The good news is that you don't need anything additional: the same StackExchange.Redis library that is needed to support the Redis IDistributedCache implementation also provides a client you can utilize directly.

In particular to your scenario here, you'd need some code like:

var server = _redis.GetServer(someServer);
foreach(var key in server.Keys(pattern: cacheKey)) {
    // do something
}

Here, _redis is an instance of ConnectionMultiplexer. This should already be registered in your service collection since it's utilized by the Redis IDistributedCache implementation. As a result, you can inject it into the controller or other class where this code exists.

The someServer variable is a reference to one of your Redis servers. You can get all registered Redis servers via _redis.GetEndpoints(). That will return an IEnumerable of servers, which you can either pick from or enumerate over. Additionally, you can simply connect directly to a particular server via passing the host string and port:

var server = _redis.GetServer("localhost", 6379);

Be advised, though, that Keys() will result in either a SCAN or KEYS command being issued at the Redis server. Which is used depends on the server version, but either is fairly inefficient, as the entire keyspace must be looked at. It is recommended that you do not use this in production, or if you must, that you issue it on a slave server.

With your question technically answered, given the complexity and the inherent inefficiency of SCAN/KEYS, you'd be better served just doing something like:

var cacheKeyPrefix = "application:customer:1234";
var profile = _distributedCache.GetString($"{cacheKeyPrefix}:Profile");
var orders = _distributedCache.GetString($"{cacheKeyPrefix}:Orders");
var invoices = _distributedCache.GetString($"{cacheKeyPrefix}:Invoices");
var payments = _distributedCache.GetString($"{cacheKeyPrefix}:Payments");

That's going to end up being much quicker and doesn't require anything special.

like image 91
Chris Pratt Avatar answered Oct 03 '22 23:10

Chris Pratt


I know question is a bit old but based on this answear: How to get all keys data from redis cache

This is example solution:

in CustomerRepository.cs

using Newtonsoft.Json;
using StackExchange.Redis;
// ...

public class CustomerRepository : ICustomerRepository
{
    private readonly IDistributedCache _redis;
    private readonly IConfiguration _configuration;

    public CustomerRepository(IDistributedCache redis, IConfiguration configuration)
    {
        _redis = redis;
        _configuration = configuration;
    }

    ///<summary>replace `object` with `class name`</summary>

    public async Task<object> GetCustomersAsync(string name)
    {
        ConfigurationOptions options = ConfigurationOptions.Parse(_configuration.GetConnectionString("DefaultConnection"));
        ConnectionMultiplexer connection = ConnectionMultiplexer.Connect(options);
        IDatabase db = connection.GetDatabase();
        EndPoint endPoint = connection.GetEndPoints().First();
        var pattern = $"application:customer:{name}:*";
        RedisKey[] keys = connection.GetServer(endPoint).Keys(pattern: pattern).ToArray();
        var server = connection.GetServer(endPoint);

        var result = await _redis.GetStringAsync(key);
        return JsonConvert.DeserializeObject<object>(result);
    }
}

in appsettings.json

{
  "ConnectionStrings": {
    "DefaultConnection": "localhost:6379,password=YOUR_PASSWORD_HERE"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  }
}
like image 39
mikolaj semeniuk Avatar answered Oct 03 '22 23:10

mikolaj semeniuk