Como implementar dead letter queue para consultas de CPF que falharam

Aprenda a implementar uma dead letter queue para tratar consultas de CPF que falharam e garantir que nenhuma validação seja perdida.

Redação CPFHub.io
Redação CPFHub.io
··7 min de leitura
Como implementar dead letter queue para consultas de CPF que falharam

Uma dead letter queue (DLQ) garante que nenhuma consulta de CPF seja perdida silenciosamente em caso de timeout, erro de rede ou indisponibilidade temporária da API. Com a CPFHub.io, a integração é feita via GET https://api.cpfhub.io/cpf/{CPF} com o header x-api-key; ao capturar as falhas em uma DLQ com Redis ou Bull, você cria uma camada de resiliência que reprocessa automaticamente as validações que falharam.

Introdução

Em qualquer integração com APIs externas, falhas são inevitáveis. Timeouts, erros de rede e indisponibilidades temporárias podem impedir que uma consulta de CPF seja concluída. Sem um mecanismo para capturar e reprocessar essas falhas, consultas são perdidas silenciosamente — e com elas, validações críticas para o seu negócio.


O que é uma dead letter queue

Uma dead letter queue é uma fila secundária que recebe mensagens que não puderam ser processadas com sucesso após um número definido de tentativas. Em vez de descartar a consulta, ela é movida para a DLQ para análise e reprocessamento posterior.

O fluxo típico:

  1. A consulta de CPF é enfileirada na fila principal.
  2. O worker tenta processar (chamar a API).
  3. Se falhar, tenta novamente (retry).
  4. Após N tentativas sem sucesso, move para a DLQ.
  5. A equipe analisa a DLQ e decide como tratar.

Quando uma consulta vai para a DLQ

CenárioTentativas recomendadasMotivo da DLQ
Timeout3API temporariamente lenta
Erro de rede3Problema de conectividade
Status 500/5033Erro no servidor da API
Status 4011Chave inválida (não adianta retentativa)
Status 4001CPF mal formatado (não adianta retentativa)

Implementação com Redis

import requests
import json
import time
import redis
from datetime import datetime

r = redis.Redis(host='localhost', port=6379, db=0)

FILA_PRINCIPAL = 'fila:consulta_cpf'
FILA_DLQ = 'fila:consulta_cpf:dlq'
MAX_TENTATIVAS = 3

headers = {
    'x-api-key': 'SUA_CHAVE_DE_API',
    'Accept': 'application/json'
}

def enfileirar_consulta(cpf, contexto=None):
    mensagem = {
    'cpf': cpf,
    'tentativa': 0,
    'contexto': contexto or {},
    'criado_em': datetime.utcnow().isoformat()
    }
    r.lpush(FILA_PRINCIPAL, json.dumps(mensagem))

def processar_fila():
    while True:
    item = r.brpop(FILA_PRINCIPAL, timeout=5)
    if not item:
    continue

    mensagem = json.loads(item[1])
    cpf = mensagem['cpf']
    tentativa = mensagem['tentativa'] + 1

    try:
    response = requests.get(
    f'https://api.cpfhub.io/cpf/{cpf}',
    headers=headers,
    timeout=15
    )

    if response.status_code == 200:
    resultado = response.json()
    salvar_resultado(cpf, resultado, mensagem['contexto'])
    print(f'OK: {cpf} (tentativa {tentativa})')
    continue

    elif response.status_code in [400, 401]:
    # Erros que nao melhoram com retry
    mover_para_dlq(mensagem, f'http_{response.status_code}', tentativa)
    continue

    except requests.exceptions.Timeout:
    pass
    except requests.exceptions.ConnectionError:
    time.sleep(5)

    # Retentativa ou DLQ
    if tentativa >= MAX_TENTATIVAS:
    mover_para_dlq(mensagem, 'max_tentativas', tentativa)
    else:
    mensagem['tentativa'] = tentativa
    mensagem['ultima_tentativa'] = datetime.utcnow().isoformat()
    r.lpush(FILA_PRINCIPAL, json.dumps(mensagem))
    # Backoff exponencial
    time.sleep(2 ** tentativa)

def mover_para_dlq(mensagem, motivo, tentativa):
    mensagem['dlq_motivo'] = motivo
    mensagem['dlq_tentativas'] = tentativa
    mensagem['dlq_timestamp'] = datetime.utcnow().isoformat()
    r.lpush(FILA_DLQ, json.dumps(mensagem))
    print(f'DLQ: {mensagem["cpf"]} - {motivo} (apos {tentativa} tentativas)')

def salvar_resultado(cpf, resultado, contexto):
    r.setex(
    f'cpf:resultado:{cpf}',
    86400,
    json.dumps(resultado)
    )

Implementação com Bull (Node.js)

const Queue = require('bull');

const filaConsulta = new Queue('consulta-cpf', {
    redis: { host: 'localhost', port: 6379 },
    defaultJobOptions: {
    attempts: 3,
    backoff: {
    type: 'exponential',
    delay: 2000
    },
    removeOnComplete: true,
    removeOnFail: false
    }
});

// Fila DLQ
const filaDLQ = new Queue('consulta-cpf-dlq', {
    redis: { host: 'localhost', port: 6379 }
});

// Processador principal
filaConsulta.process(async (job) => {
    const { cpf, contexto } = job.data;

    const response = await fetch(`https://api.cpfhub.io/cpf/${cpf}`, {
    method: 'GET',
    headers: {
    'x-api-key': process.env.CPFHUB_API_KEY,
    'Accept': 'application/json'
    },
    signal: AbortSignal.timeout(15000)
    });

    if (response.status === 401) {
    // Mover direto para DLQ (sem retry)
    await filaDLQ.add({
    cpf,
    contexto,
    motivo: 'api_key_invalida',
    tentativas: job.attemptsMade + 1
    });
    return;
    }

    if (!response.ok) {
    throw new Error(`HTTP ${response.status}`);
    }

    return response.json();
});

// Listener de falha final
filaConsulta.on('failed', async (job, err) => {
    if (job.attemptsMade >= job.opts.attempts) {
    await filaDLQ.add({
    cpf: job.data.cpf,
    contexto: job.data.contexto,
    motivo: err.message,
    tentativas: job.attemptsMade
    });
    console.log(`DLQ: ${job.data.cpf} - ${err.message}`);
    }
});

// Enfileirar consulta
async function solicitarConsulta(cpf, contexto = {}) {
    await filaConsulta.add({ cpf, contexto });
}

Monitorando a DLQ

Crie um endpoint para acompanhar as mensagens na DLQ:

from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/api/dlq/status')
def dlq_status():
    tamanho = r.llen(FILA_DLQ)
    mensagens = []
    for i in range(min(tamanho, 20)): # ultimas 20
    msg = r.lindex(FILA_DLQ, i)
    if msg:
    mensagens.append(json.loads(msg))

    # Agrupar por motivo
    motivos = {}
    for msg in mensagens:
    motivo = msg.get('dlq_motivo', 'desconhecido')
    motivos[motivo] = motivos.get(motivo, 0) + 1

    return jsonify({
    'total_dlq': tamanho,
    'por_motivo': motivos,
    'ultimas_mensagens': mensagens[:10]
    })

Reprocessando a DLQ

Após resolver o problema (renovar a chave, aguardar o restabelecimento da conectividade), reprocesse as mensagens:

def reprocessar_dlq(filtro_motivo=None):
    tamanho = r.llen(FILA_DLQ)
    reprocessados = 0
    mantidos = 0

    mensagens_manter = []

    for _ in range(tamanho):
    item = r.rpop(FILA_DLQ)
    if not item:
    break

    mensagem = json.loads(item)

    if filtro_motivo and mensagem.get('dlq_motivo') != filtro_motivo:
    mensagens_manter.append(item)
    mantidos += 1
    continue

    # Resetar tentativas e reenviar para fila principal
    mensagem['tentativa'] = 0
    mensagem['reprocessado_em'] = datetime.utcnow().isoformat()
    r.lpush(FILA_PRINCIPAL, json.dumps(mensagem))
    reprocessados += 1

    # Devolver mensagens que nao foram reprocessadas
    for msg in mensagens_manter:
    r.lpush(FILA_DLQ, msg)

    print(f'Reprocessados: {reprocessados} | Mantidos: {mantidos}')

# Reprocessar apenas falhas por tentativas esgotadas
reprocessar_dlq('max_tentativas')

Alertas para a DLQ

Configure alertas quando a DLQ começar a crescer. O guia de boas práticas de segurança em APIs da OWASP recomenda monitorar ativamente filas de erro para detectar padrões anômalos:

  • Threshold de tamanho -- Alerta se a DLQ tiver mais de 10 mensagens.

  • Taxa de crescimento -- Alerta se mais de 5 mensagens entrarem na DLQ em 1 hora.

  • Mensagens antigas -- Alerta se houver mensagens na DLQ há mais de 24 horas.


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 ~900ms, 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

Uma dead letter queue garante que nenhuma consulta de CPF seja perdida silenciosamente. Ao capturar falhas, permitir reprocessamento e gerar alertas, você cria uma camada de resiliência que mantém a integridade dos seus processos de validação mesmo diante de falhas temporárias.

Cadastre-se em cpfhub.io — 50 consultas mensais gratuitas, sem cartão de crédito — e comece hoje mesmo.

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