Dead Letter Queue
Poison messages e quando usar DLQ
Classificando erros no payment-service para decidir entre retry e DLQ.
Nesta aula você vai
- Detectar mensagens envenenadas no consumo de payment
- Separar erro transitório de erro irrecuperável
- Definir critérios de envio para DLQ
Poison messages e quando usar DLQ
Objetivos
- Classificar falhas no
payment-service. - Evitar travar partição do Kafka por mensagem inválida.
- Preservar payload para investigação e reprocessamento.
Pré-requisitos
payment-serviceem Go com consumidor Kafka.- Tópico
payment.eventsjá existente. - Conhecimento básico de erros de parse e validação.
Conceito
Poison message é mensagem que sempre falha, mesmo após retry. Exemplo: schema inválido, valor obrigatório ausente ou regra de negócio impossível. Ela deve sair do fluxo principal para uma DLQ.
Estrutura de arquivos
services/payment-service/internal/
├── consumer/payment_consumer.go
├── errors/classifier.go
└── dlq/policy.go
Passo a passo
- Crie classificador de erros em
errors/classifier.go:
package errors
import "errors"
var (
ErrInvalidPayload = errors.New("invalid payload")
ErrBusinessRule = errors.New("business rule violation")
)
func IsPoison(err error) bool {
return errors.Is(err, ErrInvalidPayload) || errors.Is(err, ErrBusinessRule)
}
func IsRetryable(err error) bool {
return !IsPoison(err)
}
- Use no consumer (
consumer/payment_consumer.go):
func (c *Consumer) handleMessage(ctx context.Context, msg kafka.Message) error {
event, err := decodePaymentRequested(msg.Value)
if err != nil {
return errors.ErrInvalidPayload
}
if event.AmountCents <= 0 {
return errors.ErrBusinessRule
}
return c.gateway.Authorize(ctx, event.OrderID, event.AmountCents)
}
- Aplique política de decisão:
if err != nil {
if errors.IsPoison(err) {
c.logger.Error("mensagem envenenada", "key", string(msg.Key), "error", err)
return c.dlqPublisher.Publish(ctx, msg, err)
}
return err // transitório, será tratado por retry
}
Como testar
Envie payload inválido:
docker compose up -d kafka payment-service
echo '{"type":"payment.requested","payload":{"orderId":"o-1","amountCents":0}}' \
| kcat -P -b localhost:9092 -t payment.events
docker compose logs -f payment-service
Você deve ver log de erro classificado como poison (não transitório).
Dicas de projeto
- Classificação de erro deve ser determinística.
- Guarde motivo da falha em header da DLQ.
- Não confunda timeout de rede com erro de domínio.
Erros comuns
- Mandar todo erro para DLQ sem retry.
- Repetir indefinidamente payload inválido.
- Não ter métrica por tipo de erro.
Resumo
Você implementou a base de classificação para identificar poison messages e preparar o envio correto para DLQ.