Os gargalos mais comuns
APIs tendem a sofrer com payload excessivo, consulta ineficiente, falta de cache, integração síncrona demais, retry mal desenhado e pouca visibilidade sobre o que realmente está estourando latência ou erro.
Pilares para escalar com previsibilidade
- Contratos claros e versionados
- Observabilidade em latência, erro e throughput
- Filas e processamento assíncrono onde faz sentido
- Cache e otimização de leitura sem quebrar consistência
- Idempotência e controle de retry
Modernizando sistemas .NET?
Veja como a ArrexTec ajuda empresas nessa situação — sem reescrever tudo do zero.
Ver serviço de APIsQuando a arquitetura precisa mudar
Se a escala já está amarrada a domínios com ritmos diferentes, integrações sensíveis e responsabilidade confusa, talvez o próximo passo não seja só tuning. Pode ser redesenho arquitetural com APIs, eventos e serviços melhor distribuídos.
Medir antes de otimizar: esqueça a média
A primeira regra de escalabilidade é não adivinhar. E a segunda é não olhar a latência média — ela esconde exatamente o problema que você precisa achar.
Uma API com média de 120 ms pode ter p99 de 8 segundos. Significa que 1% das requisições demora oito segundos, e esse 1% costuma ser o cliente maior, com mais dados, na hora de pico. A média diz que está tudo bem; o p99 diz onde o negócio está sendo prejudicado.
Acompanhe sempre p50, p95 e p99 separados, com throughput e taxa de erro. No .NET, o OpenTelemetry já expõe isso sem instrumentação manual, e o dotnet-counters resolve diagnóstico pontual em produção sem precisar de ferramenta externa.
Sem esses números, otimização vira palpite — e o palpite costuma cair no lugar errado.
O gargalo quase sempre está no banco
Antes de pensar em mais instâncias, vale olhar o acesso a dados. É onde está a maior parte do tempo perdido em API corporativa .NET:
- N+1 — a consulta traz 200 pedidos e, ao percorrer a lista, dispara uma query por item. Duzentas idas ao banco onde deveria haver uma. É o problema mais comum e o mais fácil de corrigir.
- Materializar o que não usa — carregar a entidade completa para exibir três colunas. Projetar direto para DTO com
Selectreduz tráfego e alocação de memória. - Tracking desnecessário — o EF Core rastreia mudanças por padrão. Em consulta somente leitura,
AsNoTracking()elimina esse trabalho. - Falta de índice — a consulta que era rápida com dez mil registros deixa de ser com dois milhões. O plano de execução mostra isso em segundos.
- Paginação inexistente — endpoint que devolve a tabela inteira funciona até o dia em que não funciona mais, e falha exatamente quando o cliente cresceu.
Corrigir esses cinco pontos costuma render mais que qualquer mudança de infraestrutura, e custa incomparavelmente menos.
O erro de concorrência que derruba a API inteira
Existe uma falha específica em .NET que não aparece em teste e destrói a aplicação sob carga: bloquear thread esperando operação assíncrona. É o famoso .Result ou .Wait() em cima de um método async.
Cada chamada dessas prende uma thread do pool sem fazer nada. Com pouca carga, ninguém percebe. Sob carga, o pool esgota, novas requisições ficam na fila esperando thread livre, a latência dispara e a API para de responder — sem CPU alta, sem erro no log, sem causa aparente.
A correção é async de ponta a ponta: se o método chama algo assíncrono, ele é async e usa await. Sem exceção no caminho da requisição. É a mudança de maior impacto por linha alterada em APIs .NET com problema de escala.
Cache: onde ele ajuda e onde ele engana
Cache é a ferramenta mais eficaz e a mais mal usada. Três níveis, com custos diferentes:
- Em memória — o mais rápido, mas cada instância tem o seu. Com várias réplicas, o mesmo dado fica em versões diferentes.
- Distribuído — Redis e similares. Consistente entre instâncias, com custo de rede a cada acesso.
- HTTP —
ETageCache-Control. O mais barato de todos, porque a requisição nem chega na sua aplicação.
A parte difícil nunca é guardar, é invalidar. Cache sem estratégia de expiração clara transforma bug de dado desatualizado em algo intermitente e quase impossível de reproduzir — e isso é pior que a lentidão que ele resolveu.
Regra de segurança: só coloque em cache o que pode ficar desatualizado por um período que o negócio aceite explicitamente. Se ninguém souber responder qual é esse período, não é candidato a cache.
Proteger a API de si mesma
Escalar não é só aguentar mais carga; é degradar de forma controlada quando a carga passa do limite. Quatro mecanismos, todos disponíveis no .NET moderno sem dependência externa relevante:
- Rate limiting — nativo desde o .NET 7. Impede que um cliente mal comportado consuma a capacidade de todos.
- Timeout em toda chamada externa — sem timeout, uma dependência lenta vira uma fila crescente de requisições penduradas.
- Circuit breaker — quando a dependência está fora, falhe rápido em vez de acumular espera. Polly resolve isso com poucas linhas.
- Idempotência — se o cliente faz retry, a operação não pode ser aplicada duas vezes. Chave de idempotência no comando é o que evita o pedido duplicado que aparece depois no financeiro.
Sem esses quatro, retry automático deixa de ser resiliência e passa a ser amplificador de falha: a API já sobrecarregada recebe o triplo de requisições.
Perguntas frequentes
Como saber se o problema é a API ou o banco?
Instrumente as duas camadas separadamente. Se a latência da aplicação sobe junto com o tempo de query, o gargalo é dados. Se a query está rápida mas a requisição demora, olhe esgotamento do pool de threads ou de conexões — os dois sintomas se parecem e têm causas opostas.
Vale mais escalar horizontalmente ou otimizar o código?
Otimize primeiro, quase sempre. Adicionar instâncias multiplica o custo e costuma piorar o gargalo quando ele está no banco — mais réplicas significam mais conexões concorrendo pelo mesmo recurso. Escalonamento horizontal resolve limite de CPU e memória, não consulta ineficiente.
Async/await melhora a performance?
Não melhora a latência de uma requisição isolada. Melhora a capacidade de atender requisições simultâneas, porque libera a thread durante a espera de I/O. Em API sob carga, isso é a diferença entre atender e enfileirar.
Quando usar fila em vez de resposta síncrona?
Quando o cliente não precisa do resultado imediatamente e a operação é demorada ou depende de terceiros instáveis. Processamento de arquivo, envio de notificação e integração com sistema externo lento são candidatos naturais. A API responde aceitando o pedido e o trabalho acontece fora do ciclo da requisição.
Preciso migrar para microsserviços para escalar?
Não. A maioria dos problemas de escala em API corporativa se resolve com consulta eficiente, cache e async correto — sem tocar na arquitetura. Microsserviços fazem sentido quando partes do sistema precisam escalar de forma independente, e o tema é tratado em arquitetura de microsserviços com .NET 8.