
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.
$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.
$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.
Allowed memory size exhausted no painel: qual das duas constantes do wp-config.php mexer, onde conferir o valor real e por…
strtotime com data brasileira devolve false e o INSERT quebra no MySQL 8. Use createFromFormat com getLastErrors antes de mandar…
O hash que o Magento mostra junto do erro é o nome de um arquivo em var/report. Veja como abrir,…
Seu snippet com woocommerce_checkout_fields parou de aparecer no checkout em blocos. Registre o campo pela API nova e leia o…
O pedido entrou às 21h e o relatório mostra 00h do dia seguinte. Onde setar date.timezone no PHP 8.3 e…
O pedido entra na loja e o e-mail nunca sai: antes de culpar o SMTP, veja o envio assíncrono, a…