Como Construir um Sistema de Retry Inteligente com Dead Letter Queue para Consultas de CPF

Aprenda a construir um sistema de retry com backoff exponencial e dead letter queue para consultas de CPF que falharam. Garanta que nenhuma consulta se perca.

Redação CPFHub.io
Redação CPFHub.io
··7 min de leitura
Como Construir um Sistema de Retry Inteligente com Dead Letter Queue para Consultas de CPF

Introdução

Nem toda falha na consulta de CPF é permanente. Timeouts, erros de rede e indisponibilidades temporárias são comuns em integrações com APIs externas. Um sistema de retry inteligente sabe diferenciar falhas temporárias de permanentes, aplica backoff exponencial para não sobrecarregar o provedor e envia consultas que esgotaram as tentativas para uma dead letter queue (DLQ) para reprocessamento posterior.


Anatomia de um retry inteligente

Nem toda falha merece retry. Erros 4xx geralmente indicam problemas no input e não vale a pena repetir. Já erros 5xx e timeouts são candidatos perfeitos para nova tentativa.

Código HTTPTipo de erroDeve fazer retry?Justificativa
400Bad RequestNãoInput inválido, retry não resolve
401UnauthorizedNãoCredencial errada, precisa correção
403ForbiddenNãoSem permissão, precisa ajuste
408TimeoutSimTemporário, servidor sobrecarregado
429Rate LimitSim (com delay)Esperar e tentar novamente
500Internal ErrorSimErro no servidor, pode se resolver
502/503IndisponívelSimServidor temporariamente fora
  • Backoff exponencial -- cada retry espera progressivamente mais tempo (1s, 2s, 4s, 8s) para dar tempo ao servidor de se recuperar
  • Jitter -- adiciona aleatoriedade ao tempo de espera para evitar que múltiplos clientes façam retry no mesmo instante
  • Max retries -- limita o número de tentativas para não ficar em loop infinito

Implementando o retry com backoff exponencial

const axios = require("axios");

class RetryConfig {
    constructor({
    maxRetries = 3,
    baseDelay = 1000,
    maxDelay = 30000,
    jitterFactor = 0.5,
    retryableStatuses = [408, 429, 500, 502, 503, 504],
    } = {}) {
    this.maxRetries = maxRetries;
    this.baseDelay = baseDelay;
    this.maxDelay = maxDelay;
    this.jitterFactor = jitterFactor;
    this.retryableStatuses = retryableStatuses;
    }

    calculateDelay(attempt) {
    const exponential = this.baseDelay * Math.pow(2, attempt);
    const capped = Math.min(exponential, this.maxDelay);
    const jitter = capped * this.jitterFactor * Math.random();
    return capped + jitter;
    }

    isRetryable(error) {
    if (error.code === "ECONNABORTED" || error.code === "ETIMEDOUT") {
    return true;
    }
    if (error.response) {
    return this.retryableStatuses.includes(error.response.status);
    }
    return true; // Erros de rede são retryable
    }
}

async function consultarCpfComRetry(cpf, config = new RetryConfig()) {
    let lastError;

    for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
    try {
    const response = await axios.get(
    `https://api.cpfhub.io/cpf/${cpf}`,
    {
    headers: { "x-api-key": process.env.CPFHUB_API_KEY },
    timeout: 10000,
    }
    );
    return response.data;
    } catch (error) {
    lastError = error;

    if (!config.isRetryable(error) || attempt === config.maxRetries) {
    break;
    }

    const delay = config.calculateDelay(attempt);
    console.log(`Tentativa ${attempt + 1} falhou. Retry em ${delay}ms...`);
    await new Promise((resolve) => setTimeout(resolve, delay));
    }
    }

    throw lastError;
}
  • calculateDelay -- implementa backoff exponencial com jitter para distribuir as retentativas no tempo
  • isRetryable -- avalia se o erro é temporário e merece nova tentativa
  • Loop controlado -- cada iteração incrementa o attempt e calcula um delay maior

Implementando a dead letter queue

Quando todas as tentativas de retry se esgotam, a consulta não deve ser simplesmente descartada. Uma DLQ armazena essas consultas falhadas para reprocessamento posterior.

const { Queue, Worker } = require("bullmq");
const Redis = require("ioredis");

const connection = new Redis({ host: "localhost", port: 6379 });

// Fila principal de consultas
const cpfQueue = new Queue("cpf-consultas", { connection });

// Dead Letter Queue
const cpfDLQ = new Queue("cpf-dlq", { connection });

// Worker que processa consultas
const worker = new Worker(
    "cpf-consultas",
    async (job) => {
    const { cpf, requestId } = job.data;
    try {
    const resultado = await consultarCpfComRetry(cpf);
    return resultado;
    } catch (error) {
    // Após esgotar retries, envia para DLQ
    await cpfDLQ.add("consulta-falhada", {
    cpf,
    requestId,
    error: error.message,
    originalJobId: job.id,
    failedAt: new Date().toISOString(),
    attempts: job.attemptsMade,
    });
    throw error;
    }
    },
    {
    connection,
    concurrency: 10,
    limiter: { max: 100, duration: 60000 },
    }
);

// Enfileirar uma consulta
async function enfileirarConsulta(cpf, requestId) {
    await cpfQueue.add("consultar", { cpf, requestId }, {
    attempts: 3,
    backoff: { type: "exponential", delay: 2000 },
    removeOnComplete: 1000,
    removeOnFail: false,
    });
}
  • BullMQ -- biblioteca robusta para filas de trabalho com Redis, suporta retry nativo e prioridades
  • DLQ dedicada -- fila separada que armazena jobs falhados com contexto completo do erro
  • Limiter -- controla a taxa de requisições para respeitar rate limits da API

Reprocessando a dead letter queue

A DLQ não é um cemitério de mensagens. Ela precisa de um mecanismo de reprocessamento para tentar novamente as consultas quando o problema for resolvido.

// Worker da DLQ - reprocessa periodicamente
const dlqWorker = new Worker(
    "cpf-dlq",
    async (job) => {
    const { cpf, requestId, failedAt } = job.data;

    // Verifica se a consulta não é muito antiga
    const horasDesdeAFalha =
    (Date.now() - new Date(failedAt).getTime()) / 3600000;

    if (horasDesdeAFalha > 24) {
    console.log(`Job ${requestId} expirado (${horasDesdeAFalha}h)`);
    return { status: "expired" };
    }

    // Tenta reprocessar com configuração mais tolerante
    const resultado = await consultarCpfComRetry(
    cpf,
    new RetryConfig({ maxRetries: 5, baseDelay: 5000 })
    );

    return resultado;
    },
    {
    connection,
    concurrency: 2,
    limiter: { max: 10, duration: 60000 },
    }
);

// Métricas da DLQ
async function obterMetricasDLQ() {
    const waiting = await cpfDLQ.getWaitingCount();
    const failed = await cpfDLQ.getFailedCount();
    return { pendentes: waiting, falhados: failed };
}
Parâmetro DLQValor recomendadoMotivo
Concurrency2-5Não sobrecarregar a API durante reprocessamento
TTL (expiração)24hConsultas muito antigas podem ser irrelevantes
Rate limit10/minBaixa prioridade, não competir com tráfego normal
Alerta> 100 pendentesIndica problema sistêmico que precisa de atenção

Perguntas frequentes

O que é necessário para implementar validação de CPF neste contexto?

A validação de CPF exige uma chamada à API com o número do documento e a chave de autenticação. A CPFHub.io retorna o status do CPF, nome do titular e data de nascimento em menos de 200ms, permitindo a verificação em tempo real durante o cadastro ou transação.

A API CPFHub.io funciona para todos os volumes de consulta?

Sim. O plano gratuito oferece 50 consultas por mês sem cartão de crédito — ideal para testes e projetos pequenos. Para volumes maiores, o plano Pro inclui 1.000 consultas mensais por R$149. Se o limite for ultrapassado, a API não bloqueia: cobra R$0,15 por consulta adicional.

Como garantir conformidade com a LGPD ao usar uma API de CPF?

Use o CPF apenas para a finalidade declarada ao titular, armazene apenas o necessário (não guarde o CPF cru se um token bastar), implemente controle de acesso aos logs de consulta e documente a base legal para o tratamento. A ANPD orienta que dados de identificação devem ser tratados com o princípio da necessidade.

Quanto tempo leva para integrar a API CPFHub.io?

A integração básica leva menos de 30 minutos: crie uma conta em cpfhub.io, gere a API key no painel e faça uma chamada GET para https://api.cpfhub.io/cpf/{CPF} com o header x-api-key. A documentação inclui exemplos em Python, Node.js, PHP, Java e outras linguagens.


Conclusão

Um sistema de retry inteligente com dead letter queue garante que nenhuma consulta de CPF se perca, mesmo em cenários de falha. O backoff exponencial com jitter protege o provedor de sobrecarga, enquanto a DLQ preserva as consultas para reprocessamento posterior. Teste essa abordagem com a API do cpfhub.io.

CPFHub.io

Pronto para integrar a API?

50 consultas gratuitas para testar agora. Sem cartão de crédito. Acesso imediato à documentação.

Redação CPFHub.io

Sobre a redação

Redação CPFHub.io

Time editorial especializado em APIs de CPF, identidade digital e compliance no mercado brasileiro. Produzimos guias técnicos, análises regulatórias e tutoriais sobre LGPD e KYC para desenvolvedores e líderes de produto.

WhatsAppFale conosco via WhatsApp