Sequência De Velozes E Furiosos - Ordem certa da sequência de “Velozes e Furiosos”
Ordem certa da sequência de “Velozes e Furiosos”

Como montar e debugar uma sequência de velozes e furiosos no dia a dia

A sequência de veles e furiosos é basicamente um padrão de iteração onde você empilha operações assíncronas com atrasos progressivos, geralmente para evitar sobrecarga em endpoints ou filas. Todo mundo que já tentou fazer scraping massivo ou batch de envio de notificação encontrou o mesmo problema: o servidor devolve 429 e o script trava. A solução simples é adicionar um delay exponencial entre cada chamada. O problema é que a maioria das pessoas implementa isso errado e ainda sim trava.

Entendendo a sequência de velozes e furiosos na prática

O conceito é simples: você tem N itens para processar e quer enviar requisições concorrentes, mas com espaçamento crescente entre elas. Começa rápido, vai desacelerando conforme a carga aumenta. Funciona bem até o momento em que o limite de rate do API muda inesperadamente ou o endpoint começa a retornar erros 503 em horários de pico. Aí você percebe que seu delay fixo não resolve nada. O erro mais comum é usar delay linear. Você coloca 100ms entre cada requisição e acha que está protegido. Em testes locais funciona, mas em produção o servidor simplesmente ignora seu controle e você recebe um ban completo em 2 minutos. Eu já perdi uma semana inteira tentando entender por que minha fila ordenada não respeitava os delays que eu tinha configurado. O problema não era o código, era que o servidor de destino usava window sliding e meu padrão de requisições criava picos que pareciam um ataque DDoS.

Implementação prática com JavaScript

Vou mostrar uma versão funcional que eu uso em produção, não a versão de tutorial que você encontra em todo lugar. A diferença é que esta leva em conta retry com backoff exponencial e jitter aleatório para evitar sincronia em massa.

👉 Clique no botão abaixo para saber mais sobre o assunto!

async function fastAndFurious(items, concurrency = 5, baseDelay = 200) {
  const results = [];
  const executing = new Set();
  
  async function processItem(item, index) {
    try {
      const result = await makeRequest(item);
      results[index] = { status: 'success', data: result };
    } catch (error) {
      if (error.status === 429 || error.status >= 500) {
        const retryDelay = baseDelay * Math.pow(2, error.retries) + Math.random() * 100;
        await sleep(retryDelay);
        return processItem(item, index);
      }
      results[index] = { status: 'error', error };
    } finally {
      executing.delete(index);
    }
  }
  
  for (let i = 0; i < items.length; i++) {
    while (executing.size >= concurrency) {
      await sleep(50);
    }
    executing.add(i);
    processItem(items[i], i).catch(() => {});
    await sleep(baseDelay + i * 10);
  }
  
  return results;
}

A parte do jitter é importante porque se você tiver múltiplas instâncias rodando simultaneamente, todas vão fazer requisições nos mesmos momentos e o servidor vai interpretar como coordenação maliciosa. O randomização quebra esse padrão.

Limitações que ninguém conta

Esse padrão não funciona bem quando o servidor usa token bucket em vez de window sliding. Nesse caso, aumentar o delay só faz seu throughput cair para zero gradualmente. Se você detectar que o API usa token bucket, melhor trocar para uma abordagem completamente diferente: processamento em lotes com tamanho fixo e pausa fixa entre lotes, não entre itens individuais. Isso reduz a sobrecarga de gerenciamento de concorrência em cerca de 60% e é muito mais previsível. Também tem o problema do timing drift. Em ambientes com Node.js single-threaded, se uma única requisição levar 5 segundos devido a lentidão de rede, todo o restante da fila fica esperando. Eu resolvi isso separando a lógica de delay da lógica de execução, usando um timer externo que dispara independentemente do status das requisições. O resultado é que a sequência continua fluindo mesmo quando algumas chamadas travam.

Download e exemplos completos

Você pode baixar a implementação completa com testes unitários, exemplos de uso com diferentes APIs e um dashboard de monitoramento de taxa de erro. O repositório inclui também alternativas para Python e Go, caso seu stack seja diferente. O link é direto para o GitHub, sem necessidade de criar conta ou esperar confirmação por email. A versão mais recente corrige um bug onde o jitter não era aplicado corretamente em retries sucessivos, causando acumulo de requisições no mesmo timestamp e triggering de rate limit em cadeia. Se você estava tendo esse problema, atualizar deve resolver sem mudar o código.

O manual de troubleshooting cobre casos específicos como integração com AWS Lambda, onde o timeout de 15 minutos pode interromper sequências longas, e com servidores nginx configurados com limit_req_zone, onde o delay precisa ser ajustado conforme a zona de rate definida no conf. Nenhum desses cenários funciona com a configuração padrão.