Gerenciar riscos de escalonamento

O Cloud Tasks permite enviar grandes volumes de tarefas assíncronas enquanto controla a taxa em que o tráfego chega às filas e aos serviços downstream. Ao aplicar padrões de aumento gradual e controles de taxa, é possível escalonar as cargas de trabalho sem problemas, sem acionar picos de latência de fila ou sobrecarga de destino quando o tráfego aumenta mais rápido do que a infraestrutura pode se adaptar.

Visão geral

Este documento fornece diretrizes sobre as práticas recomendadas para manter o alto desempenho do Cloud Tasks em filas de alto tráfego. Uma fila de alta TPS é uma fila que tem 500 tarefas criadas ou enviadas por segundo (TPS) ou mais. Um grupo de filas de alto TPS é um conjunto contíguo de filas, por exemplo, [queue0001, queue0002, …, queue0099], que tem pelo menos 2.000 tarefas criadas ou enviadas por segundo no total. É possível conferir o histórico de TPS de uma fila ou grupo de filas usando as métricas do Cloud Monitoring, api/request_count para operações CreateTask e queue/task_attempt_count para tentativas de tarefa. As filas e os grupos de filas com alto tráfego estão sujeitos a duas classes amplas de falhas:

A sobrecarga de fila ocorre quando a criação e o envio de tarefas para uma fila ou um grupo de filas individual aumenta mais rápido do que a infraestrutura de fila pode se adaptar. A infraestrutura do Google usa camadas adaptáveis que realocam dinamicamente a carga com base nos padrões de tráfego, e essa adaptação leva tempo. Da mesma forma, a sobrecarga de destino ocorre quando a taxa de envio de tarefas causa picos de tráfego na infraestrutura de destino downstream. Em ambos os casos, siga um padrão 500/50/5: ao escalonar além de 500 TPS, aumente o tráfego em no máximo 50% a cada 5 minutos.

Diferente do padrão 500/50/5 para cargas de trabalho de TPS alto, a aceleração inicial da fila afeta todas as filas quando elas começam em um estado frio ou inativo, incluindo filas configuradas para taxas de envio moderadas. Neste documento, analisamos diferentes cenários que podem introduzir riscos de escalonamento e fornecemos exemplos de como gerenciar o aumento da fila e aplicar o padrão 500/50/5.

Aumento inicial da fila

O padrão 500/50/5 se aplica a filas de TPS alto que já operam em um estado estável. A aceleração inicial da fila é um mecanismo de sistema separado que se aplica a todas as filas, incluindo aquelas configuradas para taxas de envio moderadas, como 20 a 100 TPS, à medida que elas fazem a transição de um estado frio ou inativo para uma operação em estado estável.

Uma fila entra em um estado frio ou inativo em qualquer uma das seguintes situações:

  • A fila foi criada recentemente ou retomada após uma pausa.

  • A fila ficou inativa por alguns minutos sem envios ativos.

  • A fila está se recuperando após o recuo das respostas de erro HTTP 429 ou 5xx downstream. Para mais informações sobre como o backoff de erro afeta as taxas de envio, consulte Erros de backoff e taxas aplicadas.

Quando as tarefas chegam a uma fila fria ou inativa, o Cloud Tasks não as envia imediatamente na taxa max_dispatches_per_second configurada. Para proteger os destinos downstream contra picos repentinos de tráfego, o Cloud Tasks começa a enviar a uma taxa de referência baixa, como cerca de 1 TPS. Em seguida, ela aumenta progressivamente a taxa de envio aplicada em intervalos de avaliação periódicos. A taxa aplicada continua aumentando enquanto a demanda da fila permanece saturada e os gerenciadores de destino retornam respostas HTTP 2xx. Dependendo da taxa de destino, pode levar vários minutos para alcançar valores altos de max_dispatches_per_second.

Se a carga de trabalho enviar tráfego com picos separados por breves períodos de inatividade de alguns minutos ou mais, a fila será redefinida para um estado frio entre as intermitências e repetirá esse processo de aumento em cada intermitência. Para minimizar os atrasos de aumento gradual em cargas de trabalho sensíveis a picos, siga estas práticas recomendadas:

  • Mantenha um fluxo constante de tráfego de referência para manter as filas sensíveis à latência ativas entre as intermitências.

  • Reutilize as filas atuais em vez de excluí-las e recriá-las com frequência.

  • Provisione capacidade de destino downstream suficiente para que os gerenciadores retornem respostas HTTP 2xx e evitem retornar respostas de erro HTTP 429 ou 5xx que redefinam a taxa de envio aplicada.

Diagnosticar limitação de fila

Se a fila enviar tarefas mais lentamente do que a taxa max_dispatches_per_second configurada, verifique se o gargalo vem do aumento da fila fria ou inativa, da espera por congestionamento de destino ou dos limites de simultaneidade.

Aumento de fila fria ou inativa

A causa provável é o aumento inicial da fila se todas as seguintes condições se aplicarem:

  • A fila foi criada recentemente, retomada depois de ser pausada ou ficou inativa por alguns minutos antes da chegada do tráfego.
  • As tentativas de tarefa são bem-sucedidas sem erros de destino, mostrando response_code="OK" na métrica do Monitoring cloudtasks.googleapis.com/queue/task_attempt_count.
  • Os envios em andamento permanecem abaixo de max_concurrent_dispatches, enquanto cloudtasks.googleapis.com/queue/depth ou cloudtasks.googleapis.com/queue/task_attempt_delays é elevado. Ao mesmo tempo, a taxa de envio em cloudtasks.googleapis.com/queue/task_attempt_count aumenta progressivamente em intervalos de avaliação periódicos.

Para mais informações sobre as métricas do Monitoring, consulte Enviar métricas para o Monitoring.

Sobrecarga de destino e espera de congestionamento

O Cloud Tasks reduz dinamicamente as taxas de envio se o destino retornar respostas de erro HTTP 429 Too Many Requests ou HTTP 5xx, como HTTP 503 Service Unavailable. Esse recuo de congestionamento evita a sobrecarga do destino.

Para verificar se os erros de destino estão fazendo com que o Cloud Tasks limite os envios, confira a métrica do Monitoring cloudtasks.googleapis.com/queue/task_attempt_count agrupada pelo rótulo response_code. Se a geração de registros de fila estiver ativada, também será possível inspecionar os registros de resposta de tentativa na Análise de Registros:

resource.type="cloud_tasks_queue"
jsonPayload.attemptResponseLog.status!="OK"

Em response_code e jsonPayload.attemptResponseLog.status, o Cloud Tasks registra códigos de status gRPC canônicos em vez de códigos de status HTTP numéricos. Por exemplo, HTTP 429 é mapeado para RESOURCE_EXHAUSTED, HTTP 503 é mapeado para UNAVAILABLE, HTTP 500 é mapeado para INTERNAL e HTTP 504 ou um tempo limite é mapeado para DEADLINE_EXCEEDED.

Se você observar RESOURCE_EXHAUSTED, UNAVAILABLE ou outros status de erro de servidor do destino, escalonar verticalmente as instâncias de serviço de destino ou a simultaneidade de destino antes de aumentar os limites de envio da fila.

Para mais informações sobre como ativar os registros de fila, consulte Usar o Cloud Logging com filas.

Gargalo de simultaneidade

De acordo com a lei de Little, a latência de destino downstream limita a capacidade máxima de envio:

Max Throughput (tasks/sec) = min(
  max_dispatches_per_second,
  max_concurrent_dispatches / Downstream Latency in seconds
)

Por exemplo, se a latência de execução downstream for de 2 segundos e max_concurrent_dispatches estiver definido como 100, a capacidade será limitada a 50 tarefas por segundo, mesmo que max_dispatches_per_second esteja configurado como 500.

Para diagnosticar se a simultaneidade é o gargalo, divida max_concurrent_dispatches pela latência de resposta de destino registrada no campo jsonPayload.attemptResponseLog.attemptDuration no Logging. Compare esse limite calculado com a taxa de envio real na métrica do Monitoring cloudtasks.googleapis.com/queue/task_attempt_count. Se as distribuições reais estabilizarem perto desse limite enquanto cloudtasks.googleapis.com/queue/depth ou cloudtasks.googleapis.com/queue/task_attempt_delays crescerem, realize uma das seguintes ações:

  • Se o destino downstream tiver capacidade extra, como quando as tarefas passam a maior parte da duração aguardando a E/S externa, aumente max_concurrent_dispatches.
  • Se o destino downstream já estiver com alta utilização de recursos, aumentar max_concurrent_dispatches pode piorar a sobrecarga do destino. Em vez disso, escalonar horizontalmente as instâncias de destino ou reduza a latência de execução do gerenciador.

sobrecarga da fila

As filas ou os grupos de filas podem ficar sobrecarregados sempre que o tráfego aumenta repentinamente. Como resultado, essas filas podem apresentar:

  • aumento da latência da criação de tarefas;
  • aumento da taxa de erros na criação de tarefas;
  • redução na taxa de despacho.

Para se defender contra isso, estabeleça controles em qualquer situação em que a taxa de criação ou envio de uma fila ou grupo de filas possa aumentar repentinamente. Comece com um máximo de 500 operações por segundo em uma fila ou grupo de filas frio e, em seguida, aumente o tráfego em 50% a cada 5 minutos. Em teoria, é possível chegar a 740 mil operações por segundo após 90 minutos usando essa programação de aumento gradual. Há várias circunstâncias em que isso pode ocorrer.

Exemplo:

  • lançamento de novos recursos que fazem uso intenso do Cloud Tasks
  • movimentação do tráfego entre filas
  • reequilíbrio do tráfego em mais ou menos filas
  • execução de jobs em lote que injetam um grande número de tarefas

Nesses e em outros casos, siga o padrão 500/50/5.

Usar a divisão de tráfego do App Engine

Se um aplicativo do App Engine criar as tarefas, use a divisão de tráfego do App Engine (ambiente padrão do App Engine ou ambiente flexível do App Engine) para suavizar os aumentos de tráfego. Ao dividir o tráfego entre as versões (ambiente padrão ou ambiente flexível), é possível aumentar as solicitações gerenciadas por taxa ao longo do tempo para proteger a integridade da fila. Por exemplo, considere o caso de aumento do tráfego para um grupo de filas recém-expandido: deixe [queue0000, queue0199] ser uma sequência de filas de alto TPS que recebem 100.000 criações de TPS no total no pico.

Deixe [queue0200, queue0399] ser uma sequência de novas filas. Depois de mudar todo o tráfego, o número de filas na sequência dobra, e o novo intervalo de filas recebe 50% do tráfego total da sequência.

Ao implantar a versão que aumenta o número de filas, aumente gradualmente o tráfego para a nova versão e, portanto, para as novas filas, usando a divisão de tráfego:

  • Comece movendo 1% do tráfego para a nova versão. Por exemplo, 50% de 1% de 100.000 TPS gera 500 TPS para o conjunto de novas filas.
  • A cada cinco minutos, aumente em 50% o tráfego enviado para a nova versão, conforme detalhado na tabela a seguir:
Minutos desde o início da implantação % do tráfego total deslocado para a nova versão % do tráfego total para as novas filas % do tráfego total para as filas antigas
0 1.0 0,5 99,5
5 1,5 0,75 99,25
10 2,3 1,15 98,85
15 3,4 1,7 98,3
20 5,1 2,55 97,45
25 7,6 3,8 96,2
30 11,4 5,7 94,3
35 17,1 8,55 91,45
40 25,6 12,8 87,2
45 38,4 19,2 80,8
50 57,7 28,85 71,15
55 86,5 43,25 56,75
60 100 50 50

Picos de tráfego impulsionados pela versão

Ao lançar uma versão que aumenta significativamente o tráfego para uma fila ou grupo de filas, o lançamento gradual é, novamente, um mecanismo importante para suavizar os aumentos. Lance as instâncias gradualmente para que o lançamento inicial não exceda 500 operações por segundo no total para as novas filas, aumentando em no máximo 50% a cada 5 minutos.

Novas filas ou grupos de filas com alto TPS

Filas recém-criadas são especialmente vulneráveis. Grupos de filas, por exemplo, [queue0000, queue0001, …, queue0199], são tão sensíveis quanto filas únicas durante os estágios iniciais de lançamento. Para essas filas, o lançamento gradual é uma estratégia importante. Lance serviços novos ou atualizados que criem filas ou grupos de filas com alto TPS em etapas. Mantenha a carga inicial abaixo de 500 TPS e aumente em 50% ou menos, com pelo menos 5 minutos de intervalo.

Grupos de filas recém-expandidos

Ao aumentar a capacidade total de um grupo de filas, por exemplo, expandindo [queue0000-queue0199 para queue0000-queue0399], siga o padrão 500/50/5. Para procedimentos de lançamento, os novos grupos de filas se comportam da mesma forma que as filas individuais. Aplique o padrão 500/50/5 ao novo grupo como um todo, não apenas a filas individuais dentro do grupo.

Para essas expansões de grupos de filas, o lançamento gradual é uma estratégia importante. Se a origem do tráfego for o App Engine, use a divisão de tráfego. Ao migrar o serviço para adicionar tarefas ao número maior de filas, lance as instâncias gradualmente para que o lançamento inicial não exceda 500 operações no total para as novas filas e não aumente o tráfego em mais de 50% a cada 5 minutos.

Expansão emergencial do grupo de filas

Às vezes, talvez você queira expandir um grupo de filas atual, por exemplo, porque espera que as tarefas sejam adicionadas ao grupo de filas mais rapidamente do que o grupo pode enviá-las. Se você distribuir os nomes das novas filas de maneira uniforme entre os nomes das filas atuais em ordem lexicográfica, poderá enviar tráfego imediatamente para essas filas. Verifique se não há mais de 50% de novas filas intercaladas e se o tráfego para cada fila é menor que 500 TPS. Esse método é uma alternativa ao uso da divisão de tráfego e do lançamento gradual.

Para conseguir esse tipo de nomenclatura intercalada, adicione um sufixo às filas que terminam em números pares. Por exemplo, se você tiver 200 filas [queue0000-queue0199] e quiser criar 100 novas filas, escolha [queue0000a, queue0002a, queue0004a, …, queue0198a] como os novos nomes de fila, em vez de [queue0200-queue0299].

Se você precisar de um aumento maior, ainda será possível intercalar até 50% mais filas a cada 5 minutos.

Enfileiramento de tarefas em lote ou em grande escala

Quando você precisa adicionar um grande número de tarefas, por exemplo, milhões ou bilhões, um padrão de injeção dupla pode ser útil. Em vez de criar tarefas de um único job, use uma fila de injetores. Cada tarefa adicionada à fila do injetor se divide e adiciona 100 tarefas à fila ou ao grupo de filas relevante. É possível acelerar a fila do injetor ao longo do tempo. Por exemplo, comece com 5 TPS para gerar 500 TPS na fila ou no grupo de filas de destino e, em seguida, aumente a taxa em 50% a cada 5 minutos.

Tarefas nomeadas

Quando você cria uma tarefa, o Cloud Tasks atribui a ela um nome exclusivo por padrão. É possível atribuir seu próprio nome a uma tarefa usando o parâmetro name. No entanto, isso apresenta uma sobrecarga de desempenho significativa, resultando em maiores latências e taxas de erro potencialmente maiores associadas a tarefas nomeadas. Nomes de tarefas sequenciais, como carimbos de data/hora, podem aumentar significativamente essa sobrecarga.

Se você atribuir seus próprios nomes, use um prefixo bem distribuído para nomes de tarefas, como um hash do conteúdo. Para mais informações sobre como nomear uma tarefa, consulte a referência do campo Task.name.

sobrecarga do destino

O Cloud Tasks pode sobrecarregar os serviços e recursos downstream que você usa, como o App Engine, o Firestore no modo Datastore (Datastore) e sua rede, se os envios de fila aumentarem repentinamente. Se houver um acúmulo de tarefas, retomar essas filas pode sobrecarregar os serviços. Para se defender contra a sobrecarga de destino, siga o padrão 500/50/5: se uma fila enviar mais de 500 TPS, aumente o tráfego acionado por uma fila em no máximo 50% a cada 5 minutos. Para monitorar os aumentos de tráfego de forma proativa, use as métricas do Monitoring. É possível usar os alertas do Monitoring para detectar situações potencialmente perigosas.

Retomar filas de alta TPS

Quando você retoma uma fila pausada, o Cloud Tasks aplica a inicialização da fila e aumenta gradualmente a taxa de envio de um valor de referência baixo para a taxa max_dispatches_per_second configurada da fila. No entanto, se um grande backlog de tarefas se acumular em uma fila de alto TPS ou em várias filas que você retomar ao mesmo tempo, a taxa de envio ainda poderá subir mais rápido do que os serviços de destino downstream podem escalonar. Para evitar a sobrecarga do destino ao retomar filas com um grande backlog, escalone a retomada de várias filas. Como alternativa, reduza temporariamente a taxa de max_dispatches_per_second e aumente-a gradualmente seguindo o padrão 500/50/5. Para mais informações sobre como o Cloud Tasks limita e aumenta os envios quando as filas são retomadas, consulte Erros de espera e taxas aplicadas.

Tarefas agendadas em massa

Um grande número de tarefas programadas para serem enviadas ao mesmo tempo também pode introduzir um risco de sobrecarga do destino. Se você precisar iniciar um grande número de tarefas de uma só vez, use os controles de taxa de fila para aumentar gradualmente a taxa de envio ou provisionar a capacidade de destino com antecedência.

Maior fan-out

Ao atualizar os serviços que o Cloud Tasks chama, aumentar o número de chamadas remotas pode criar riscos de produção. Por exemplo, digamos que as tarefas em uma fila de TPS alto chamem o gerenciador /task-foo. Uma nova versão pode aumentar significativamente o custo de chamar /task-foo se, por exemplo, essa nova versão adicionar várias chamadas caras do Datastore ao gerenciador. O resultado líquido de uma versão como essa é um aumento enorme no tráfego do Datastore, que está imediatamente relacionado a mudanças no tráfego do usuário. Use o lançamento gradual ou a divisão de tráfego para gerenciar o aumento.

Novas tentativas

Seu código pode tentar novamente em caso de falha ao fazer chamadas da API Cloud Tasks. No entanto, quando uma proporção significativa de solicitações falha com erros do lado do servidor, uma alta taxa de repetição pode sobrecarregar ainda mais as filas e retardar a recuperação. Limite a quantidade de tráfego de saída se o cliente detectar que uma proporção significativa de solicitações está falhando com erros do lado do servidor. Por exemplo, é possível usar o algoritmo de limitação adaptativa descrito no capítulo Como lidar com sobrecarga do livro Engenharia de confiabilidade do site. As bibliotecas de cliente gRPC do Google implementam uma variação desse algoritmo.