Diga não ao throw!

Abril 13, 2007 at 5:58 am 5 comentários

Exceções, apesar de muito práticas, podem trazer muitos problemas. Tratar situações comuns com um simples throw pode custar muito mais caro que uma lógica mais elaborada para o tratamento de situações consideradas excepcionais.

A questão principal é: Quando eu devo utilizar exceptions?

Uma maneira interessante de começar a obter a resposta é se perguntar se uma determinada situação é uma situação não esperada. 
Mas claro, nem tudo é tão fácil. Se você já está fazendo essa pergunta para algumas situações, provavelmente é porque elas podem acontecer e são esperadas.

Então a pergunta que irá se encaixar na maioria das situações é se você pode se dar o luxo de gerar uma exceção em um determinado ponto da aplicação.

Com certeza você deve estar imagindo o porquê do luxo – desde quando gerar uma exceção é luxo?

Brincadeiras à parte, gerar uma exceção exige um custo da aplicação, e este custo pode ser muito alto (assim como irei demonstrar neste post).

O foco deste post são as exceções lançadas por aplicações gerenciadas pelo CLR, portanto managed exceptions. E aqui está nosso problema: Managed exceptions (aquelas geradas através de sua aplicação .NET) têm uma riqueza muito grande de detalhes disponíveis para “investigação”, como um stack do CLR completo para o objeto da exceção. Estes recursos consomem espaço e tempo.

Segue então um pequeno guideline (ou pelo menos uma tentativa) sobre como utilizar exceptions.

Regra #1: Não utilize exceção como se fosse um “else”

Como já foi dito anteriormente, exceções servem para tratar situações inesperadas, algo que não conseguimos prever para algum tratamento. Gerar exceções para um campo inserido incorretamente pelo usuário, ou para indicar que um campo obtido do banco de dados não tem um texto esperado, realmente não é legal.

Regra #2: Se você tem que lançar uma exceção para alguma situação, garante que isso não vá ocorrer muitas vezes

Ok, vamos imaginar uma situação onde você realmente tem que lançar uma exceção…. faça com que isso não esteja dentro de um while (true)… 🙂

Lançar exceções exigem um custo, lançar 2.000 em 10 minutos acabam com um servidor. Portanto, se realmente for utilizar tratamento de exceção, que seja de uma forma controlada, pensada.

Regra #3: Se você está preparado para lançar uma exceção para outras pessoas, provavelmente você não precisa lançá-la.

Esta é uma das minhas regras preferidas: se você fez todo um sistema/camada (tier ou layer) esperando que outro sistema ou camada tratasse essa sua exceção, muito provavelmente esta situação pode ser tratada de outra maneira.

Vale a pena neste caso pensar sobre uma outra solução que não exija uma exception de retorno.

Regra #4: Conheça o framework e saiba quem utiliza try/finally implicitamente

O próprio .NET framework, em alguns momentos, faz uso extremo de tratamento de exceções. O using é um bom exemplo:

 

Ok, mas como investigar que exceções travaram meu servidor?

Esta semana, um dos servidores de um projeto que estou participando começou a trabalhar lentamente, perto de 100%. Esta utilização estava sempre alternando entre os servidores do web farm.

Após um hang do IIS, analisei um dump gerado pelo DebugDiag através do WinDbg e consegui visualizar 1.001 exceções lançadas em 18 minutos. Este caso aconteceu por “desrespeito” da Regra #1.

Neste exemplo abaixo, através do comando !dumpheap -stat foi possível encontrar os objetos de uma determinada exceção (ou !dumpheap -type System.Exception):

<…economizando espaço>
790fa9e8 743 53496 System.Exception
<…>

Depois de encontrar uma suspeita do problema, uma boa idéia é obter todos os objetos deste tipo, além de seu endereço, através do comando !dumpheap -mt 790fa9e8:

Address     MT           Size
<…>
01368c44 790fa9e8 72
01368cb0 790fa9e8 72
01368d1c 790fa9e8 72
01368d88 790fa9e8 72
01368df4 790fa9e8 72
01368e60 790fa9e8 72
total 743 objects
Statistics:
MT Count TotalSize Class Name
790fa9e8 743 53496 System.Exception
Total 743 objects

Sabendo que se trata de exceções, é possível ver detalhes deste objeto, como sua mensagem, através de !dumpobj <endereço>:

 

0:000> !dumpobj 0135554c
Name: System.Exception
MethodTable: 790fa9e8
EEClass: 790fa978
Size: 72(0x48) bytes
(C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
Fields:
MT Field Offset Type VT Attr Value Name
<…>
790fa3e0 40000b8 10 System.String 0 instance 0134c16c _message
<…>

Com o atributo message disponível para visualização, basta rodar novamente o !dumpobj (ou !do) e verificar seu conteúdo:

 

0:000> !do 0134c16c
Name: System.String
MethodTable: 790fa3e0
EEClass: 790fa340
Size: 44(0x2c) bytes
(C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String: Just for Test
<..>

Além disso, para obter o stack trace (simples, mas já ajuda), basta executar !PrintException (ou !pe) com o endereço da exceção:

 

0:000> !pe 0135554c
Exception object: 0135554c
Exception type: System.Exception
Message: Just for Test
InnerException: <none>
StackTrace (generated):
SP IP Function
0391DB98 04340A52 TooManyExceptions.ThrowManyExceptions()

StackTraceString: <none>
HResult: 80131500

Com o stack trace na mão é possível identificar a origem das exceções, e com isso alterar o tratamento para que não utilize tanto throw.

Neste exemplo acima, através de uma análise simples de um Dump foi possível observar que muitas exceções estavam sendo lançadas em pouco tempo, o que, com certeza, ocasionava o hang do IIS.

Agora, diga 3 vezes em alto e bom som: Diga não ao throw! 🙂

Portanto, sempre que possível, aplique as regras sobre tratamento de exceções e evite qualquer surpresa!

Até!

Anúncios

Entry filed under: .NET.

Mapeando páginas HTTP para HTTPS automaticamente Tratamento Global de Exceções

5 comentários Add your own

  • 1. Weyler  |  Abril 17, 2007 às 12:22 pm

    Aeeeee vai dar throw na cadeia!
    Diga não à POG!

    Muito bacana o artigo! auei!

    Responder
  • 2. Wilder  |  Abril 26, 2007 às 8:41 pm

    Muito legal o artigo! Parabéns.
    Mas qual solução você recomendaria para o caso de desenvolvimento em camadas, onde na camada x eu trato uma possível exceção. Se eu não utilizar o throw, como faria pra notificar a camada de apresentação, por exemplo? Existe um pattern pra isso? Qual?
    Obrigado e abraços

    Responder
  • 3. Eduardo Miranda  |  Abril 26, 2007 às 10:48 pm

    Interessante o artigo. O tratamento de exceções realmente é caro na maioria das tecnologias e o .Net não é exceção. Pensar antes de sair levantando erros é fundamental.

    Agora que você “botou pilha” te pergunto: como gerenciar exceções de regras de negócio? Por exemplo, durante o cadastro do cliente o CPF não bateu. (Lógico que a UI filtra, mas a validação deve ser feita também na camada de negócios).

    Só uma sugestão para um novo post 😉

    Responder
  • 4. André Nobre  |  Maio 3, 2007 às 2:20 pm

    Wilder / Eduardo,
    vou seguir a dica do Eduardo e vou escrever um novo post sobre isso em breve 🙂

    Abraços!

    Responder
  • 5. Fabiano França » Blog Archive » links for 2007-06-18  |  Junho 18, 2007 às 8:21 am

    […] Diga não ao throw! « public AndreNobre : BlogContext (tags: exception asp.net) […]

    Responder

Deixe uma Resposta

Preencha os seus detalhes abaixo ou clique num ícone para iniciar sessão:

Logótipo da WordPress.com

Está a comentar usando a sua conta WordPress.com Terminar Sessão / Alterar )

Imagem do Twitter

Está a comentar usando a sua conta Twitter Terminar Sessão / Alterar )

Facebook photo

Está a comentar usando a sua conta Facebook Terminar Sessão / Alterar )

Google+ photo

Está a comentar usando a sua conta Google+ Terminar Sessão / Alterar )

Connecting to %s

Trackback this post  |  Subscribe to the comments via RSS Feed


Calendário

Abril 2007
S T Q Q S S D
« Mar   Maio »
 1
2345678
9101112131415
16171819202122
23242526272829
30  

Most Recent Posts


%d bloggers like this: