quarta-feira, setembro 12, 2012

Dois Bugs Incomuns com ASP.Net

Gostaria de compartilhar com a minha dupla de leitores dois bugs curiosos que eu enfrentei recentemente em uma aplicação ASP.NET. Foram duas pequenas besteiras, mas ilustram alguns aspectos curiosos do ASP.Net.

O Arquivo Preso

Após uma alteração pequena, surgiu um comportamento estranho e intermitente na aplicação: ao acessar pela segunda vez um arquivo bmp, ocorria uma exceção indicando que o arquivo "já estava aberto por outro processo".

Demorou algum tempo até descobrir que o problema estava em uma chamada ao método Bitmap.FromFile.  Apesar da documentação falar claramente que o arquivo fica preso até o objeto ser destruído, várias coisas conspiraram para eu não perceber isto:
  • Eu estava interessado somente nas dimensões da imagem, usei este método somente porque parecia ser o jeito mais simples de obtê-las.
  • Não é o tipo de função que se espere que mantenha o arquivo preso. Os motivos dela manter o arquivo aberto estão explicados aqui.
  • Não existe uma função explícita para fechar o arquivo, ele só será liberado quando o objeto for destruído.
  • O objeto estava declarado em um bloco bem curto de código. Mas é claro que isto não faz diferença. O .Net só vai destruir o objeto (e liberar o arquivo) quando rodar o garbage collector. Para quem aprendeu primeiro C++ fica a falsa ilusão de que o objeto será destruído no final do bloco, liberando todos os recursos.
  • No ASP.Net os conceitos de início e fim da execução de um programa são vagos. No desktop é bem claro quando um programa inicia e termina (tirando os casos de programas que ficam como zumbis depois que a gente manda ele terminar, né Thunderbird?). A rigor um programa web precisa rodar somente o tempo para atender a um get ou post. Como isto é muito desperdício de recursos e atrapalha o desempenho, o runtime do ASP.Net reaproveita instâncias do programa. O efeito prático, neste caso, era a intermitência do programa. Dois acessos consecutivos à mesma imagem sempre causava o bug, mas depois de algum tempo o programa terminava, liberando o arquivo.
A solução é bem simples: chamar o método Dispose quando não precisa mais do Bitmap. Acrescentar uma linha (com um comentário explicando), atualizar o número da versão, gerar o deploy e enviar a nova dll para o cliente substituir a bugada...

E Tudo Para de Funcionar!

Ao tentar acessar qualquer página da aplicação, o .Net dá o erro "The located assembly's manifest definition does not match the assembly reference."

A causa? O cliente, como toda pessoa bem cuidadosa, renomeou a versão antiga para algo como xyz_old.dll antes de colocar a versão nova de xyz.dll. A lógica de localização e carga do .Net fez alguma bagunça entre os dois e não conseguiu carregar nenhum.

Bastou mover para outro canto a versão antiga para tudo voltar ao normal (sem o bug da imagem, é claro).

Nenhum comentário: