Erros comuns em gRPC que você deve evitar
Se você já “fez funcionar” com gRPC, mas depois sofreu com performance, contratos quebrados ou problemas de observabilidade,
este guia é para você. Vou passar pelos erros mais frequentes e como corrigir com mais segurança e previsibilidade.
⚡ Latência e streaming
🔎 Observabilidade
🧯 Erros e timeouts
1) Quebrar compatibilidade do contrato (proto) no primeiro “ajuste”
gRPC depende diretamente do contrato do .proto.
Quando você muda campos ou semântica de forma destrutiva, o resultado quase sempre é: deploys coordenados demais,
erros difíceis de diagnosticar e migração cara.
- Renomear campos sem cuidado: evite renomeações que alterem o significado sem migração planejada.
- Reutilizar números de tags: nunca reaproveite um
field number. Tags identificam o campo na mensagem. - Remover campos cedo demais: remova só quando não houver clientes antigos. Em muitos cenários, “deprecated + período de convivência” é o caminho.
- Mudar tipos de forma incompatível: trocar
stringporint32(ou equivalente) quebra parsing e lógica dos consumidores. - Adicionar requisitos implícitos: se um campo passa a ser “obrigatório” na prática, você precisa tratar default/validação e compatibilidade.
2) Tratar streaming como “só mais um endpoint”
Streaming é poderoso, mas frequentemente usado sem estratégia. O custo aparece como memória alta, backpressure ignorado,
timeouts inesperados e consumo de recursos que “parece aleatório”.
- Ignorar limites e tamanhos: defina limites de payload, número máximo de mensagens e tamanho por item.
- Falta de backpressure: em fluxos longos, você precisa garantir que o produtor não “despeje” dados mais rápido do que o consumidor processa.
- Streams sem cancelamento: sempre considere
context/ cancelamento do lado do servidor e do cliente para interromper trabalho. - Erros tratados como “eventos” sem parada: em caso de erro relevante, finalize o stream com status adequado (não apenas log).
- Escolher o tipo de RPC errado:
Unarypara requisições pontuaisServer streamingquando o servidor gera uma sequênciaClient streamingquando o cliente envia uma sequência para agregaçãoBidirectionalquando ambos participam de forma contínua
3) Timeouts e controle de erro inconsistentes (e o caos começa)
gRPC oferece status codes e mensagens que devem ser usados com intenção.
O erro comum é devolver “qualquer coisa” e deixar o cliente descobrir sozinho — normalmente tarde demais.
- Sem timeout: requisições sem deadline podem “segurar” threads, conexões e recursos sob carga.
- Timeout fixo e irreal: não escolha um valor genérico; modele com base em SLO/SLAs e no comportamento do seu sistema.
- Tratar o mesmo erro como “tudo dá pra tentar de novo”:
retry deve ser criterioso (ex.: rede/transiente). Erros de validação e negócio não devem virar retry agressivo. - Status code errado: use códigos coerentes:
INVALID_ARGUMENTpara validaçãoNOT_FOUNDquando o recurso não existeUNAVAILABLEpara indisponibilidade/transienteDEADLINE_EXCEEDEDquando o prazo estouraINTERNALpara falhas inesperadas
- Perder contexto: inclua detalhes relevantes (sem vazar dados sensíveis) para debug e suporte.
// Exemplo genérico (pseudocódigo semelhante a Go/Java):
// Objetivo: deadline + mapeamento correto de status + cancelamento.
func (s *Server) CreateOrder(ctx context.Context, req *CreateOrderRequest) (*CreateOrderResponse, error) {
// Garanta deadline no servidor (ou dependa do cliente, mas seja consistente)
ctx, cancel := context.WithTimeout(ctx, 800*time.Millisecond)
defer cancel()
if req == nil || req.CustomerId == 0 {
return nil, status.Error(codes.InvalidArgument, "customer_id inválido")
}
// Respeite cancelamento/backpressure
if err := s.repo.SaveOrder(ctx, req); err != nil {
// Erro de dependência pode virar Unavailable/DeadlineExceeded
if errors.Is(err, context.DeadlineExceeded) {
return nil, status.Error(codes.DeadlineExceeded, "tempo excedido para persistir pedido")
}
if errors.Is(err, repo.ErrTransient) {
return nil, status.Error(codes.Unavailable, "falha temporária ao salvar pedido")
}
return nil, status.Error(codes.Internal, "falha inesperada ao salvar pedido")
}
return &CreateOrderResponse{OrderId: 123}, nil
}
4) Observabilidade fraca: não “enxerga” latência, distribuição e causa
gRPC pode ser rápido — até o momento em que não é. Sem métricas e tracing, você vira refém de “chutes” e logs soltos.
O resultado: incidentes longos e dificuldade para provar regressões.
- Sem métricas por método: acompanhe pelo menos latência (p50/p95/p99), taxa de erro e contagem de chamadas por RPC.
- Sem correlação de request: use identificadores (trace/span) para ligar client ↔ server e dependências internas.
- Ignorar status code como métrica: “erro” genérico não basta. Métrica por
codes.*acelera o diagnóstico. - Logs sem estrutura: log em texto livre atrapalha agregação. Prefira campos e padronize o que vai em cada log.
- Não instrumentar interceptors: interceptors são o lugar natural para adicionar headers de rastreio, métricas e tratamento transversal.
Quer melhorar ainda mais? Continue pelo yurideveloper.com.br
Se você curte gRPC e quer ganhar eficiência de arquitetura e operação, recomendo ler os próximos posts:
programação defensiva, timeouts e retries, e boas práticas de observabilidade.
Sou Apaixonado pela programação e estou trilhando o caminho de ter cada diz mais conhecimento e trazer toda minha experiência vinda do Design para a programação resultando em layouts incríveis e idéias inovadoras! Conecte-se Comigo!