/FastCache

dotnetcore, dotnet cache, dotnet redis, dotnet InMemoryCache

Primary LanguageC#MIT LicenseMIT

Build Status codecov NuGet version (FastCache.Core)

πŸ”₯Easily to use cacheπŸ”₯

  • InMemory Support
  • Integrate into Redis caching
  • Fast, concurrent, evicted memory, support big cache

🀟 Install

Choose caching provider that you need and install it via Nuget.

Install-Package FastCache.InMemory
Install-Package FastCache.Redis
Install-Package FastCache.MultiSource

πŸš€ Quick start

// Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseServiceProviderFactory(new DynamicProxyServiceProviderFactory());
builder.Services.AddInMemoryCache(); // InMemory

// builder.Services.AddMultiBucketsInMemoryCache(); // Big cache
// builder.Services.AddRedisCache("server=localhost:6379;timeout=5000;MaxMessageSize=1024000;Expire=3600", canGetRedisClient: true) // "Expire=3600" is redis global timeout
// canGetRedisClient = true => get redisClient instance
// var redisClient = serviceProvider.GetService<ICacheClient>();

// UserService.cs
[Cacheable("user-single", "{id}", 60 * 30)] // Cache expires after two seconds
public virtual User Single(string id)
{
    return _dbContext.Set<User>().Single(x => x.Id == id);
}

**********************************
***** Method must be virtual *****
**********************************

πŸ“š Support Autofac

builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
builder.Host.ConfigureContainer<ContainerBuilder>(build =>
{
    build.RegisterDynamicProxy();
});

β˜€οΈ Use in Controller

// Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseServiceProviderFactory(new DynamicProxyServiceProviderFactory());
builder.Services.AddInMemoryCache(); // InMemory

builder.Services.AddMvc().AddControllersAsServices(); // the key point

// UserController.cs
[ApiController]
[Route("/user")]
public class UserController : ControllerBase
{
    [Route("/"), HttpGet]
    [Cacheable("user-single", "{id}", 60 * 10)]
    public virtual User Get(string id)
    {
        return _userService.Single(id);
    }
}
**********************************
***** Method must be "virtual" *****
**********************************

⏱ About Cache expiration ( For InMemory )

  • There are three ways of cache eviction, LRU and TTL and Random

πŸͺ£ About InMemory BigCache ( Multi Buckets )

  • Fast. Performance scales on multi-core CPUs.
  • The cache consists of many buckets, each with its own lock. This helps scaling the performance on multi-core CPUs, since multiple CPUs may concurrently access distinct buckets.
  • FastCache.InMemory automatically evicts old entries when reaching the maximum cache size set on its creation.

πŸ“Œ Redis cluster

builder.Services.AddRedisCache("server=127.0.0.1:6000,127.0.0.1:7000,127.0.0.1:6379;db=3;timeout=7000")

This Redis component supports Redis Cluster, configure any node address, 
it will be able to automatically discover other addresses and slot distribution,
and use the correct node when performing read and write operations.

This Redis component does not directly support the Redis sentinel, but supports it in the form of active-standby failover.

Cache automatic eviction

// UserService.cs
public class UserService
{
    [Cacheable("user-single", "{id}", 2)] // Cache expires after two seconds
    public virtual User Single(string id)
    {
        return _dbContext.Set<User>().Single(x => x.Id == id);
    }
}

Active cache eviction

// UserService.cs
public class UserService
{
    [Cacheable("user-single", "{id}", 2)] // Cache expires after two seconds
    public virtual User Single(string id)
    {
        return _dbContext.Set<User>().Single(x => x.Id == id);
    }
    
    [Evictable(new[] { "user-single", "other cache name" }, "{id}")]
    // [Evictable(new[] { "user" }, "{id}*")] // when {id} is "123", it will match keys starting with user:123 and perform a fuzzy deletion.
    public virtual void Delete(string id)
    {
        // delete logic...
    }
}

πŸ‘» Match both uses

// UserService.cs
public class UserService
{
    [Cacheable("user-single", "{id}")] // cache never expires
    public virtual User Single(string id)
    {
        // Get User logic...
    }
    
    [Cacheable("user-single", "{user:id}")]
    [Evictable(new[] { "user-single", "other cache name" }, "{user:id}")] 
    public virtual User Update(User user)
    {
        // Update logic...
    }
}

**********************************************

The Update method will be executed first. 
After the method is successfully executed, the setup cache will be invalidated,
and finally the Cacheable operation will be executed.

STEP:
 1. When "user:id" = "123"
 2. Then Evict cache: "user-single:123"
 3. Then After updated will caching:  "user-single:123" -> { latest user data }


πŸš€ This means that the cache will always be kept up to date,
thus triggering queries to the database will be significantly reduced πŸš€ 

**********************************************

🍺 Multi Source ( Currently supports Redis and inMemory )

// Program.cs
builder.Services.AddMultiSourceCache(
    "server=localhost:6379;timeout=5000;MaxMessageSize=1024000;Expire=3600",  // redis connectionString
    true                                                                      // can get redis client
);


// UserController.cs
// Target.Redis Target.InMemory
[HttpGet]
[MultiSourceCacheable("MultiSource-single", "{id}", Target.Redis, 60)] // Target.Redis
public virtual async Task<User> Get1(string id)
{
    return await _userService.Single(id).Result;
}

[HttpGet]
[MultiSourceCacheable("MultiSource-single", "{id}", Target.InMemory, 60)] // Target.InMemory
public virtual async Task<User> Get2(string id)
{
    return await _userService.Single(id).Result;
}


**********************************************
According to business needs, flexibly store the cache in redis or memory
**********************************************

πŸŽƒ Parameter Description

// MemoryCache
public static void AddInMemoryCache(
    this IServiceCollection services, 
    int maxCapacity = 1000000,
    MaxMemoryPolicy maxMemoryPolicy = MaxMemoryPolicy.LRU, int cleanUpPercentage = 10
)
{  // ... }

// MulitBucketsMemoryCache
public static void AddMultiBucketsInMemoryCache(
    this IServiceCollection services,
    uint buckets = 5,
    uint maxCapacity = 500000,
    MaxMemoryPolicy maxMemoryPolicy = MaxMemoryPolicy.LRU,
    int cleanUpPercentage = 10)
{  //...  }
Parameter Type Default Require Explain
buckets uint 5 false The number of containers to store the cache, up to 128
bucketMaxCapacity uint 1000000 false (MemoryCache) Initialize capacity

(MulitBucketsMemroyCache) The capacity of each barrel, it is recommended that 500,000 ~ 1,000,000
maxMemoryPolicy MaxMemoryPolicy MaxMemoryPolicy.LRU false LRU = Least Recently Used , TTL = Time To Live, Or RANDOM
cleanUpPercentage int 10 false After the capacity is removed, the percentage deleted

Variable explanation

// foo:bar:1 -> "item1"
{
   "foo": {
      "bar": [
         "item1",
         "qux"
     ]
   }
}

// foo:bar:0:url -> "test.weather.com"
{
   "foo": {
      "bar": [
         {
            "url": "test.weather.com",
            "key": "DEV1234567"
         }
     ]
   }
}