quarta-feira, 22 de janeiro de 2020

Deu ruim!

Resultado de imagem para carroça puxada por burro
Após um tempo de operação do sistema se chega às tabelas com milhões de registros, outras tabelas com outros mil, muitos usuários ativos e concorrentes, a infraestrutura começa a apresentar sinais de incapacidade, e os problemas que nem imaginávamos ter, aparecem nos piores momentos. Com certeza vai tirar de você uma noite de sono, e do seu chefe.

Não é normal um usuário aguardar cinco minutos para receber a resposta de um servidor Web, como também não é normal se formar filas no servidor SQL, coisas que dificilmente surgem quando o sistema é recém inaugurado.

Então, vou listar alguns aspectos que considero relevantes, e algumas dicas baseados na experiência de muitas noites em claro:

Uma coisa que o programador nunca pensa é onde o seu sistema vai ser executado, e quais recursos irá consumir, mesmo porque isso é um problema do pessoal da infra. O foco é fazer aparecer o conteúdo na interface não importa como, e as vezes esse 'como' leva para caminhos tortuosos que consomem recursos em excesso do servidor. Programador não vai pensar nisso, e se houver uma diretiva na empresa para conduzir a solução, pode ser que ele contorne a diretiva.

Então a primeira coisa é pensar na arquitetura da aplicação:

  1. Windows: Processamento local (depende da capacidade da máquina), armazenamento no servidor. O processamento de uma máquina não influencia as demais conectadas, se uma falha o sistema se mantém operante. O custo da solução é maior.
  2. Web: Processamento e armazenamento remoto (servidor). Nesse caso uma máquina servidora atende ao processamento de todos os clientes, deve ser robusta para comportar processamento paralelo. Se o servidor falha o sistema cai. Se um script entra em loop e consome os recursos do servidor, o sistema cai para todo mundo. O custo da solução é menor.
  3. Smartdevice: O processamento normalmente é local, mas certas arquiteturas se comportam como sistemas Web. Sistemas nativos utilizam recursos nativos e são independentes dos demais, sendo o servidor um elemento de apoio apenas. Sistemas online são como sistemas Web.
Dessa forma, solução Web significa que o seu lindo programa disputa recursos com outros programas ao mesmo tempo, e se algum deles estiver consumindo recursos em excesso, todos ficam lentos e ruins. E como esse é o pior cenário de concorrência, vamos focar nele.

Consultas otimizadas: 
Os for eachs do programa NÃO podem percorrer a tabela inteira para localizar um registro. A consulta à tabela deve sempre escolher os filtros adequados, e retornar sempre o mínimo possível de registros. Tabelas grandes e pesadas não perdoam quando os índices estão incorretos e a pesquisa percorre alguns milhares de registros.

  • O melhor índice e filtro é aquele que devolve um registro
  • Às vezes é melhor usar um índice que devolva alguns registros e em seguida se busque nos mesmos o que desejamos do que ter que percorrer a tabela inteira
  • Não saia criando índices para todo lado para melhorar o desempenho da consulta, tabelas grandes não perdoam. O custo para organizar cada um dos índices em tabelas gigantes é altíssimo. Muito processamento é necessário para otimizar índices, levando a operação de manutenção dos registros ficar lenta, vai melhorar um pouco a consulta e vai transformar a operação em uma carroça.

A pior coisa que podemos localizar numa tabela é o nome de uma pessoa, pois o usuário não vai digitar o nome completo, vai entrar um LIKE bonito na programação e o desempenho final vai ser uma porcaria. Tabelas de pessoas costumam crescer muito.

Tela Carregada:
Um ponto que o programador também não se importa muito é com os controles que vão para a interface. Um grid, por exemplo, com muita coisa oculta pode não ser uma boa estratégia, muitas variáveis criadas na interface vão levar o desempenho do navegador de internet no chão. É melhor uma única variável com um monte de conteúdo do que um monte de variáveis com pouco conteúdo cada uma.

Tela armazena conteúdo persistente, mas não pode ser confundida com uma tabela. As vezes o programador consulta um monte de tabelas para montar a interface com tudo que consultou, e em seguida deixar o conteúdo oculto na tela. Isso é meio irracional, mas estou acostumado a ver programas assim. É melhor manter apenas o valor que vai permitir consultar todo o resto, se assim a pessoa quiser ao pressionar um botão de ação, por exemplo. No retorno do servidor se consulta tudo que tem direito.

A regra de ouro é a seguinte, somente coloque na interface aquilo que a pessoa PRECISA ver, e não queira adivinhar o que a pessoa precisa, pergunte primeiro. Sendo assim abrir interface com conteúdo pré-determinado não é uma boa opção. Encher a interface de informação inútil também não é uma boa opção. Ser sintético sim, é uma boa, pois vai reduzir a quantidade de informação a ser transmitida, vai aliviar a carga do servidor Web e vai acelerar a apresentação no navegador do cliente.

Finalmente é melhor mostrar a informação com um Textblock.Caption do que uma &variável com readonly. Variável normalmente é retransmitida para o servidor, mesmo as ocultas, e a interface vai ficar mais lenta, pois mais informação vai retornar para o servidor.

Enfim, use o mínimo de variáveis em uma interface web, opte por freestyle grid com textblock ao invés de grid com variáveis.

Pouco conteúdo na interface é o segredo para um desempenho feliz.

Warnings:
O Genexus normalmente coloca warnings nos programas quando ele é especificado, indicando situações esquisitas. Você por acaso tem costume de ler os avisos e corrigir o que o Genexus propõe?  Comece por aí então.

Monitoramento:
No servidor é que a festa de fato acontece, e claro, todos os programas devem estar corretos e operando. O servidor configurado e dimensionado corretamente, nem precisa dizer né.

Atenção à sazonalidade das operações, e caso existam momentos de pico é melhor redimensionar a infra para suportar a demanda, e neste caso a utilização de nuvem é mais indicada, pois existe a possibilidade da ampliação e redução acontecer automaticamente, trabalho manual a menos. Genexus funciona na nuvem muito bem, mesmo em Ev1, que apesar de rodar .Net 2 já fiz testes na Microsoft Azure e foi de boa.

Aqui é que a coisa pega, não é fácil monitorar a aplicação, principalmente porque tem milhares de soluções de monitoramento que fica até dificil escolher alguma. A forma mais pobre de se analisar a situação do servidor são os registros de log do servidor e gráficos de consumo, as vezes assustadores.


Quando o servidor chega nesse nível, melhor descobrir quais os processos estão rodando lá (Worker Proccess) na imagem abaixo.



Se você não dispõe de uma solução maravilhosa de monitoramento poderá executar um simples comando no console do Windows para descobrir quem está carregando as coisas por lá.

C:\WINDOWS\system32\inetsrv\appcmd.exe list requests > resultado.txt

Isso vai gerar um relatório (resultado.txt) de todos os processos que estão em execução, no seguinte formato:

REQUEST "850000028000026d" (url:GET /site1/p1.aspx, time:887531 msec, client:177.95.190.87, stage:ExecuteRequestHandler, module:IsapiModule)

REQUEST "3600000280000301" (url:GET /site2/p2.aspx?6bde67e80ecdcea9ff88ed41ec240efa698fe68f2dc38a2dcc26d72a26e444d4,gx-no-cache=1579701550528, time:876531 msec, client:177.95.134.119, stage:ExecuteRequestHandler, module:IsapiModule)

E um pequeno detalhe nessa confusão de letrinhas é o time:876531 msec, ou seja, o tempo de resposta que o tal programa está levando para responder ao cliente. Nesse caso, incríveis, 14 minutos.


A meta da equipe deve ser produzir programas leves que respondam rápido, no momento da crise, atender a mais clientes possíveis, se é que isso é possível.

Boa sorte, e se possível, bons sonhos.




Nenhum comentário: