trilha

System Design/02 - Componentes/Communication2 min

TCP

Perguntas-guia
  • Que problema isso resolve?
  • Quando usar / quando NÃO usar?
  • Qual o principal trade-off?
  • Como isso falha em produção?

Conceito

Protocolo de transporte orientado a conexão que garante entrega ordenada e sem duplicatas — construído sobre um IP que não garante nada.

O que ele adiciona sobre o IP

Garantia Mecanismo
Entrega ACK + retransmissão por timeout
Ordem Número de sequência
Sem duplicata Deduplicação por número de sequência
Controle de fluxo Janela anunciada pelo receptor
Controle de congestionamento Slow start, AIMD, ajuste da janela
Integridade Checksum

Handshake e encerramento

Abertura (3 vias):   SYN → SYN-ACK → ACK      = 1 RTT antes do primeiro byte
Com TLS 1.3:         +1 RTT                    = 2 RTT total
Encerramento:        FIN → ACK → FIN → ACK

O custo de abertura é o motivo de connection pooling e keep-alive existirem: em API de alta frequência, o handshake pode custar mais que a requisição.

Estados que aparecem em produção

TIME_WAIT (espera ~2×MSL antes de liberar a porta) acumula em servidores com muitas conexões curtas e pode esgotar portas efêmeras. CLOSE_WAIT acumulado quase sempre indica bug de aplicação: o socket foi fechado do outro lado e o código nunca chamou close().

Trade-offs

As garantias custam latência. Head-of-line blocking é o efeito mais relevante: um pacote perdido bloqueia a entrega de todos os pacotes seguintes, mesmo que já tenham chegado. Em HTTP/2, que multiplexa vários streams numa conexão TCP, uma perda trava todos os streams — foi exatamente esse limite que motivou o HTTP/3 sobre QUIC (UDP).

Controle de congestionamento também tem custo: o slow start faz cada conexão nova começar devagar, o que penaliza requisições curtas.

E há o caso em que TCP simplesmente não serve: quando o dado envelhece mais rápido que a retransmissão. Retransmitir áudio de 200 ms atrás não ajuda ninguém — daí UDP.

Exemplo prático

Uma API interna que abre uma conexão TCP nova a cada chamada, entre serviços com 1 ms de RTT:

sem pool:  handshake (1 RTT) + TLS (1 RTT) + requisição = ~3 ms
com pool:  requisição = ~1 ms

Três vezes mais rápido sem tocar em nenhuma linha de lógica. E, sob carga alta, o servidor deixa de acumular TIME_WAIT — que é o modo pelo qual esse problema costuma aparecer primeiro: não como lentidão, mas como "esgotamento de portas efêmeras" em horário de pico.

Relacionado


Parte de Communication · roadmap.sh/system-design

Buscar

Busca por título, seção e texto das notas