Learn

How to use Redis for Query Caching

Will Johnston
Author
Will Johnston, Developer Growth Manager at Redis
Prasan Kumar
Author
Prasan Kumar, Technical Solutions Developer at Redis

What is query caching?#

Why you should use Redis for query caching#

TIP

If you use Redis Cloud, cache aside is easier due to its support for JSON and search. You also get additional features such as real-time performance, High scalability, resiliency, and fault tolerance. You can also call upon high-availability features such as Active-Active geo-redundancy.

Cache-aside with Redis (cache miss)#

Cache-aside with Redis (cache hit)#

TIP

If you use Redis Cloud and a database that uses a JDBC driver, you can take advantage of Redis Smart Cache, which lets you add caching to an application without changing the code. Click here to learn more!

Microservices architecture for an e-commerce application#

INFO

You don't need to use MongoDB/ Postgresql as your primary database in the demo application; you can use other prisma supported databases as well. This is just an example.

E-commerce application frontend using Next.js and Tailwind#

NOTE

Below is a command to the clone the source code for the application used in this tutorial

git clone --branch v4.2.0 https://github.com/redis-developer/redis-microservices-ecommerce-solutions

Caching in a microservices application with Redis and primary database (MongoDB/ Postgressql)#

Get products by filter request#

docs/api/get-products-by-filter.md
// POST http://localhost:3000/products/getProductsByFilter
{
  "productDisplayName": "puma"
}

Get products by filter response (cache miss)#

{
  "data": [
    {
      "productId": "11000",
      "price": 3995,
      "productDisplayName": "Puma Men Slick 3HD Yellow Black Watches",
      "variantName": "Slick 3HD Yellow",
      "brandName": "Puma",
      "ageGroup": "Adults-Men",
      "gender": "Men",
      "displayCategories": "Accessories",
      "masterCategory_typeName": "Accessories",
      "subCategory_typeName": "Watches",
      "styleImages_default_imageURL": "http://host.docker.internal:8080/images/11000.jpg",
      "productDescriptors_description_value": "<p style=\"text-align: justify;\">Stylish and comfortable, ...",
      "createdOn": "2023-07-13T14:07:38.020Z",
      "createdBy": "ADMIN",
      "lastUpdatedOn": "2023-07-13T14:07:38.020Z",
      "lastUpdatedBy": null,
      "statusCode": 1
    }
    //...
  ],
  "error": null,
  "isFromCache": false
}

Get products by filter response (cache hit)#

{
  "data": [
    //...same data as above
  ],
  "error": null,
  "isFromCache": true // now the data comes from the cache rather DB
}

Implementing cache-aside with Redis and primary database (MongoDB/ Postgressql)#

server/src/services/products/src/service-impl.ts
async function getProductsByFilter(productFilter: Product) {
  const prisma = getPrismaClient();

  const whereQuery: Prisma.ProductWhereInput = {
    statusCode: DB_ROW_STATUS.ACTIVE,
  };

  if (productFilter && productFilter.productDisplayName) {
    whereQuery.productDisplayName = {
      contains: productFilter.productDisplayName,
      mode: 'insensitive',
    };
  }

  const products: Product[] = await prisma.product.findMany({
    where: whereQuery,
  });

  return products;
}
server/src/services/products/src/routes.ts
const getHashKey = (_filter: Document) => {
  let retKey = '';
  if (_filter) {
    const text = JSON.stringify(_filter);
    retKey = crypto.createHash('sha256').update(text).digest('hex');
  }
  return 'CACHE_ASIDE_' + retKey;
};

router.post(API.GET_PRODUCTS_BY_FILTER, async (req: Request, res: Response) => {
  const body = req.body;
  // using node-redis
  const redis = getNodeRedisClient();

  //get data from redis
  const hashKey = getHashKey(req.body);
  const cachedData = await redis.get(hashKey);
  const docArr = cachedData ? JSON.parse(cachedData) : [];

  if (docArr && docArr.length) {
    result.data = docArr;
    result.isFromCache = true;
  } else {
    // get data from primary database
    const dbData = await getProductsByFilter(body); //method shown earlier

    if (body && body.productDisplayName && dbData.length) {
      // set data in redis (no need to wait)
      redis.set(hashKey, JSON.stringify(dbData), {
        EX: 60, // cache expiration in seconds
      });
    }

    result.data = dbData;
  }

  res.send(result);
});
TIP

You need to decide what expiry or time to live (TTL) works best for your particular use case.

Ready to use Redis for query caching?#

Additional resources#