Desafio da Semana #11

DESAFIO DA SEMANA #11

Por: Roberto Alexis Farah

Olá pessoal!

O desafio dessa semana se baseia num incidente de suporte real que trabalhei recentemente.

Primeiro um pouco de história: o incidente era um sintoma de hang (100% de cpu) em quatro servidores IIS, três IIS 5 rodando em Windows 2000 e um IIS 6 rodando em Windows 2003. Todos os servidores acessavam quatro servidores SQL Server 2000 Enterprise rodando em máquinas Windows 2000 Advanced Server.

Como sempre a parte desafiante e que tomou tempo é de se isolar o problema. Uma vez isolado achar a solução é um desafio menor, muitas vezes é algo direto. Ainda algumas vezes alguém menciona algo do tipo: “Ah! Não acredito que era isso!”.

Nesse caso em específico o sintoma de alta CPU nos servidores IIS foi isolado para uma determinada página ASP e, posteriormente, para um fragmento de código nessa página ASP. Portanto, partindo-se de vários executáveis cheguei em um fragmento de código fonte; comprovadamente o culpado, embora aparentemente pareça um inofensivo fragmento de código. J

Durante a investigação me baseei, num primeiro instante, nos únicos dumps que tínhamos, que não foram coletados durante a ocorrência do sintoma. Ainda assim foi possível se criar uma hipótese inicial, onde suspeitei do banco de dados após a análise do dump. Entretanto, uma nova coleta de dumps durante a ocorrência do sintoma mostrou que as threads não seguiam um padrão quando comparadas com o dump anterior, com isso descartei a primeira hipótese e elaborei outra que se mostrou correta, uma vez que a informação, na segunda vez, foi coletada no momento do sintoma.

Aqui vale uma explicação sobre hipóteses: criar uma hipótese e tentar comprová-la é um modo de nos mantermos focados em uma direção. A grande vantagem disso é justamente de evitar força-bruta durante a investigação e agir estrategicamente, com um objetivo em mente. Quando a hipótese é comprovada isso significa muitas vezes que o problema foi isolado ou que estamos muito perto disso. Entretanto, quando a hipótese é descartada isso também é um bom resultado! Explico: quando você coleta informação suficiente para descartar uma hipótese baseada em fatos isso permite que você elimine um potencial problema que poderia explicar os sintomas , reduzindo a complexidade do seu problema já que algumas variáveis podem ser seguramente descartadas.

Outro fator importante é a percepção do cliente. Um engenheiro que tenta isolar um problema com base em ações sem propósito definido, ações aleatórias sem uma estratégia que faça sentido certamente não será bem visto pelo cliente, além de comprometer o tempo de resolução do problema. Qualquer cliente espera que o engenheiro saiba conduzir a investigação, portanto, é fundamental usar um (eu diria entre vários) ensinamento do xadrez: “todo movimento deve ter um propósito”, ou seja, toda ação deve ter um propósito, para isso as ações devem fazer parte de uma estratégia cujo objetivo é comprovar uma hipótese.

Voltando ao nosso problema, durante o início da investigação fiz várias perguntas que usualmente faço antes de tomar qualquer tipo de ação. Em situações reais adiciono outras relacionadas ao ambiente do cliente e as vezes removo algumas caso já tenha a informação. Eis algumas delas:

1- Por favor, descreva todos os sintomas que conseguiu identificar.

2- Qual o sistema operacional e service pack sendo utilizado?

3- Qual a versão de IIS, SQL Server e outros e os respectivos Service Pack?

4- Há componentes na solução?

5- Qual a linguagem de desenvolvimento utilizada?

6- Há alguma particularidade durante os sintomas?

7- O sintoma é facilmente reprodutível?

8- A ocorrência do sintoma obedece algum padrão?

9- O sintoma ocorre nos ambientes de desenvolvimento, homologação e produção?

10- O sintoma sempre ocorreu ou passou a ocorrer após alguma mudança?

11- Qual a frequência do sintoma? Ela tem se mantido estável?

12- O que foi tentado de modo a eliminar o sintoma ou para amenizar os sintomas?

13- Quais as mensagens de erro que ocorrem durante o sintoma?

Muitas vezes com as respostas das perguntas acima é possível se elaborar uma hipótese muito forte, digo, uma hipótese que na maioria das vezes se mostra verdadeira. Nesse incidente em particular me chamou a atenção o fato da aplicação ter funcionado bem desde aproximadamente 2 anos atrás quando foi criada e não ter sofrido alterações recentemente. Entretanto, gradativamente o sintoma de performance baixa e alta CPU começou a se manifestar.

Dica: Geralmente sintomas que não existiam algum tempo atrás e gradativamente passaram a se manifestar sem mudanças na aplicação deveriam colocar em True seu flag mental de bProblemasDeEscalabilidade. ;)

Abaixo apresento o código ASP causador do sintoma de alta CPU e algumas informações para que você possa deduzir o que poderia ter ocasionado o sintoma de alta CPU e resolver o problema.

CENÁRIO

Aplicação web feita em ASP apresenta alta CPU sempre que o fragmento script abaixo é executado:

Set objFso = Server.CreateObject("Scripting.FileSystemObject")

Set objPasta = objFso.GetFolder(Server.MapPath("/imagens/grande/" & left(rsProduto("codigo"), 2)))

imagemGrande = false

For Each objArq in objPasta.Files

If ucase(objArq.Name) = ucase(rsProduto("codigo") & ".jpg") then

imagemGrande = true

end if

Next

Sim… esse códigozinho acima foi o responsável por impactar severamente todos os servidores web do meu cliente! E justamente por isso resolvi criar esse desafio. Desse modo, temos mais exemplo de um problema simples que escapa de desenvolvimento/testes para produção tornando-se um monstro difícil de ser localizado e capaz de criar um impacto enorme nos usuários!

Várias threads estavam consumindo 100% de CPU há vários minutos quando executando o loop acima.

As imagens JPG de produtos ficavam em um diretório. Basicamente cada produto tinha várias imagens no mesmo diretório. Durante o passar dos anos a quantidade de produtos aumentou bastante... após algum tempo os sintomas começaram. (opa, bProblemasDeEscalabilidade = True J)

O loop não estava travado em uma única linha mas executando continuamente...

Nota: A rede estava ok. O diretório de imagens era local e o I/O também estava ok conforme comprovado por logs de Performance Monitor.

SINTOMA

100% de CPU em várias threads sempre quando executavam o fragmento de código acima. O sintoma persistia durante vários minutos.

OBJETIVO

Identifique o PROBLEMA e proponha uma SOLUÇÃO.

Dica: Se achar melhor crie um código VBScript ou JavaScript e tente criar a condição que causa o sintoma de alta cpu. Baseado no cenário acima será fácil deduzir…

Até semana que vem.