DevOps 28 min de leitura

Engenharia de Alta Demanda na Prática: Web Farm IIS, Controle de Estoque Atômico e Observabilidade Sob Picos Extremos

Guilherme Nascimento 0 comentários

Engenharia de Alta Demanda na Prática: Web Farm IIS, Controle de Estoque Atômico e Observabilidade Sob Picos Extremos

Engenharia de Alta Demanda na Prática: Web Farm IIS, Estoque Atômico e Observabilidade

Ilustração: Observabilidade Avançada e Engenharia de Sistemas em Servidores de Alta Demanda
HORA NERD // SRE & INFRAESTRUTURA 2026

Destaque de Engenharia
Deep-Dive Prático para Arquitetos .NET, Engenheiros de SRE e SysAdmins

Quando o pico de acessos atravessa a CDN, subir máquinas virtuais às cegas não impede o colapso: a rede e o kernel travam antes do processador. Este artigo resolve na prática as pontas soltas operacionais deixadas pelo mega-download do GTA VI, abordando o tuning de portas efêmeras no Windows Server, a arquitetura de Web Farm IIS sem pontos únicos de falha, reservas atômicas de estoque no Redis sem deadlock relacional e telemetria cirúrgica no Zabbix e Grafana.

✓
Kernel & TCP: Eliminação de esgotamento SNAT e filas do http.sys.
✓
Web Farm IIS: ARR + Keepalived VRRP L7, CCS e MachineKey estática.
✓
Estoque Atômico: Script Lua no Redis e mensageria assíncrona.
✓
SRE Telemetria: Monitoramento RED/USE com contadores PerfMon.

**Variações Alternativas de Título para SEO:**

1. *Web Farm IIS e Controle Atômico em Picos Extremos: Guia Prático de Engenharia e SRE*

2. *Arquitetura de Alta Demanda: Tuning de Kernel Windows, Estoque no Redis e Observabilidade RED/USE*

—

Em nosso artigo anterior (“O Mega-Download do GTA VI e o Teste de Estresse da Internet Global”), analisamos a camada macro de sustentação da internet: como estratégias de Multi-CDN, Anycast BGP e Origin Shielding evitam que petabytes de dados derrubem links de trânsito e saturem a borda (edge).

No entanto, há uma fronteira onde o cache estático deixa de existir e a realidade do processamento dinâmico se impõe: a origem da sua aplicação corporativa.

Quando um Flash Crowd — seja o lançamento mundial de um jogo de 150 GB com autenticação simultânea, a virada de lote de uma Black Friday ou a emissão de milhares de pedidos por segundo — atravessa a blindagem da CDN, todo o peso da concorrência recai sobre os servidores de aplicação, o kernel do sistema operacional e o motor de banco de dados.

É exatamente aqui que a maioria das arquiteturas corporativas colapsa. O sintoma clássico é desconcertante para equipes despreparadas: a CPU do servidor marca apenas 18% de uso, a memória RAM está com 60% livre, mas os usuários recebem erros 502 Bad Gateway, 503 Service Unavailable e timeouts de conexão.

Este artigo é a sequência técnica e prática. Não discutiremos teorias genéricas de escalabilidade. Desceremos ao nível de sockets TCP, chamadas de sistema, tuning de pools no IIS, concorrência atômica em memória e métricas cirúrgicas de telemetria para blindar sua infraestrutura contra o colapso.

—

1. A Matemática do Gargalo Invisível: Por que a Rede e o Kernel Caem Antes da CPU

Durante uma onda massiva de acessos simultâneos (Flash Crowd), o processador central da máquina raramente é o primeiro recurso a quebrar. O estrangulamento ocorre em camadas mais baixas: na pilha de rede do kernel do sistema operacional, nas filas de requisição de drivers em modo kernel (http.sys) e na tabela de estados de conexões TCP.

TERMINAL / bashUTF-8
+---------------------------------------------------------------------------------------------------+
|               O CAMINHO DA MORTE DE UMA REQUISIÇÃO SOB SOBRECARGA NO KERNEL                      |
+---------------------------------------------------------------------------------------------------+

   [ REQUISIÇÕES CONCORRENTES (10.000/s) ]
                     │
                     ▼
   [ PLACA DE REDE (NIC) / DRIVER ] ──► IRQ / DPC Overload
                     │
                     ▼
   [ KERNEL TCP/IP STACK ] ───────────► SYN Queue esgotada? ──► [ DROP SILENCIOSO / RST ]
                     │
                     ▼
   [ DRIVER HTTP.SYS (Modo Kernel) ] ─► QueueLength esgotada? ──► [ HTTP 503 SERVICE UNAVAILABLE ]
                     │                 (Padrão: 1.000 slots)      (Direto do Kernel, 0% CPU .NET)
                     ▼
   [ USER-SPACE WORKER (w3wp.exe) ] ──► ThreadPool Starvation ──► [ LATÊNCIA INFINITA / 504 TIMEOUT ]
                     │
                     ▼
   [ BACKEND / REVERSE PROXY ] ───────► SNAT / TIME_WAIT Loop ─► [ WSAENOBUFS (Erro 10055) ]
+---------------------------------------------------------------------------------------------------+

O Triângulo das Bermudas da Rede: Portas Efêmeras, TIME_WAIT e SNAT

Toda conexão TCP de saída (seja do seu balanceador de carga IIS ARR para os nós da Web Farm, ou dos seus servidores web para o SQL Server, Redis ou APIs de pagamento) é governada por uma tupla de 4 elementos:

Tupla de Conexão TCP: (IP Origem, Porta Origem, IP Destino, Porta Destino)

Quando o IP de destino e a porta de destino são fixos (por exemplo, 10.0.1.50:1433 do seu banco de dados), a única variável que permite criar novas conexões simultâneas é a porta efêmera de origem.

Por padrão histórico no Windows Server, o intervalo de portas dinâmicas alocadas pelo kernel para conexões de saída compreende a faixa de 49152 a 65535. Isso disponibiliza exatamente 16.384 portas efêmeras.

O Acúmulo em TIME_WAIT

Ao término de cada transação HTTP que não reutiliza conexões de forma estrita (Connection Keep-Alive mal configurado ou conexões fechadas pelo cliente), a ponta que iniciou o fechamento ativo da conexão TCP deve mantê-la no estado TIME_WAIT. O objetivo da RFC 793 é garantir que pacotes retardatários na rede não interfiram em uma nova conexão que venha a reutilizar a mesma tupla.

No Windows Server, o valor padrão do parâmetro TcpTimedWaitDelay é de 120 segundos (2 minutos):

Vazão Máxima de Fechamento: 16.384 portas / 120 segundos ≈ 136 conexões finalizadas por segundo

Se o seu sistema abrir e fechar mais de 136 conexões por segundo em direção ao mesmo backend ou serviço externo, o reservatório de portas efêmeras se esgota completamente em menos de dois minutos.

O resultado é a Exaustão de Portas SNAT (Source Network Address Translation Exhaustion). No log de eventos do Windows, surgem mensagens crípticas do Winsock:

TERMINAL / textUTF-8
System Error: 10055 (WSAENOBUFS)
"An operation on a socket could not be performed because the system lacked sufficient buffer space or because a queue was full."

Nesse momento, qualquer nova tentativa de conectar a um banco de dados, gateway de pagamento ou microserviço é abortada instantaneamente, mesmo com os servidores operando com memória e CPU abundantes.

A Fila Silenciosa do http.sys

No ecossistema Windows/IIS, o servidor web não abre diretamente uma porta socket em espaço de usuário (user-mode). As requisições HTTP chegam através do driver de kernel http.sys.

O http.sys gerencia uma fila em memória para cada Application Pool (QueueLength). O valor padrão de fábrica no IIS é de apenas 1.000 requisições.

Se sua aplicação .NET sofrer uma lentidão transitória de 200 milissegundos devido a um lock de banco de dados, e você estiver recebendo 6.000 requisições por segundo:
1. Em 170 milissegundos, a fila de 1.000 slots do http.sys enche completamente.
2. A partir da requisição 1.001, o http.sys descarta a requisição sumariamente devolvendo HTTP 503 Service Unavailable.
3. Essa rejeição ocorre dentro do ring 0 (kernel), sem sequer acordar o processo do IIS (w3wp.exe). Se você estiver olhando o Gerenciador de Tarefas ou a CPU do processo .NET, não verá nada fora do normal.

—

Diagnóstico e Tuning do Kernel Windows para Alta Densidade

Para preparar o kernel do Windows Server para sustentar dezenas de milhares de conexões simultâneas sem esgotamento de portas efêmeras ou buffers, execute as intervenções a seguir.

1. Verificação e Expansão de Portas Dinâmicas (PowerShell / Netsh)

Abra o PowerShell como Administrador e consulte o intervalo atual:

TERMINAL / powershellUTF-8
# Consultar faixa de portas dinâmicas IPv4 ativas
netsh int ipv4 show dynamicport tcp

Se o número de portas estiver próximo a 16.384, expanda imediatamente a faixa para permitir mais de 64.000 portas efêmeras:

TERMINAL / powershellUTF-8
# Inicia a faixa em 1025 e aloca 64.511 portas para saída TCP
netsh int ipv4 set dynamicport tcp start=1025 num=64511
netsh int ipv6 set dynamicport tcp start=1025 num=64511

# Validação do novo intervalo (1025 a 65535)
netsh int ipv4 show dynamicport tcp

2. Redução Agressiva do TcpTimedWaitDelay via Registro

Reduza o tempo em que o socket permanece preso em TIME_WAIT de 120 segundos para 30 segundos (mínimo seguro para redes corporativas modernas) e force o reuso de portas:

TERMINAL / powershellUTF-8
# Caminho dos parâmetros do protocolo TCP/IP no Registro
$tcpPath = "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters"

# 1. Reduz o ciclo de TIME_WAIT para 30 segundos (valor hexadecimal 0x1E)
Set-ItemProperty -Path $tcpPath -Name "TcpTimedWaitDelay" -Value 30 -Type DWord

# 2. Define o teto máximo de portas de usuário para 65534
Set-ItemProperty -Path $tcpPath -Name "MaxUserPort" -Value 65534 -Type DWord

# 3. Reduz retransmissões de pacotes SYN para evitar manter conexões mortas pendentes
Set-ItemProperty -Path $tcpPath -Name "TcpMaxDataRetransmissions" -Value 3 -Type DWord

# 4. Habilita proteção ativa contra ataques de SYN Flood
Set-ItemProperty -Path $tcpPath -Name "SynAttackProtect" -Value 1 -Type DWord

Write-Host "Tuning de parâmetros TCP/IP aplicado com sucesso. Reinício recomendado." -ForegroundColor Green

3. Diagnóstico em Tempo Real do Estado dos Sockets

Para monitorar o estado real das conexões durante um teste de carga ou momento de pico, execute este pipeline do PowerShell:

TERMINAL / powershellUTF-8
# Totaliza sockets agrupados pelo estado da máquina de estados TCP
Get-NetTCPConnection | Group-Object State -NoElement | Format-Table Count, Name -AutoSize

# Contagem específica de sockets travados em TIME_WAIT
$timeWaitCount = (Get-NetTCPConnection -State TimeWait -ErrorAction SilentlyContinue).Count
Write-Host "Total de Conexões em TIME_WAIT no momento: $timeWaitCount" -ForegroundColor Yellow

—

2. Web Farm IIS em Alta Disponibilidade (Arquitetura Ativo/Ativo para Ambientes .NET/C#)

Construir uma camada de aplicação corporativa baseada em Windows Server e .NET capaz de suportar grandes volumes exige eliminar categoricamente qualquer Ponto Único de Falha (Single Point of Failure – SPoF).

Abaixo detalhamos a topologia de alta disponibilidade recomendada para ambientes on-premises e nuvens híbridas:

TERMINAL / bashUTF-8
+---------------------------------------------------------------------------------------------------+
|               ARQUITETURA DE WEB FARM IIS ATIVO/ATIVO COM BORDA RESILIENTE                        |
+---------------------------------------------------------------------------------------------------+

                          [ TRÁFEGO CLIENTE / CDN ]
                                     │
                                     ▼
                    [ VIRTUAL IP (VIP): 192.168.10.100 ]
                                     │
                 ┌───────────────────┴───────────────────┐
                 ▼ (VRRP Heartbeat / Health Check L7)    ▼
       +───────────────────+                   +───────────────────+
       |   ARR NODE 01     |                   |   ARR NODE 02     |
       |  (IIS ARR + URL)  | ◄=== VRRP v2 ===► |  (IIS ARR + URL)  |
       |  Master (Ativo)   |                   |  Backup (Standby) |
       +───────────────────+                   +───────────────────+
                 │                                       │
                 └───────────────────┬───────────────────┘
                                     │ (Balanceamento L7: Weighted Round Robin / Healthz)
         ┌───────────────────────────┼───────────────────────────┐
         ▼                           ▼                           ▼
+─────────────────+         +─────────────────+         +─────────────────+
|  WEB NODE 01    |         |  WEB NODE 02    |         |  WEB NODE 03    |
|  (IIS Worker)   |         |  (IIS Worker)   |         |  (IIS Worker)   |
|  .NET 8 / C#    |         |  .NET 8 / C#    |         |  .NET 8 / C#    |
+─────────────────+         +─────────────────+         +─────────────────+
         │                           │                           │
         └───────────────────────────┼───────────────────────────┘
                                     │
          SMB 3.0 / Clustered Storage (Redundância Física com CA)
         ┌───────────────────────────────────────────────────────┐
         | • Shared Configuration (applicationHost.config)       |
         | • Centralized Certificate Store (CCS - *.pfx)         |
         | • Static MachineKey Padronizada em todos os nós       |
         └───────────────────────────────────────────────────────┘

Camada de Borda: ARR Sincronizado vs. O Perigo do Windows NLB

A primeira decisão crítica reside na camada de balanceamento frontal:

Por que Evitar o Windows NLB (Network Load Balancing) Tradicional

O recurso nativo do Windows Server (NLB) opera em camada 3/4 e apresenta sérios problemas operacionais em datacenters modernos:

  • Modo Unicast: Exige habilitar MAC Address Spoofing nos hipervisores (Hyper-V ou VMware ESXi). Sem switches configurados com Static ARP, causa Switch Flooding — o switch passa a retransmitir todo o tráfego da VIP para todas as portas da VLAN, degradando servidores adjacentes.
  • Modo Multicast: Associa um endereço IP Unicast a um MAC Multicast, quebrando a conformidade com a RFC 1812 e sendo bloqueado por muitos roteadores de borda e firewalls modernos.
  • Ausência de Health Check de Camada 7: O Windows NLB só sabe se a placa de rede da máquina responde a pacotes. Se o processo w3wp.exe travar com um deadlock de memória e começar a devolver HTTP 500 ou não responder requisições, o NLB continuará enviando metade dos usuários para a máquina morta.

A Alternativa Resiliente: Keepalived (VRRP) com IIS ARR

A implementação profissional consiste em dois servidores dedicados atuando como proxies reversos com IIS Application Request Routing (ARR) e URL Rewrite, ancorados por um IP Virtual (VIP) via protocolo VRRP (Virtual Router Redundancy Protocol) via Keepalived.

O Keepalived monitora ativamente a saúde da aplicação em Camada 7 (/healthz). Se o ARR primário falhar ou parar de responder a requisições HTTP válidas, o IP Virtual migra para o segundo nó em sub-segundos:

Exemplo de configuração para o nó Master do Keepalived (/etc/keepalived/keepalived.conf ou equivalente em Windows port/Linux Gateway):

TERMINAL / iniUTF-8
vrrp_script chk_arr_health {
    # Script que valida se o IIS ARR local está respondendo HTTP 200 na porta 80/443
    script "/usr/bin/curl -s -f -o /dev/null http://127.0.0.1/health-check"
    interval 1       # Executa a cada 1 segundo
    weight -20       # Reduz prioridade se falhar
    fall 2           # Considera morto após 2 falhas consecutivas
    rise 2           # Recupera após 2 sucessos
}

vrrp_instance VI_HORANERD_EDGE {
    state MASTER
    interface eth0
    virtual_router_id 51
    priority 101     # Prioridade maior no Node 01
    advert_int 1

    authentication {
        auth_type PASS
        auth_pass Secr3tP@ssVRRP
    }

    virtual_ipaddress {
        192.168.10.100/24 dev eth0 label eth0:vip
    }

    track_script {
        chk_arr_health
    }
}

—

Sincronização Stateless: O Trio Indispensável

Para que uma Web Farm com múltiplos servidores IIS funcione de forma totalmente intercambiável (stateless), três elementos precisam ser rigorosamente padronizados:

1. Shared Configuration (applicationHost.config centralizado)

Em vez de configurar sites, pools e regras em cada nó separadamente (o que inevitavelmente gera divergência de configuração), utiliza-se o Shared Configuration apontando para um compartilhamento de arquivos SMB 3.0 de alta disponibilidade com criptografia e cache offline local habilitado:

TERMINAL / powershellUTF-8
# Habilita Shared Configuration apontando para um repositório central SMB 3.0
# Importante: A conta utilizada deve ter privilégios de leitura na pasta de rede
Import-Module WebAdministration

$sharedPath = "\\storage-cluster.corp.local\IIS_Configurations\Farm01"
$encryptionKeyPassword = "SenhaCriptograficaExtremamenteForte123!"

Enable-IISSharedConfig -PhysicalPath $sharedPath `
                       -UserName "CORP\svc_iis_farm" `
                       -Password "SenhaDeServicoForte!" `
                       -KeyEncryptionPassword $encryptionKeyPassword

Write-Host "Shared Configuration ativado com sucesso em direção a: $sharedPath" -ForegroundColor Green

2. Centralized Certificate Store (CCS)

Elimine a necessidade de importar certificados SSL/TLS nos certificados de computador de cada nó da fazenda. Com o Centralized Certificate Store (CCS):

  • Os certificados PFX são armazenados em um único compartilhamento de rede seguro.
  • O nome do arquivo deve corresponder exatamente ao Server Name Indication (SNI) do domínio (ex.: horanerd.com.br.pfx ou _wildcard.horanerd.com.br.pfx).
  • O IIS carrega o certificado sob demanda na memória durante o handshake TLS com o cliente.
  • Renovação Zero Downtime: Para renovar o certificado SSL que vai expirar, basta sobrescrever o arquivo .pfx na pasta compartilhada. Nenhum iisreset é necessário; as próximas conexões TLS já utilizarão a chave nova.

Instalação e configuração via PowerShell:

TERMINAL / powershellUTF-8
# Instala o recurso Centralized SSL Certificate Support
Install-WindowsFeature -Name Web-CertProvider -IncludeManagementTools

# Configura o caminho do armazenamento central de certificados
Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
    -location '' `
    -filter "system.webServer/centralCertProvider" `
    -name "enabled" -value "True"

Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
    -filter "system.webServer/centralCertProvider" `
    -name "certStoreLocation" -value "\\storage-cluster.corp.local\SSL_Certs"

Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
    -filter "system.webServer/centralCertProvider" `
    -name "userName" -value "CORP\svc_iis_farm"

Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
    -filter "system.webServer/centralCertProvider" `
    -name "password" -value "SenhaDeServicoForte!"

3. Padronização Obrigatória de

O erro mais frequente em farms IIS é esquecer a configuração da .

Por padrão, o .NET Framework e o ASP.NET geram chaves criptográficas em memória de forma aleatória em cada inicialização do servidor. Se o usuário fizer uma requisição POST com um token Anti-Forgery (__RequestVerificationToken) ou cookie de autenticação criptografado pelo Nó 01, e o balanceador direcionar a requisição seguinte para o Nó 02, a decodificação falha catastrófica:

TERMINAL / textUTF-8
CryptographicException: The validation key cannot be verified.
Invalid viewstate or CSRF anti-forgery token mismatch.

Para evitar isso, defina chaves estáticas de validação e criptografia diretamente no web.config compartilhado da aplicação:

TERMINAL / xmlUTF-8
<configuration>
  <system.web>
    <!-- machineKey estática padronizada: obrigatória para nós em Web Farm -->
    <machineKey 
      validationKey="D92E4628BC5B80C34B8F3316E2DF8235A214C92E1B952136C78912E3F45D98A7B2C103F6789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE" 
      decryptionKey="4F62B83C52D756329A8C5B426E7DF980A123C45E67F89012" 
      validation="SHA256" 
      decryption="AES" />
  </system.web>
</configuration>

—

Tabela de Tuning de Produção: IIS & ASP.NET Worker Process

Nunca entre em um evento de alta demanda operando com as opções default do IIS. A tabela abaixo sintetiza os ajustes obrigatórios testados em ambientes corporativos de alto throughput:

Parâmetro de Configuração Valor Padrão (Default) Valor Recomendado (Produção) Justificativa de Engenharia e Impacto Real
**Queue Length**
`(appPool.queueLength)`
`1000` **`10000` a `65535`** Evita que o driver `http.sys` rejeite conexões em pico com erro `HTTP 503`. Cria um buffer para absorver picos repentinos enquanto as threads do worker processam.
**Idle Time-out (minutes)**
`(processModel.idleTimeout)`
`20` **`0` (Desativado)** Impede que o processo `w3wp.exe` seja finalizado por inatividade. Elimina a latência de “cold start” (compilação JIT e subida de memória) quando novo tráfego chega.
**Reciclagem Sobreposta**
`(Overlapping Recycling)`
Horários dinâmicos e reciclagem simultânea **Programada (04:00 AM) com `DisallowOverlappingRotation=false`** Garante que o pool antigo continue servindo conexões em trânsito enquanto o novo processo sobe e aquece. Nunca recicle nós simultaneamente na farm.
**Private Memory Limit**
`(recycling.periodicRestart.privateMemory)`
`0` (Ilimitado) **80% da RAM física útil do nó** Previne que um *memory leak* consuma toda a memória do Windows, causando swap em disco (*paging file thrashing*) e congelamento do sistema operacional.
**MaxConcurrentRequestsPerCPU**
`(aspnet.config)`
`5000` **`10000` a `20000`** Permite que o pipeline assíncrono do ASP.NET sustente um maior número de requisições `async/await` suspensas esperando retorno de I/O de rede/banco.
**MinFreeThreads / MaxWorkerThreads**
`()`
`minFreeThreads=”8″` **`minFreeThreads=”176″` `minLocalRequestFreeThreads=”152″`** Garante que sempre existam threads disponíveis no CLR ThreadPool para processar chamadas de saída e callbacks sem enfileiramento artificial.

Script de Automação de Tuning do Application Pool via PowerShell:

TERMINAL / powershellUTF-8
Import-Module WebAdministration

$appPoolName = "AppPool_HoraNerd_Production"

# 1. Ajuste da fila de requisições do http.sys para 65535
Set-ItemProperty "IIS:\AppPools\$appPoolName" -Name "queueLength" -Value 65535

# 2. Desativação do Idle Timeout (0 minutos = mantido vivo continuamente)
Set-ItemProperty "IIS:\AppPools\$appPoolName" -Name "processModel.idleTimeout" -Value ([TimeSpan]::FromMinutes(0))

# 3. Habilitação de StartMode AlwaysRunning (requer módulo Application Initialization)
Set-ItemProperty "IIS:\AppPools\$appPoolName" -Name "startMode" -Value "AlwaysRunning"

# 4. Configuração de limites de reciclagem segura
Set-ItemProperty "IIS:\AppPools\$appPoolName" -Name "recycling.periodicRestart.time" -Value ([TimeSpan]::FromMinutes(0))
Set-ItemProperty "IIS:\AppPools\$appPoolName" -Name "recycling.disallowOverlappingRotation" -Value "False"

Write-Host "Tuning do Application Pool [$appPoolName] aplicado com sucesso." -ForegroundColor Green

—

3. Sobrevivendo ao Checkout: Sala de Espera na Borda e Estoque Atômico sem Deadlock

O momento onde 95% dos comércios eletrônicos quebram durante um evento de alta demanda não é na renderização das páginas estáticas de catálogo, mas sim no funil de fechamento do pedido (Checkout).

O motivo técnico é simples: leitura é barata e cacheável; escrita transacional com controle de concorrência é cara e serializada.

TERMINAL / bashUTF-8
+---------------------------------------------------------------------------------------------------+
|               ARQUITETURA DE CHECKOUT RESILIENTE: SALA DE ESPERA E DISSOCIAÇÃO                    |
+---------------------------------------------------------------------------------------------------+

   [ 50.000 USUÁRIOS NO CHECKOUT ]
                 │
                 ▼
   [ VIRTUAL WAITING ROOM (EDGE) ] ──► Emite Token JWT criptografado com cota de passagem
                 │                     (Admite apenas 500 req/s controladas na origem)
                 ▼
   [ ORIGEM / API DE CHECKOUT ] ──────► Valida assinatura JWT em O(1)
                 │
                 ▼
   [ REDIS IN-MEMORY (LUA SCRIPT) ] ──► Decremento Atômico de Estoque em 0.2ms
                 │                     Sem Locks de Tabela | Sem Deadlocks SQL
                 ▼
   [ RESERVA CONFIRMADA EM MEMÓRIA ]
                 │
                 ▼
   [ MENSAGERIA ASSÍNCRONA ] ─────────► Enfileira evento "PedidoCriadoEvent" (RabbitMQ / Kafka)
                 │
                 ▼
   [ BACKGROUND WORKERS ] ────────────► Consumo cadenciado gravando no Banco Relacional (SQL Server)
                                       Pool de conexões estável em 50 conexões (Carga plana)
+---------------------------------------------------------------------------------------------------+

O Desastre da Contenção no Banco Relacional (SELECT ... FOR UPDATE)

A abordagem ingênua ensinada em tutoriais de banco de dados costuma implementar o fechamento de estoque com travas pessimistas:

TERMINAL / sqlUTF-8
-- Padrão perigoso sob alta concorrência:
BEGIN TRANSACTION;

SELECT Saldo 
FROM Estoque WITH (UPDLOCK, ROWLOCK) 
WHERE ProdutoId = 42;

-- Se Saldo > 0:
UPDATE Estoque 
SET Saldo = Saldo - 1 
WHERE ProdutoId = 42;

INSERT INTO Pedidos (ProdutoId, UsuarioId, DataHora) VALUES (42, 1001, GETDATE());

COMMIT TRANSACTION;

O que Acontece Quando 10.000 Pessoas Tentam Comprar o Mesmo Item Simultaneamente?

1. Contenção de Linha (Row Lock Contention): Uma única transação obtém a trava exclusiva (X-Lock) na linha do produto 42. As outras 9.999 transações ficam suspensas no estado WAITING, mantendo suas conexões abertas no pool de conexões do backend.
2. Esgotamento do Pool de Conexões (Connection Pool Exhaustion): As threads do C#/.NET ficam paradas aguardando a liberação do lock. Rapidamente, o pool de 100 conexões do ADO.NET se esgota. Requisições subsequentes falham com: Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool.
3. Escalação de Bloqueio (Lock Escalation): Para gerenciar a quantidade imensa de locks de linha, o otimizador do SQL Server decide converter os milhares de bloqueios de linha em um bloqueio de tabela inteira (Table Lock). Agora, ninguém mais consegue comprar nenhum produto do catálogo.
4. Deadlocks (Vítima 1205): Se o fluxo da transação atualizar simultaneamente a tabela de Estoque, Clientes e Auditoria, transações cruzadas concorrentes disputarão recursos em ordens invertidas. O motor relacional detecta o abraço mortal e aborta uma das operações com Error: 1205 (Transaction was deadlocked on lock resources and has been chosen as the deadlock victim).

—

Solução 1: Virtual Waiting Room na Borda com Token JWT

Para proteger a origem, implementa-se uma Sala de Espera Virtual (Virtual Waiting Room) executada diretamente nos nós de borda (Cloudflare Workers, Fastly Compute@Edge ou Envoy Proxy).

A borda monitora a vazão de processamento da origem. Se o volume de requisições de checkout ultrapassar a capacidade máxima nominal suportada pelo pool de conexões (ex.: 500 requisições/segundo), os usuários excedentes são retidos na borda em uma fila virtual interativa.

A admissão é controlada por um token JWT assinado criptograficamente (HMAC SHA-256):

TERMINAL / jsonUTF-8
{
  "sub": "user_892314",
  "queue_position": 142,
  "status": "admitted",
  "target_path": "/checkout/processar",
  "nbf": 1794553200,
  "exp": 1794553800,
  "iss": "horanerd-edge-gatekeeper"
}
  • Como Funciona na Origem:

Quando a requisição atinge o servidor IIS/.NET, um middleware de autenticação verifica unicamente a assinatura criptográfica e a validade temporal (nbf/exp) do JWT via chave pública em memória (uma operação de microsegundos que consome zero I/O de rede ou banco de dados).

  • Se o token for inválido ou estiver expirado, o usuário é imediatamente redirecionado de volta para o final da fila na borda com status HTTP 429 / 302.

—

Solução 2: Controle de Estoque Atômico com Script Lua no Redis

Para substituir completamente os locks pessimistas do SQL Server, transferimos a verdade transitória do estoque para o Redis.

O motor de execução do Redis executa comandos de forma estritamente single-threaded em relação aos dados. Quando um bloco de código escrito em Lua é executado dentro do Redis através do comando EVAL, ele é 100% atômico: nenhuma outra operação de leitura ou escrita pode ser intercalada durante a execução do script.

Script Lua de Decremento Atômico de Estoque (decremento_estoque.lua):

TERMINAL / luaUTF-8
-- ==============================================================================
-- SCRIPT LUA ATÔMICO DE DECREMENTO DE ESTOQUE PARA REDIS
-- KEYS[1]: Chave de saldo do produto (ex: "estoque:produto:42:saldo")
-- KEYS[2]: Chave de reserva/pedidos (ex: "estoque:produto:42:reservas")
-- ARGV[1]: Quantidade solicitada para compra (ex: 1)
-- ARGV[2]: ID do Usuário ou ID do Pedido
-- ARGV[3]: Tempo de expiração da reserva em segundos (ex: 600 = 10 minutos)
-- ==============================================================================

local stockKey = KEYS[1]
local reservationKey = KEYS[2]
local requestedQty = tonumber(ARGV[1])
local userId = ARGV[2]
local reservationTtl = tonumber(ARGV[3])

-- 1. Verifica se a chave do produto existe no Redis
local currentStock = redis.call('GET', stockKey)
if not currentStock then
    -- Código -2: Produto não cadastrado ou cache frio
    return -2
end

currentStock = tonumber(currentStock)

-- 2. Verifica se o saldo é suficiente
if currentStock < requestedQty then
    -- Código -1: Estoque insuficiente (Esgotado)
    return -1
end

-- 3. Verifica se o usuário já possui uma reserva ativa para evitar clique duplo
local existingReservation = redis.call('HEXISTS', reservationKey, userId)
if existingReservation == 1 then
    -- Código -3: Compra duplicada em andamento
    return -3
end

-- 4. Decrementa o estoque de forma atômica
redis.call('DECRBY', stockKey, requestedQty)

-- 5. Grava a reserva temporária associada ao usuário
redis.call('HSET', reservationKey, userId, requestedQty)

-- 6. Define a expiração do hash de reserva se ainda não tiver TTL configurado
local currentTtl = redis.call('TTL', reservationKey)
if currentTtl < 0 then
    redis.call('EXPIRE', reservationKey, reservationTtl)
end

-- Retorno 1: Sucesso absoluto na reserva do item
return 1

Integração no Backend C# / .NET via StackExchange.Redis:

TERMINAL / csharpUTF-8
using StackExchange.Redis;
using System.Security.Cryptography;

public class CheckoutService
{
    private readonly IDatabase _redisDb;
    private readonly string _luaScriptSha; // Script pré-carregado no Redis com SCRIPT LOAD

    public CheckoutService(IConnectionMultiplexer redisConnection, string luaScriptContent)
    {
        _redisDb = redisConnection.GetDatabase();
        // Carrega o script previamente no Redis para execução ultrarrápida via EVALSHA
        var server = redisConnection.GetServer(redisConnection.GetEndPoints().First());
        _luaScriptSha = server.ScriptLoad(luaScriptContent);
    }

    public async Task<CheckoutResult> ReservarItemAsync(int produtoId, string usuarioId, int quantidade)
    {
        var keys = new RedisKey[]
        {
            $"estoque:produto:{produtoId}:saldo",
            $"estoque:produto:{produtoId}:reservas"
        };

        var args = new RedisValue[]
        {
            quantidade,
            usuarioId,
            600 // 10 minutos para concluir o pagamento
        };

        // Executa o script atômico no Redis via hash SHA1
        var result = (int)await _redisDb.ScriptEvaluateAsync(_luaScriptSha, keys, args);

        return result switch
        {
            1 => CheckoutResult.Sucesso,
            -1 => CheckoutResult.EstoqueEsgotado,
            -2 => CheckoutResult.ProdutoInexistente,
            -3 => CheckoutResult.PedidoJaEmProcessamento,
            _ => CheckoutResult.ErroDesconhecido
        };
    }
}

Persistência Assíncrona com RabbitMQ / Kafka

Uma vez que o Redis confirmou o decremento com retorno 1 (em menos de 1 milissegundo), a requisição web retorna imediatamente sucesso para o cliente.

A gravação definitiva nas tabelas relacionais do SQL Server é despachada como um evento de mensageria assíncrona (PedidoCriadoEvent) para uma fila do RabbitMQ ou tópico do Kafka:

TERMINAL / bashUTF-8
[ Usuário ] ──► (POST /checkout) ──► [ Redis Lua: -1 Estoque ]
                                             │
      ◄────── Resposta HTTP 200 OK ──────────┤ (Latência Total: ~15ms)
                                             ▼
                                  [ Fila: "pedidos-novos" ]
                                             │
                                             ▼
                             [ Background Worker Service ]
                                             │
                             (Grava em lotes com transações suaves)
                                             ▼
                                    [ SQL Server DB ]

Esse desacoplamento transforma uma montanha russa de 20.000 transações concorrentes simultâneas em um fluxo constante e controlado de 100 gravações por segundo que o banco relacional processa sem qualquer elevação de temperatura.

—

4. Observabilidade Orientada a Sintomas: O que Monitorar no Zabbix e Grafana Durante a Guerra

Em operações de alta demanda, monitorar exclusivamente uso de CPU e Memória RAM é receita para o fracasso. É comum presenciar infraestruturas onde a CPU do cluster inteiro oscila entre 15% e 25%, mas nenhum usuário consegue concluir uma transação.

A observabilidade de engenharia deve ser estruturada a partir da combinação de duas metodologias consagradas de SRE:

  • Método RED (focado no Usuário / Serviços):

* Rate: Taxa de requisições por segundo recebidas.
* Errors: Quantidade e porcentagem de requisições que retornam status 5xx.
* Duration: Tempo de resposta medido em percentis (p50, p95, p99).

  • Método USE (focado nos Recursos da Máquina):

* Utilization: Porcentagem de tempo em que um recurso esteve ocupado.
* Saturation: O grau de trabalho enfileirado que não pôde ser atendido imediatamente.
* Errors: Falhas físicas ou lógicas reportadas pelo sistema operacional.

—

Contadores Essenciais do Windows Performance Monitor (PerfMon)

Para monitorar com precisão o comportamento do IIS e da runtime .NET, os seguintes contadores do PerfMon devem ser coletados ativamente pelo Zabbix Agent 2 e exibidos no Grafana:

Contador do PerfMon Categoria / Objeto O que Mede de Fato? Gatilho de Alarme Crítico (SRE)
`CurrentQueueSize` `\HTTP Service Request Queues(*)` Número de requisições paradas na fila de kernel do `http.sys` esperando atendimento pelo IIS. **`> 50` por mais de 15 segundos.** Indica que as threads do worker process estão saturadas.
`Requests Queued` `\ASP.NET` Requisições aceitas pelo IIS, mas paradas na fila do pipeline do ASP.NET aguardando thread livre no ThreadPool. **`> 20`.** Se esse valor subir, os clientes sofrerão timeouts de conexão.
`Requests Current` `\ASP.NET` Total de requisições sendo executadas concorrentemente no momento. Monitorar saturação do pool de threads do CLR.
`# of Exceps Thrown / sec` `\.NET CLR Exceptions(*)` Número de exceções de software lançadas por segundo pelo código C# (mesmo as capturadas por `try/catch`). **Picos anômalos (`> 100/s`).** Criação de exceções consome CPU para captura de stack trace.
`Contention Rate / Sec` `\.NET CLR LocksAndThreads(*)` Frequência por segundo com que threads tentam obter um lock em C# (`lock(obj)` ou `Monitor.Enter`) e encontram o recurso travado. **`> 50/s`.** Diagnóstico inequívoco de gargalo de concorrência em código em memória.
`Current Connections` `\Web Service(_Total)` Total de conexões TCP ativas estabelecidas nos sites do IIS. Detectar ataques DDoS ou conexões zumbis presas.
`Connection Failures` `\TCPv4` Conexões que falharam no estabelecimento do handshake de 3 vias (SYN). **`> 0 contínuo`.** Alerta máximo de saturação da fila SYN ou firewall de software.
`Connections Reset` `\TCPv4` Conexões que foram fechadas de forma anômala via pacote TCP RST. Saturação de recursos de rede ou descarte por timeout.

—

Configuração de Coleta Ativa no Zabbix Agent 2

No arquivo de configuração do agente Zabbix nos servidores Windows (C:\Program Files\Zabbix Agent 2\zabbix_agent2.conf), adicione as chaves de desempenho ativo utilizando a sintaxe de contadores nativos:

TERMINAL / iniUTF-8
# ==============================================================================
# HORA NERD - PERFMON COLLECTOR PARA IIS & .NET RUNTIME
# ==============================================================================

# 1. Tamanho da fila do driver http.sys para o pool principal de produção
PerfCounter = iis.httpsys.queue["\HTTP Service Request Queues(AppPool_HoraNerd_Production)\CurrentQueueSize", 5]

# 2. Requisições enfileiradas no pipeline do ASP.NET
PerfCounter = aspnet.requests.queued["\ASP.NET\Requests Queued", 5]

# 3. Requisições em processamento simultâneo
PerfCounter = aspnet.requests.current["\ASP.NET\Requests Current", 5]

# 4. Exceções CLR lançadas por segundo
PerfCounter = dotnet.exceptions.rate["\.NET CLR Exceptions(_Global_)\# of Exceps Thrown / sec", 10]

# 5. Taxa de contenção de locks entre threads
PerfCounter = dotnet.threads.contention["\.NET CLR LocksAndThreads(_Global_)\Contention Rate / Sec", 10]

# 6. Conexões ativas no IIS
PerfCounter = iis.connections.active["\Web Service(_Total)\Current Connections", 10]

# 7. Falhas de conexão TCP IPv4
PerfCounter = tcp.connection.failures["\TCPv4\Connection Failures", 10]

—

Arquitetura do Dashboard de Guerra no Grafana

Durante o evento, a equipe de engenharia deve manter um painel de controle único (Single Pane of Glass) organizado em 4 fileiras hierárquicas:

TERMINAL / bashUTF-8
+---------------------------------------------------------------------------------------------------+
|               DASHBOARD DE GUERRA (GRAFANA): VISÃO HIERÁRQUICA DE SRE                             |
+---------------------------------------------------------------------------------------------------+

 [ ROW 1: INDICADORES EXECUTIVOS (MÉTODO RED) ]
 ┌───────────────────────┐ ┌───────────────────────┐ ┌────────────────────────────────────────────┐
 | Throughput Total      | | Taxa de Erro HTTP 5xx | | Latência HTTP (Percentis)                  |
 | 14.850 req/s          | | 0.02% (Target < 0.1%) | | p50: 18ms | p95: 85ms | p99: 210ms         |
 └───────────────────────┘ └───────────────────────┘ └────────────────────────────────────────────┘

 [ ROW 2: SATURAÇÃO DE KERNEL & RUNTIME (MÉTODO USE) ]
 ┌───────────────────────┐ ┌───────────────────────┐ ┌────────────────────────────────────────────┐
 | Fila http.sys (Kernel)| | ASP.NET Requests Queue| | Concorrência de Locks CLR                 |
 | 0 slots (Normal)      | | 0 requisições         | | 2 contentions/s                          |
 └───────────────────────┘ └───────────────────────┘ └────────────────────────────────────────────┘

 [ ROW 3: CAMADA DE REDE & TCP SOCKETS ]
 ┌───────────────────────┐ ┌───────────────────────┐ ┌────────────────────────────────────────────┐
 | Conexões TIME_WAIT    | | Conexões Estabelecidas| | Utilização de Portas Efêmeras (%)          |
 | 4.120 sockets         | | 12.400 conexões ativas| | 18% da capacidade máxima (Seguro)          |
 └───────────────────────┘ └───────────────────────┘ └────────────────────────────────────────────┘

 [ ROW 4: INFRAESTRUTURA DE DADOS & SINCAL ]
 ┌───────────────────────┐ ┌───────────────────────┐ ┌────────────────────────────────────────────┐
 | Latência Redis (Lua)  | | Profundidade da Fila  | | Conexões Ativas no Banco Relacional        |
 | 0.42 ms (Média)       | | RabbitMQ: 850 msgs    | | 42 conexões (Pool máx: 100)               |
 └───────────────────────┘ └───────────────────────┘ └────────────────────────────────────────────┘

—

5. Checklist de “Dia D” (Validação Pré-Evento)

Utilize este roteiro de validação operacional antes de iniciar qualquer grande campanha promocional, virada de lote ou abertura de downloads massivos:

TERMINAL / markdownUTF-8
### FASE 1: T-72h (Preparação de Capacidade e Pré-Aquecimento)
- [ ] Executar script de Application Initialization (Warm-up) no IIS para compilar JIT e carregar rotas do ASP.NET.
- [ ] Configurar `Min Pool Size` nas Connection Strings do .NET (ex.: `Min Pool Size=50;`) para manter conexões abertas com o banco sem latência de handshake.
- [ ] Pré-alocar capacidade nas instâncias do Redis (desativar comandos perigosos como `KEYS *` e validar chave `maxmemory-policy`).
- [ ] Validar cotas de rede e limites de IOPS provisionados nas nuvens públicas ou storages locais.

### FASE 2: T-24h (Hardening e Ativação de Kill Switches)
- [ ] Decretar Code Freeze rigoroso (nenhum novo deployment de código em produção).
- [ ] Ativar Feature Flags / Kill Switches de componentes não essenciais:
  - [ ] Desativar motor de recomendações personalizadas na home page.
  - [ ] Substituir cálculo de frete dinâmico via API externa lenta por tabela de contingência em cache.
  - [ ] Desativar carregamento de avaliações em tempo real e comentários de clientes.
  - [ ] Suspender rotinas pesadas de relatórios analíticos e rotinas de backup que concorrem com o horário nobre.

### FASE 3: T-2h (Validação de Borda e Health Checks)
- [ ] Auditar endpoints de monitoramento de saúde: garantir que o `/healthz` seja ultraleve (não execute queries pesadas no banco de dados a cada probe).
- [ ] Validar a sincronização dos nós da Web Farm (Shared Configuration e Centralized Certificate Store).
- [ ] Realizar teste de failover real da VIP (Keepalived / ARR) forçando queda programada de um nó de borda.
- [ ] Ajustar TTL dos registros de DNS para 300 segundos para permitir chaveamento ágil de tráfego se necessário.

### FASE 4: T-Zero (Operação no Olho do Furacão)
- [ ] Equipe de SRE alocada na War Room com Dashboard do Grafana projetado.
- [ ] Monitoramento ativo dos contadores `http.sys QueueLength` e conexões `TIME_WAIT`.
- [ ] Operador de borda pronto para modular a vazão da Virtual Waiting Room caso a latência p99 ultrapasse 500ms.

—

6. Conclusão: Engenharia de Baixo Nível é o Verdadeiro Diferencial de Negócio

Momentos de demanda extrema não perdoam suposições. A diferença entre um faturamento milionário e um colapso que destrói a reputação de uma marca está no conhecimento profundo do que acontece quando o software encontra o hardware.

Subir instâncias às cegas sem ajustar a pilha TCP/IP do sistema operacional resulta apenas em mais máquinas paradas esperando por locks de banco de dados.

Ao dominar o tuning de kernel no Windows Server, implementar uma Web Farm IIS verdadeiramente resiliente com ARR e Keepalived, transferir a concorrência crítica de escrita para scripts atômicos em memória no Redis e acompanhar a batalha com telemetria baseada em RED e USE, sua empresa deixa de temer a tempestade de acessos e passa a operá-la com maestria matemática.

—


Consultoria em Engenharia de Tráfego & SRE
Web Farms IIS•Redis Atômico•Tuning de Kernel•Zabbix & Grafana

Sua infraestrutura aguenta o tráfego do seu maior evento ou vai travar no checkout?

A equipe de infraestrutura e confiabilidade da Hora Nerd, conduzida pelo Arquiteto de Sistemas Guilherme Nascimento, atua na reestruturação de Web Farms corporativas, eliminação de gargalos em bancos relacionais, implementação de filas de alta concorrência e parametrização cirúrgica de sistemas operacionais para momentos críticos.

Guilherme Nascimento ✓

Guilherme Nascimento

Especialista & Fundador

Especialista em Infraestrutura, Cloud/DevOps e Cibersegurança | +10 anos de experiência prática

Profissional com mais de uma década atuando no campo de batalha da tecnologia corporativa. Especialista em arquiteturas resilientes, automação com Docker/Kubernetes, Hardening de Active Directory, Linux avançado e observabilidade SRE. Criador do ecossistema Hora Nerd, dedicado a desmistificar a tecnologia para profissionais e estudantes.

Cloud & DevOps Active Directory Hardening Linux SysAdmin Docker & CI/CD Observabilidade SRE
HORA NERD EMPRESAS · DIAGNÓSTICO TÉCNICO

Sua empresa enfrenta desafios com este tema ou precisa destravar a infraestrutura?

Elimine gargalos de rede, problemas de lentidão em servidores, custos descontrolados de nuvem e riscos de segurança. Atuamos como especialista freelance emergencial ou consultoria técnica contínua.

Deixe seu comentário

Seu e-mail não será publicado. Campos obrigatórios estão marcados com *