Inteligência Artificial

Erro 429 na API de IA: backoff exponencial em PHP com retry e usleep

O lote roda 40 requisições e na quadragésima primeira vem HTTP/1.1 429 Too Many Requests. sleep(1) entre as chamadas só empurra o problema: com dois workers em paralelo, o segundo estoura do mesmo jeito. O que resolve é respeitar o header Retry-After e dobrar a espera a cada tentativa.

O laço de cinco tentativas

$espera = 0;
$resposta = null;

for ($i = 0; $i < 5; $i++) {
    if ($espera > 0) {
        usleep($espera);
    }

    [$status, $retryAfter, $corpo] = enviar($url, $payload);

    if ($status !== 429 && $status < 500) {
        $resposta = $corpo;
        break;
    }

    $espera = $retryAfter > 0
        ? min($retryAfter, 30) * 1000000
        : (int) ((2 ** $i) * 500000 + random_int(0, 250000));
}

usleep trabalha em microssegundos, então 500000 é meio segundo. Com cinco tentativas você espera quatro vezes: 0,5s, 1s, 2s e 4s, uns 7,5 segundos no pior caso. O random_int é o jitter: sem ele, dez processos que tomaram 429 no mesmo instante voltam todos juntos e tomam 429 de novo. E o min() está ali porque um Retry-After de 3600 prenderia o script por uma hora.

Capturando o Retry-After no cURL

$retryAfter = 0;

curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, string $linha) use (&$retryAfter) {
    if (stripos($linha, 'retry-after:') === 0) {
        $retryAfter = (int) trim(substr($linha, 12));
    }

    return strlen($linha);
});

A callback precisa devolver o número de bytes da linha, senão o cURL aborta a transferência com erro de write. E o Retry-After pode vir como data HTTP em vez de segundos: aí o cast pra int dá 0 e cai no backoff, que é o que eu quero mesmo.

Uma coisa que me custou caro: só faça retry em 429 e nos 5xx. Com a chave errada, o 401 volta cinco vezes por item pra chegar no mesmo lugar. E se isso roda numa requisição em vez de por cron, a espera conta no fastcgi_read_timeout do nginx, 60s por padrão, e no request_terminate_timeout do PHP-FPM; no php-cli o max_execution_time já vem 0.

Post Recentes

array_filter no PHP apaga o 0 junto: como filtrar só o que é vazio

Chamar array_filter sem callback descarta 0, '0' e false junto com os vazios. Veja o callback certo, o ARRAY_FILTER_USE_BOTH e…

7 horas atrás

413 Request Entity Too Large no WordPress: ajuste nginx e PHP-FPM juntos

O upload do tema ou do vídeo morre com 413 e o php.ini não resolve? Quem corta é o nginx:…

13 horas atrás

Apagar dados do cliente no WooCommerce sem perder o histórico do pedido

O cliente pediu exclusão pela LGPD mas a nota fiscal precisa ficar guardada. A saída é anonimizar o pedido, não…

2 dias atrás

Timeout do PHP-FPM na chamada de IA: tire do request e jogue no CLI

504 no nginx quando a API de IA demora? Quem desiste primeiro é o nginx, não o PHP. Enfileire o…

2 dias atrás

Erro crítico neste site: usar o modo de recuperação do WordPress 6.9

O site caiu com erro crítico depois de atualizar um plugin e o e-mail de recuperação não chegou? Force um…

3 dias atrás

NumberFormatter no PHP: R$ formatado e valor por extenso em pt-BR

O number_format não põe o R$ nem escreve o valor por extenso. Veja como usar NumberFormatter com CURRENCY e SPELLOUT…

4 dias atrás