Radis- Distributed Caching System with .NET 6 API With PostgreSqL DB
jayamoorthi parasuraman
FSD|Azure|Cosmos, postgresql, docker,|AKS|DevOps|Dot.Net Core/.Net5/6/7/8 ||xunit,nunit, integration, graphQL,gRpc | EFCore|API |WCF| Angular/React |Microservices,DDD/TDD| Dapper | Sonar Mob: +919715783720/+6580537622
Distributed hybrid caching as side car pattern accessing data from Reids.
Distributed caching in .NET refers to a caching mechanism that allows data to be stored across multiple servers or nodes rather than on a single machine. This approach helps improve application performance, scalability, and reliability by enabling faster data access and reducing the load on underlying data sources (like databases).
Git Source : https://github.com/jayamoorthi/BookApiPostgresql
Key Concepts of Distributed Caching:
Prerequisties:
Redis Cache
PostgreSql Database
Install Nuget Packages :
Microsoft.Extensions.Caching.StackExchangeRedis
Npgsql.EntityFrameworkCore.PostgreSQL
AspNetCore.HealthChecks.Redis
Docker Compose :
application run in the local environment
version: '3.4'
services:
books.api:
image: ${DOCKER_REGISTRY-}bookapipostgresql
container_name: books.api
build:
context: .
dockerfile: BookApiPostgresql\Dockerfile
ports:
- 5000:5000
- 5001:5001
books.cache:
image: redis/redis-stack:latest
container_name: books.cache
restart: always
volumes:
- ./.containers/redis_volume_data:/data
ports:
- 6379:6379
- 8001:8001
books.db:
image: postgres:latest
container_name: books.db
hostname: localhost
#dns: postgresdb
environment:
- POSTGRES_DB=books
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_HOST=localhost
volumes:
#- ./.containers/books-db:/path/in/container
#- /path/on/the/host:books-db/lib/postgres:rw
#- ./.containers/books-db:containers/books-db/lib/postgres/db/data
- ./.containers/books-db:/var/containers/books-db/lib/postgres/db/data
ports:
- 5432:5432
pgadmin:
image: dpage/pgadmin4
restart: always
ports:
- "5050:80"
# environment:
# - pgadmin_default_email: [email protected]
# - pgadmin_default_password: mypgadminpass
# volumes:
# - pgadmin_data:/var/lib/pgadmin
depends_on:
- books.db
Database Connection appsetting :
"ConnectionStrings": {
"PostgresDatabase": "Host=localhost;port=5432;Database=books;Username=postgres;Password=postgres;Include Error Detail=true",
"RedisCache": "localhost:6379"
},
Distributed Caching using Redis Server:
To implement distributed caching, we need to perform additional dependency service configurations.
public static class RedisExtensions
{
private const string ConnectionString = "RedisCache";
public static IServiceCollection AddRedisConfig(this IServiceCollection services,
IConfiguration configuration)
{
services.AddStackExchangeRedisCache(
options => options.Configuration = configuration.GetConnectionString(ConnectionString));
return services;
}
public static IHealthChecksBuilder AddRedisHealth(this IHealthChecksBuilder services,
IConfiguration configuration)
{
services.AddRedis(configuration.GetConnectionString(ConnectionString));
return services;
}
}
builder.Services
.AddPostgreSqlConfig(builder.Configuration)
.AddRedisConfig(builder.Configuration)
.AddHealthChecks()
.AddPostgreSqlHealth(builder.Configuration)
.AddRedisHealth(builder.Configuration);
builder.Services.AddScoped<ApplicationDbContext>();
builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
builder.Services.AddScoped<IBookRepository, BookRepository>();
builder.Services.AddScoped<IUnitOfWork, UnitOfWork>();
builder.Services.AddScoped<IBookService, BookService>();
builder.Services.AddScoped<IRedisCacheService, RedisCacheService>();
builder.Services.AddDistributedMemoryCache();
领英推荐
CacheService:
Generic method for storing and retrival the data from Redis server.
public class RedisCacheService : IRedisCacheService
{
private readonly IDistributedCache cache;
public RedisCacheService(IDistributedCache Cache)
{
cache = Cache;
}
public async Task<T?> GetDataAsync<T>(string key, CancellationToken cancellationToken)
{
string? data = await cache.GetStringAsync(key, cancellationToken);
if (string.IsNullOrEmpty(data))
{
return default;
}
return JsonSerializer.Deserialize<T>(data);
}
public async Task RemoveDataAsync(string key, CancellationToken cancellationToken)
=> await cache.RemoveAsync(key, cancellationToken);
public async Task SetDataAsync<T>(
string key,
T data,
CancellationToken cancellationToken)
{
var options = new DistributedCacheEntryOptions()
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5)
};
await cache.SetStringAsync(
key,
JsonSerializer.Serialize(data),
options);
}
}
BookService :
Data will be in the cache then return it else read from Db and store in the redis cache for further access it
public class BookService:IBookService
{
private readonly IBookRepository _bookRepository;
private readonly IUnitOfWork _unitOfWork;
private readonly IRedisCacheService _redisCacheService;
public BookService(IBookRepository bookRepository, IUnitOfWork unitOfWork, IRedisCacheService redisCacheService)
{
_bookRepository = bookRepository;
_unitOfWork = unitOfWork;
_redisCacheService = redisCacheService;
}
public async Task<Book?> GetBookByIdAsync(int id, CancellationToken cancellationToken)
{
var cacheKey = $"book:{id}";
// Try to get book from the hybrid cache
var book = await _redisCacheService.GetDataAsync<Book>(cacheKey, cancellationToken);
if (book != null)
{
return book; // Return from cache
}
var dbRead = await _bookRepository.FindAsync(id);
await _redisCacheService.SetDataAsync<Book>(cacheKey, dbRead, cancellationToken);
return dbRead;
}
}
Running :
Create Book API PayLoad:
{
"title": "Redis",
"isbn": "Redis1",
"description": "Hybird cache",
"author": "Jayamoorthi",
"createdAt": "2025-01-11T08:31:54.956Z"
}
Get Book By ID api
Redis Cache data can accces using redis cli command, it is already running redis server in our local for using Docker compose up - d ,
Docker-Compose up -d
Redis server by default port 6379 and redis-cli port 8001,
docker exec -it <redis_ContinerName> redis-cli
docker exec -it books.cache redis-cli Then redis cli will up and running against redis server port 127.0.0.1:6379.
Then using Redis cli comment to get / set / delete value from redis server.
C:\Users\91978\CleanArch\NewTemplate\BookApiPostgresql\BookApiPostgresql>docker exec -it books.cache redis-cli
127.0.0.1:6379> GET book_1
(nil)
127.0.0.1:6379> GET book:1
(nil)
127.0.0.1:6379> GET book:1
(error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> GET "book_1"
(error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> HGETALL book_1
1) "data"
2) "{\"Id\":1,\"Title\":\"Redis\",\"ISBN\":\"Redis1\",\"Description\":\"Hybird cache\",\"Author\":\"Jayamoorthi\",\"CreatedAt\":\"2025-01-11T08:31:54.956+00:00\"}"
Conclusion :
Implemented Distributed cache using redis using docker compose for redis-stack-server with redis cli for accessing cached data.
Thanks for reading this article, Happy weekend. See you bye bye !.