DEV Community

Leandro Andrade
Leandro Andrade

Posted on

Cache timeout strategy with Node.js and Redis

A topic that I consider most incredible is related to performance. About Rest API's performance, the best known ways to improve the response time of API requests is by using cache.

Cache allows quick access to data that is requested most often. With this, we have less access to the database, and we get more speed in responding to requests that our API may be receive.

For this, one of the most used databases in the cache strategy is Redis, a memory data solution that is simple, efficient and that delivers excellent performance.

But a detail that we must observe when using cache strategies is to determine a timeout for accessing data, because we can have an unavailability of access to the cache and we do not want our application waiting a long period of time to obtain an answer.

In APIs that use Node.js, we can achieve this strategy using two libraries, which are:

  • ioredis: Redis client for connection to the database;
  • bluebird: library that adds resources when working with Promises;

The ioredis library already uses Promises in our functions, but what we can do is add extra behaviors, making ioredis start using the Promises functions provided by bluebird.

We can set this behavior as it follows:

const Redis = require("ioredis");
const Promise = require('bluebird');

// Promise original enable for cancelation
Promise.config({ cancellation: true });

// Change to use bluebird Promises.
Redis.Promise = Promise;
Enter fullscreen mode Exit fullscreen mode

We have set the Promise.config ({cancellation: true}) snippet to define that we want the Promise that originated the request to be canceled after the timeout is reached, so the command will not be "trying" to send to Redis.

After that, we can change the behavior of the cache access by adding a function timeout that bluebird library provided. We create a function that accesses the cache data as it follows:

exports.getCache = async (key) => {
    return Redis.client().get(key)
        .timeout(2000)
        .then(cache => cache ? (console.log(`REDIS: data from cache!`), JSON.parse(cache)) : null)
        .catch(err => console.log('ERROR_REDIS: Timeout exceeded!'));
}
Enter fullscreen mode Exit fullscreen mode

Now the behavior will be as it follows: if the cache does not respond the request in 2000 milliseconds (2 seconds), we only print that cache timeout has been exceeded and we follow the application flow. So we have the opportunity to think of some another strategies in our API like searching for information in other database, accessing an external API, etc.

We can do the same thing with the function that registers the data in the cache:

exports.setCache = async (key, value) => {
    const newKey = getKey({ key });
    Redis.client().set(newKey, JSON.stringify(value), 'EX', 120)
        .timeout(2000)
        .then(() => console.log(`REDIS: key ${ key } set cache!`))
        .catch(err => console.log('ERROR_REDIS: Timeout exceeded'));
}
Enter fullscreen mode Exit fullscreen mode

Now the behavior will be as it follows: if the cache does not respond in 2000 milliseconds (2 seconds), we only print the cache timeout has been exceeded and we follow the application flow.

We can make other improvements in the functions that retrieve and insert the data in the cache, such as printing an error that may be happen, but I preferred to make it as simple and clear as possible for that we can focus on the expected behavior.

Conclusion

Developing API's in Node.js using Redis as a cache strategy becomes an excellent alternative. Working with the ioredis and bluebird library allows us to add extra behaviors. With that, we were able to build a more resilient API that improve better experience to the user.

I developed some example API in Node.js using MySQL and Redis with cache strategy presented here. If you like to see the code, access: https://github.com/leandroandrade/ncache

Try to change the timeout function and see how the API behavior

I hope I helped and have fun.

Top comments (0)