Um dos assuntos que considero mais incríveis é performance. Sobre performance de API's Rest, uma das formas mais conhecidas de melhorar o tempo de resposta de requisições à API é utilizando cache.
Cache permite acesso rápido a dados que são solicitados com maior frequência. Com isso, temos menos acesso à base de dados, com isso ganhamos mais velocidade em responder a solicitações que nossa API venha a receber.
Para isso, um dos bancos de dados mais utilizados na estratégia de cache é o Redis, solução de dados em memória simples, eficiente e que entrega uma performance excelente.
Mas um detalhe que deve ser observado quando utilizamos estratégias de cache é determinar um timeout para acesso aos dados, pois podemos ter uma indisponibilidade de acesso ao cache e não queremos que nossa aplicação fique aguardando um longo período de tempo até obter uma resposta.
Em API's que utilizam Node.js, podemos conseguir essa estratégia utilizando duas bibliotecas, quais são:
- ioredis: cliente Redis para conexão com a base de dados;
- bluebird: biblioteca que adiciona recursos ao trabalhar com Promises;
A biblioteca ioredis já utiliza Promises nas suas funções, mas o que podemos fazer é adicionar comportamentos extras, fazendo com que o ioredis passe a utilizar as funções de Promises disponibilizadas pelo bluebird.
Configuramos esse comportamento da seguite forma:
const Redis = require("ioredis");
const Promise = require('bluebird');
// cancelamos a Promise original
Promise.config({ cancellation: true });
// alteramos para utilizar as funções de Promise do bluebird.
Redis.Promise = Promise;
Configuramos o trecho Promise.config({ cancellation: true }) para informar que queremos que a Promise que originou a solicitação seja cancelada após o timeout ser atingido, assim o comando não ficará "tentando" enviar ao Redis.
Após esta configuração, podemos alterar o comportamento do acesso ao cache adicionando a função timeout que a biblioteca bluebird disponibiliza. Criamos a função que acessa os dados do cache da seguinte forma:
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!'));
}
Agora o comportamento será o seguinte: caso o cache não responda à solicitação em 2000 milissegundos (2 segundos), apenas informamos que o timeout do cache foi excedido e seguimos o fluxo da aplicação. Assim, temos oportunidade de pensar em alguma outra estratégia na nossa API, como buscar a informação em outra base de dados, acessar uma API externa, etc.
Podemos fazer a mesma coisa com a função que registra o dado no 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'));
}
Agora o comportamento será o seguinte: caso o cache não responda em 2000 milissegundos (2 segundos), apenas informamos que o timeout do cache foi excedido e seguimos o fluxo da aplicação.
Podemos fazer outras melhorias nas funções que recuperam e inserem os dados no cache, como apresentar no log algum erro que possa acontecer, mas preferi deixar o mais simples e claro para que possamos deixar nosso foco no comportamento esperado.
Conclusão
Desenvolver API's em Node.js utilizando o Redis como estratégia de cache torna-se uma excelente alternativa. Trabalhar com as bibliotecas ioredis e bluebird nos permite adicionar comportamentos extras. Com isso, conseguimos construir uma API mais resiliente e que entregue mais valor ao usuário final.
Desenvolvi um exemplo de API em Node.js utilizando MySQL e Redis com estratégia de cache apresentada aqui. Caso queira ver o código, acesse: https://github.com/leandroandrade/ncache
Experimente alterar o valor da função timeout e veja como a API se comporta.
Espero ter ajudado e divirtam-se.
Top comments (0)