AWS Serverless: Tudo o que você precisa saber sobre cache para suas aplicações serverless
Tags: ptbr, serverless, aws, webdev
Published: 2019-10-13 08:27:12

Na semana passada, alguém me perguntou no [AWS User Group no The Hague](https://www.meetup.com/aws-hague/events/jdqldryzmbxb/), "O cache ainda é relevante para aplicativos serverless?"

Supondo-se que o Lambda seja escalonado automaticamente pelo tráfego, ainda precisamos nos preocupar com o cache? E se sim, onde e como implementamos o cache?

Então, vamos dividir. 

# O armazenamento em cache ainda é MUITO relevante

Sim, o Lambda escala automaticamente por tráfego. Mas tem limites.

Existe o limite flexível de 1000 execuções simultâneas na maioria das regiões. O qual você pode aumentar por meio de um ticket de suporte. Mas também há um limite rígido para a rapidez com que você pode aumentar as execuções simultâneas após as 1000 iniciais. Na maioria das regiões, esse limite é de 500 por minuto.

O que significa que você levará 18 minutos para atingir um rendimento máximo de 10 mil execuções simultâneas. Isso não é um problema se seu tráfego for muito estável ou seguir a [curva da campainha](https://en.wikipedia.org/wiki/The_Bell_Curve), para que não haja picos repentinos. 

![](https://theburningmonk.com/wp-content/uploads/2019/09/img_5d912351d3d88.png)

No entanto, se seu tráfego for muito brusco, o limite de 500/min será um problema. Por exemplo, se você fornece um serviço de streaming para eventos ao vivo ou está no ramo de pedidos de alimentos. Nos dois casos, o armazenamento em cache precisa ser parte integral do seu aplicativo.

![](https://theburningmonk.com/wp-content/uploads/2019/09/img_5d912363e59e6.png)\

E depois há a questão do desempenho e da eficiência de custos.

O armazenamento em cache melhora o tempo de resposta, pois elimina viagens de ida e volta desnecessárias. No caso de serverless, isso também se traduz em economia de custos, pois a maioria das tecnologias que usamos são pagas por uso. 

# Onde você deve implementar o cache?

Uma API REST típica pode ser assim:

![](https://theburningmonk.com/wp-content/uploads/2019/09/img_5d91237c82ab2.png)

Você tem:

- Route53 como o DNS.
- CloudFront como o CDN.
- API Gateway para lidar com autenticação, taxa de limites e validação de solicitação.
- Lambda para executar a lógica de negócios.
- DynamoDB como o banco de dados. 

Nesta configuração muito típica, você pode implementar o cache em vários locais. Minha preferência geral é armazenar em cache o mais próximo possível do usuário final. Isso maximiza o benefício de economia da sua estratégia de armazenamento em cache.

# Armazenamento em cache no aplicativo cliente

Dada a opção, sempre habilitarei o cache no próprio aplicativo da web ou dispositivo móvel.

![](https://theburningmonk.com/wp-content/uploads/2019/09/img_5d912394b66e1.png)

Para dados imutáveis ​​ou raramente alterados, isso é muito eficaz. Por exemplo, os navegadores armazenam em cache imagens e marcações HTML o tempo todo para melhorar o desempenho. E o protocolo HTTP possui um [rico conjunto de cabeçalhos](https://www.keycdn.com/blog/http-cache-headers) para permitir que você ajuste o comportamento do cache.

Frequentemente, o cache do lado do cliente pode ser implementado facilmente usando técnicas como [memoização](https://en.wikipedia.org/wiki/Memoization) e encapsulado em bibliotecas reutilizáveis.

No entanto, armazenar em cache dados no lado do cliente significa que você deve responder a pelo menos uma solicitação por cliente. Isso ainda é muito ineficiente e você também deve armazenar em cache as respostas no lado do servidor. 

![](https://theburningmonk.com/wp-content/uploads/2019/09/img_5d9123a8a37d5.png)

# Armazenamento em cache no CloudFront

O CloudFront possui um recurso de cache interno. É o primeiro lugar que você deve considerar o cache no servidor.

![](https://theburningmonk.com/wp-content/uploads/2019/09/img_5d9123b7645eb.png)

O armazenamento em cache na borda (_edge_) é muito econômico, pois elimina a maioria das chamadas para o API Gateway e o Lambda. Pulando essas chamadas também melhora a latência de ponta a ponta e, finalmente, a experiência do usuário. Além disso, ao fazer o cache na borda, você não precisa modificar o código do aplicativo para ativar o cache.

O CloudFront suporta armazenamento em [cache por strings de consulta](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/QueryStringParameters.html), [cookies](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Cookies.html) e [cabeçalhos de solicitação](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/header-caching.html). Ele também [suporta failover de origem](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/header-caching.html), o que pode melhorar o tempo de atividade do sistema.

Na maioria dos casos, esse é o único cache do servidor que eu preciso.

# Armazenamento em cache no API Gateway

O CloudFront é ótimo, mas também possui limitações. Por exemplo, o CloudFront apenas armazena em cache as respostas às solicitações GET, HEAD e OPTIONS. Se você precisar armazenar em cache outras solicitações, será necessário armazenar em cache as respostas na camada API Gateway.

![](https://theburningmonk.com/wp-content/uploads/2019/09/img_5d9123ce18071.png)

Com o cache do API Gateway, você pode armazenar em cache as respostas a qualquer solicitação, incluindo POST, PUT e PATCH. No entanto, [isso não está ativado por padrão](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-caching.html).

![](https://theburningmonk.com/wp-content/uploads/2019/09/img_5d9123dfa72e6.png)

Você também tem muito mais controle sobre a chave de cache. Por exemplo, se você tiver um terminal com vários parâmetros de caminho e/ou string de consulta, por exemplo,

> GET /{productId}?shipmentId={shipmentId}&userId={userId}

Você pode escolher quais parâmetros de caminho e sequência de consulta estão incluídos na chave de cache. Nesse caso, é possível usar apenas o **productId** como a chave de cache. Portanto, todas as solicitações para o mesmo ID do produto receberiam a resposta em cache, mesmo que **shipmentId** e **userId** sejam diferentes.

Uma desvantagem do cache do API Gateway é que você muda do preço de pagamento por uso para pagar pelo tempo de atividade. Basicamente, você estará pagando pelo tempo de atividade de um nó Memcached (que o API Gateway gerencia para você). 

![](https://theburningmonk.com/wp-content/uploads/2019/09/img_5d9123f8094bd.png)

O cache do API Gateway é poderoso, mas encontro poucos casos de uso. O principal caso de uso que tenho é para armazenar em cache solicitações POST.

# Armazenamento em cache na função Lambda

Você também pode armazenar em cache dados na função Lambda. Qualquer coisa declarada fora da função manipuladora é reutilizada entre invocações.

```js
// esse `html` é reutilizado entre invocações
let html;

const loadHtml = async () => {
  if (!html) {
    // carrega o HTML de algum lugar
    // só é executado durando o "cold start" e depois é cacheado
  }

  return html
}

module.exports.handler = async (event) => {
  return {
    statusCode: 200,
    body: await loadHtml(),
    headers: {
      'content-type': 'text/html; charset=UTF-8'
    }
  }
}
```

Você pode aproveitar o fato de que os contêineres são reutilizados sempre que possível e armazenar em cache quaisquer configurações estáticas ou objetos grandes. Essa é realmente uma das recomendações [no guia oficial de boas práticas](https://docs.aws.amazon.com/lambda/latest/dg/best-practices.html).

![](https://theburningmonk.com/wp-content/uploads/2019/09/img_5d9124143db2e.png)
![](https://theburningmonk.com/wp-content/uploads/2019/09/img_5d91241d9f215.png)

No entanto, os dados em cache estão disponíveis apenas para esse contêiner e não há como compartilhá-los em todas as execuções simultâneas de uma função. Isso significa que a taxa geral de cache em falta pode ser bastante alta - a primeira chamada em cada contêiner será uma falta de cache (_miss cache hit_).

Como alternativa, você pode armazenar em cache os dados no Elasticache. Isso permitiria que os dados armazenados em cache fossem compartilhados entre muitas funções. Mas também exige que suas funções estejam dentro de uma VPC.

A boa notícia é que _cold start_ por estar dentro de uma VPC [desaparecerá em breve](https://aws.amazon.com/blogs/compute/announcing-improved-vpc-networking-for-aws-lambda-functions/). A má notícia é que você ainda estará pagando pelo [tempo de atividade](https://aws.amazon.com/elasticache/pricing/) do cluster Elasticache. Além disso, a introdução do Elasticache exigiria alterações no código do aplicativo para leituras e gravações.

Se você usa o DynamoDB, há muito pouco motivo para usar o Elasticache para o cache no nível do aplicativo. Em vez disso, você deve usar o [DAX](https://aws.amazon.com/dynamodb/dax/).

# DAX

O DAX permite que você colha os benefícios do Elasticache sem precisar executá-lo. Você ainda precisa pagar pelo tempo de atividade dos nós de cache, mas eles são totalmente gerenciados pelo DynamoDB.

![](https://theburningmonk.com/wp-content/uploads/2019/09/img_5d9124353bc0e.png)

O melhor do DAX é que ele requer uma alteração mínima do seu código. O principal problema que encontrei com o DAX é seu [comportamento de cache](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DAX.consistency.html#DAX.consistency.query-cache) em relação a solicitações de consulta (_query_) e escaneamentos (_scan_). Em resumo, as consultas (_query_) e escaneamentos (_scan_) têm seus próprios caches e não são invalidadas quando o cache do item é atualizado. O que significa que eles podem retornar dados obsoletos imediatamente após uma atualização.

![](https://theburningmonk.com/wp-content/uploads/2019/09/img_5d912447bfebc.png)

# Em Conclusão

Para resumir, o cache ainda é uma parte importante de qualquer aplicativo serverless. Melhora a escalabilidade e o desempenho do seu aplicativo. Também ajuda a manter seu custo sob controle, mesmo quando você precisa escalar para milhões de usuários.

Como discutimos neste post, você pode implementar o cache em todas as camadas do seu aplicativo. Na medida do possível, você deve implementar o cache do lado do cliente e para as respostas de API, fazer o cache na borda (_edge_) com o CloudFront. Quando o cache de borda não for possível, mova o cache para ainda mais dentro da sua aplicação, em torno do API Gateway e, em seguida, para o Lambda ou o DAX.

Se você não estiver usando o DynamoDB, ou precisar armazenar em cache dados compostos por fontes de dados diferentes, considere também a introdução do Elasticache. 

![](https://theburningmonk.com/wp-content/uploads/2019/09/img_5d912459f20ef.png)

---

# Créditos

- [All you need to know about caching for serverless applications](https://theburningmonk.com/2019/10/all-you-need-to-know-about-caching-for-serverless-applications/), escrito originalmente por [Yan Cui](https://twitter.com/theburningmonk).