As inicializações a frio da função Lambda intrigaram inúmeros heróis e desenvolvedores da AWS. Alguns juram que são um problema real hoje, enquanto outros franzem a testa e as desconsideram como não sendo mais relevantes. No entanto, pela minha experiência, há casos de uso em que inicializações a frio podem impactar a experiência do usuário e devem ser tratadas.
Nesta publicação opinativa, você aprenderá se as partidas a frio ainda são um problema, quando minimizá-las e como.
TL;DR: Sim, mas o contexto importa.
Índice
O que é uma partida a frio?
As inicializações a frio no AWS Lambda ocorrem quando uma função do AWS Lambda é invocada após não ser usada por um longo período ou quando a AWS está dimensionando instâncias de função em resposta ao aumento de carga. - AJ Stuyvenberg, herói sem servidor
Uma inicialização a frio lambda se refere ao atraso inicial experimentado quando uma função AWS Lambda é invocada pela primeira vez ou após ficar ociosa, enquanto o ambiente de tempo de execução da função é inicializado. Isso inclui baixar o código da função e iniciar o ambiente de execução.
Em poucas palavras, é uma latência única adicionada ao tempo geral de execução da sua função. A duração de uma inicialização a frio varia de menos de 100 ms a mais de 1 segundo, de acordo com a documentação oficial da AWS . Uma vez que a função esteja "quente", outras invocações não sofrerão a inicialização a frio, a menos que, devido a demandas de tráfego, o serviço Lambda aumente o número de execuções simultâneas, resultando em novas invocações de funções lambda e inicializações a frio.
Além disso, ao atualizar o código da função, você receberá uma inicialização a frio novamente durante sua invocação.
Agora que entendemos a definição técnica de partidas a frio, vamos falar sobre impacto.
Quão ruim isso realmente é?
Impacto de partida a frio do Lambda
De acordo com uma análise das cargas de trabalho de produção do Lambda, as inicializações a frio geralmente ocorrem em menos de 1% das invocações. - AWS
Um por cento não parece muito, não é? Bem, se você tem um uso crítico enfrentando fluxo síncrono e milhões de invocações, esse 1% agora se traduz em potencialmente dez mil clientes insatisfeitos, o que não é o ideal.
No entanto, tudo é muito subjetivo e o contexto importa, pois há casos de uso em que isso é aceitável.
Vamos começar com os casos simples, os casos de uso em que uma inicialização a frio provavelmente é suficiente.
Casos de uso de impacto menor
Vamos tirar o caso de uso mais simples do caminho: casos em que as inicializações a frio são tão rápidas que não são um problema para você. Esse é geralmente o caso para funções que usam tempos de execução como C++, Go, Rust e LLRT . No entanto, você deve seguir as melhores práticas e otimizações em cada tempo de execução para manter uma inicialização a frio de baixo impacto.
Invocação assíncrona
Outro uso simples é quando sua função é invocada de forma assíncrona, seja um fluxo SQS, SNS, EventBridge, DynamoDB, etc. Nesses casos de uso, outro segundo de tempo de execução provavelmente será suficiente e não será um problema.
Fluxo não crítico
Uma latência extra de 0,5-1 segundo importa em fluxos não críticos para 1% dos clientes? Provavelmente não, especialmente se for um fluxo não voltado para o cliente, como chamadas de serviço para serviço.
No entanto, algumas ações voltadas ao cliente também podem ser aceitáveis com uma inicialização a frio ocasional.
Depende do tempo total: Um cliente notará a diferença entre 2 segundos e 1 segundo? Talvez, mas pode ser aceitável para a maioria.
Por outro lado, se o cliente está esperando 5 segundos e agora pode ter que esperar 6 ou 7, isso pode ser bastante perceptível, pois o cliente já está esperando há muito tempo, e tempo extra pode aumentar o aborrecimento.
Padrão de tráfego
Seu comportamento de tráfego também desempenha um papel. Se você tem tráfego constante sem mudanças drásticas de escala, você pode não experimentar nenhuma inicialização a frio, pois sempre há funções quentes.
Casos de uso de grande impacto - é aqui que dói
Vamos rever os casos de uso da minha experiência em que as inicializações a frio tiveram muito impacto na experiência do usuário e tivemos que tomar medidas para minimizá-los.
Desempenho crítico é essencial
Inícios frios prejudicam mais quando se lida com fluxos voltados para o cliente, onde o desempenho é crítico, mesmo para 1% dos clientes. Por exemplo, se você tem microsserviços dedicados à autenticação ou autorização que são necessários para operar em alta simultaneidade e concluir a execução em menos de uma dúzia de milissegundos, 1% de 0,5-1 segundo extra pode ser um fator decisivo. Eu diria que o Lambda pode ser muito caro ou inadequado para tais casos de uso, então faça sua pesquisa.
Padrão de tráfego irregular
Os números mágicos de 1% fornecidos pela AWS podem ser diferentes no seu caso, dependendo do padrão de tráfego. Se o tráfego for irregular, por todo lugar e imprevisível, sua função pode ter mais inicializações a frio do que a média.
Partidas a frio em cadeia
Vamos rever a arquitetura abaixo. Você tem três micro-serviços, cada um contendo uma função Lambda.
O usuário envia uma solicitação de API para a primeira função (1). A primeira função chama o microserviço (2) e então chama o terceiro (3).
O primeiro microsserviço está esperando que ambos os microsserviços respondam, então seu tempo de execução total depende do desempenho deles. Vamos supor que o primeiro microsserviço teve uma inicialização a frio de 1 segundo, mas quando ele chamou o segundo microsserviço, ele também teve uma inicialização a frio de um segundo. Isso aconteceu novamente quando ele chamou o terceiro serviço.
Então agora temos no total uma inicialização a frio de 3 segundos, o que é muito ruim da perspectiva da experiência do usuário.
Depende dos seus padrões de tráfego, mas pode acontecer. Você pode obter durações de inicialização a frio de zero, um, dois ou três segundos; tudo é um jogo justo. O problema se torna ainda mais desafiador quando equipes diferentes gerenciam os microsserviços, cada uma responsável por definir suas funções e otimizações.
O ponto principal é que cada função lambda da qual você depende em um fluxo crítico aumenta a chance de uma penalidade de inicialização a frio agregada, e você deve considerar isso com antecedência.
Otimizações de partida a frio
Inícios a frio podem desencorajar as pessoas a usar o serverless, e eu posso entender de onde essas pessoas vêm, mas não é preto no branco. Inícios a frio podem ser melhorados e otimizados a ponto de não serem relevantes para seus casos de uso.
Muitos parâmetros podem reduzir ou aumentar a duração da partida a frio, e é essencial entendê-los. Vamos listar os fatores mais críticos.
Tamanho da memória da função
Mais memória se traduz em mais núcleos de CPU e melhor desempenho, o que por sua vez pode encurtar a duração da inicialização a frio. No entanto, seu custo geral aumentará. Use ferramentas como AWS Lambda Powertuning para ajustar a relação desempenho/custo.
Arquitetura da CPU
Escolher uma arquitetura de CPU ARM64 pode aumentar o desempenho geral em alguns casos. Use o Lambda Powertuning para verificar se há alguma diferença na sua função.
Tempo de execução da função
Diferentes tempos de execução têm desempenho de inicialização a frio diferente. Java é notoriamente lento, mas pode ser melhorado com SnapStart .
Você pode selecionar Rust ou LLRT (lambda low latency runtime) para inicializações a frio mais curtas.
Para conteúdo relacionado a Rust, sugiro que você siga o conteúdo de Benjamin's Pyle .
AJ resumiu as diferenças entre os tempos de execução de forma bastante clara (procure o ícone de neve como medição de partida a frio):
Método de Criação de Função
AJ discute essa discrepância em sua sessão re: invent (veja o vídeo abaixo).
Você pode criar um arquivo ZIP contendo todas as dependências e o código-fonte da sua função Lambda e enviá-lo para a AWS, ou pode criar uma imagem de contêiner que contenha tudo o que precisa.
As funções baseadas em contêineres têm inicializações a frio mais curtas. No entanto, eu não mudaria todas as minhas criações ZIP para contêineres ainda. As funções baseadas em contêineres sofrem com tempo de construção e implantação mais longo, e são mais difíceis de gerenciar (arquivos docker não são divertidos). Eu usaria apenas se quisesse o melhor desempenho ou se minha função tivesse dependências acima de 250 MB, que é a limitação do método ZIP.
Você pode ler mais sobre isso no meu blog aqui .
Importações Importam
Os desenvolvedores tendem a adicionar importações que trazem bibliotecas inteiras apenas para uma função pequena.
Cada pequeno detalhe importa e aumenta o tempo total de importação como parte da inicialização a frio. Precisamos otimizar nosso código e importações. Se você usa Python, pode analisar seu código com uma ferramenta como Tuna e otimizar suas bibliotecas (talvez substituir as mais lentas) e suas importações.
No entanto, não importa o quão otimizado seu código esteja, em algum momento você sofrerá com inicializações a frio.
Vamos discutir outra solução que pode ir além das otimizações.
Como observação, recomendo fortemente a sessão re:invent 2023 do AJ se você quiser se aprofundar no Lambda e entender as inicializações a frio.
Concorrência Provisionada para o Resgate
Vamos supor que você tenha feito todas as otimizações possíveis que listei acima, mas ainda assim tenha inicializações a frio significativas em seus fluxos críticos voltados ao cliente.
Sua próxima solução será configurar a simultaneidade provisionada para suas funções Lambda. A simultaneidade provisionada é a
número de ambientes de execução pré-inicializados alocados para sua função. Esses ambientes de execução estão prontos para responder imediatamente às solicitações de função recebidas. Configurar a simultaneidade provisionada incorre em cobranças adicionais para sua conta da AWS. - Documentação da AWS
Pague por funções sempre quentes — chega de partidas a frio. Sim, funciona, e é caro.
No entanto, não é mágica. Se você definir 10 funções para simultaneidade provisionada e o serviço Lambda precisar escalar para a 11ª função devido a requisitos de tráfego e escala, você terá uma inicialização a frio nessa 11ª função e além. No entanto, se você ajustar o número de simultaneidade provisionada para que se ajuste às suas necessidades de escala, não deve haver nenhuma inicialização a frio (a menos que você carregue um novo código-fonte lambda, é claro).
O único problema com a simultaneidade provisionada é que ela pode ficar bem cara rapidamente. Imagine que você a habilita em várias contas e várias regiões; o custo se multiplica rapidamente. Uma vez eu errei na calculadora de preços da AWS , e o custo real foi muito maior do que eu esperava, então tivemos que reduzi-lo rapidamente.
Nesta postagem , descrevemos como você pode otimizar ainda mais a simultaneidade provisionada e habilitar uma simultaneidade provisionada dinâmica - uma abordagem que maximiza o desempenho e reduz custos.
Otimizações de Custos
Primeiro, defina a simultaneidade provisionada somente em seus casos de uso extremamente voltados para o usuário, que devem ter o melhor desempenho. Mesmo assim, certifique-se de defini-la somente na conta de produção e não nas contas de desenvolvimento ou teste.
Outra prática recomendada para economia de custos é definir diferentes configurações de simultaneidade para várias contas e regiões. A AWS recomenda estimar as configurações necessárias; você pode ler a fórmula deles aqui .
Por fim, uma vez configurado, o monitoramento e os alertas são necessários para garantir que você não esteja gastando demais ou de menos. Você pode seguir este guia da AWS para entender como criar seus alertas e painel.
Resumo
Neste post, definimos as partidas a frio e discutimos seus impactos — quando elas podem ser prejudiciais e quando não são.
Discutimos como otimizar e minimizar o impacto da inicialização a frio e, quando todas as otimizações falharam, a solução sugerida, porém cara, é habilitar a simultaneidade provisionada.
No próximo post da série , forneceremos uma otimização de custos para simultaneidade provisionada ao longo de exemplos de código CDK.