Radis- Distributed Caching System with .NET 6 API With PostgreSqL DB



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:

  1. Cashe Storege store and access data temporarily for faster and secure access
  2. Scalability Cache node & improve system performance
  3. High Availability ensure by storing cached data across multiple nodes
  4. Data Consistency ensure by same data between the cache and the underlying data source
  5. Session Management in web applications, allowing session data to be shared across multiple server instances

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 !.

要查看或添加评论,请登录

jayamoorthi parasuraman的更多文章

  • Data Visualization (Matplot + Pandas) using Python (Jupyter NoteBook)

    Data Visualization (Matplot + Pandas) using Python (Jupyter NoteBook)

    Jupyter : Jupyter is an open-source, web-based interactive computing platform that allows you to create and share…

  • FastApi - Python

    FastApi - Python

    FastAPI is a modern, fast (high-performance), web framework for building APIs with Python. It is based on standard…

    2 条评论
  • .NET 6 with PostgreSQL and Redis Cache

    .NET 6 with PostgreSQL and Redis Cache

    To exploring how to utilise PostgreSQL with .NET 6.

  • Bogus - Generate Realistic Fake Data in C#

    Bogus - Generate Realistic Fake Data in C#

    GitSource: https://github.com/jayamoorthi/ApiDIScrutor Bogus is a fake data generator for .

  • What is Clean Code ?

    What is Clean Code ?

    The most popular definition of clean code is code that is easy to understand and easy to change. Clean code is code…

  • PipelineBehavior in MediatR using .NET with CQRS

    PipelineBehavior in MediatR using .NET with CQRS

    Git Source: https://github.com/jayamoorthi/ApiDIScrutor What are Pipeline behaviours Pipeline behaviours are a type of…

  • NET API using Scrutor DI with CQRS.

    NET API using Scrutor DI with CQRS.

    Dependency injection (DI) is built-in features of ASP.NET Core.

  • Decorator Pattern

    Decorator Pattern

    Decorator pattern is a structural design pattern that allows you to add new functionalities to an Existing object…

    1 条评论
  • Getting Started with gRPC using .Net

    Getting Started with gRPC using .Net

    RPC is a language agnostic, high-performance Remote Procedure Call (RPC) framework. Benifites: Modern…

    1 条评论
  • Value objects in DDD?

    Value objects in DDD?

    Value Objects is one of the component of DDD. Identity is fundamental for entities.

社区洞察

其他会员也浏览了