[PowerAlerts] Tratamento de Erros

[PowerAlerts] Tratamento de Erros

Fala, pessoal. Beleza?

Na última semana mostrei um caso onde o Power Alerts ajudou um cliente a identificar um problema na aplicação através do alerta [Database Errors].

Hoje vou explorar um pouco mais sobre esse tema de tratamentos de erros e mostrar melhores práticas e situações onde, ao utilizar o tratamento de erros, pode ser um problema para nossos códigos.

Bom, sem mais delongas vamos ao que interessa.

Inicialmente, temos algumas formas de tratar erros no SQL Server. Os mais comuns de vermos são “@@ERROR”, RAISERROR e “Try Catch”. Iremos focar nesse último.

Para deixar o mais didático possível, tentarei utilizar os exemplos mais curtos e diretos que conseguir, focando no problema e solução e não no código em si. Beleza?

Cenário:

Contextualizando o cenário, temos um ambiente de um sistema de ERP, realizando uma venda, e um sistema de LOGISTICA, que irá receber essa venda e seguir com os devidos trâmites.

Fail Fast:

Iniciaremos nossa parte técnica com uma boa prática chamada “Fail Fast”. Essa prática consiste basicamente em sair/parar o mais rápido possível. Portanto, caso saibamos previamente que algo não faz sentido para a aplicação, devemos tratá-la antes mesmo de iniciar qualquer procedimento.

No nosso caso, iremos tratar a possibilidade da quantidade e valor para não serem zerados ou negativos pois, esses valores não fazem sentido para nossa aplicação e podem gerar problemas em todo o ciclo de vida do dado.

O código ficou assim:

Ao cair na condição “QTD_VENDIDA OR VALOR <= 0”, é retornada a mensagem de erro “Quantidade ou Valor inválido!” e encerra a execução. O encerramento é garantido pelo comando RETURN.

Apenas para título de conhecimento, no SQL Server, mesmo que ocorram erros no decorrer do código, a execução ainda é mantida até o fim do objeto. Veja o exemplo abaixo, onde ocorre um erro de severidade 16 e ainda sim o código continua sendo executado. Ao executar um erro de severidade 20 a sessão é finalizada:

Ao ajustar a quantidade e executada novamente, a linha é inserida com sucesso:

TRY CATCH:

Perfeito. Agora recebemos um requisito solicitando que “ao inserir um item, deve-se mandar esse item para a separação”.

A entidade “Separação” está no banco de dados da Logistica do servidor DS-SRV02. Devemos chamar a procedure stpInserirSeparacao para essa finalidade.

E incluída a chamada da procedure.

Até aqui tudo bem, nosso código está rodando conforme esperado:

Agora, foi implementado um novo campo na procedure chamado da logística chamado “Restrição de venda”. Esse campo deve sobrepor o campo “Finalidade de Venda” quando informado.

Para esse ajuste foi realizado a substituição da função ISNULL para COALESCE:

Seguindo o fluxo no ERP, em teoria, nada deveria mudar pois o novo campo não é um campo obrigatório.
Ao fazer a chamada temos o erro:

 

“Msg 2628, Level 16, State 1, Procedure LOGISTICA.dbo.stpInserirSeparacao, Line 18 [Batch Start Line 268]
String or binary data would be truncated in table ‘LOGISTICA.dbo.SEPARACAO’, column ‘FINALIDADE_VENDA’. Truncated value: ‘NAO ENVIAD’.”

Mas o que aconteceu aqui? Somente foi trocada a função de ISNULL para COALESCE que, em teoria, fazem a mesma coisa só com parâmetros a mais.

E ambas variáveis tem o mesmo tamanho. Não deveria ter dado o erro!!!

Pois bem, o leitor mais atento já deve ter percebido o problema. O ISNULL utiliza o tipo de dado do seu primeiro parâmetro, ou seja, VARCHAR(10) e com isso ele trunca o segundo valor e insere ele na tabela. Nos prints anteriores é possível ver o texto “NAO ENVIAD” efetivado na tabela.

Já o COALESCE não tem essa validação, e utiliza o próprio tipo de dado que estiver na variável/coluna fazendo com que o erro de truncate ocorra. Sabiam dessa? 😉

Mas, e agora? Não temos poder de ajustar a procedure da Logística. Como podemos fazer para que nossa aplicação não dê erro e continue executando normalmente?

Para essa finalidade podemos utilizar o TRY CATCH.

E ao executarmos:

Continua com erro… E aqui vem o segundo ponto importante: nem todo erro é tratado dentro do TRY…CATCH.

Certo, a Logistica implementou o TRY CATCH emergencial do lado dela:

Agora sim, executamos com sucesso. Apesar não ter enviado nenhum item para a Separação, pelo menos não paramos as vendas. Caso isso não fosse possível teríamos que comentar esse trecho, ou algo do tipo.

Mas, e se eu enviar um produto negativo conforme criamos a primeira validação. O que vai acontecer?

Não deu erro conforme tinhamos visto no primeiro exemplo. E, para piorar, ele ainda somou a quantidade negativa deixando meu estoque errado.

A utilização do TRY CATCH mudou o comportamento do nosso primeiro tratamento. Lembra que a premissa era validar e o return seria o responsável por finalizar a execução? Ao executar o comando RAISERROR, o código foi diretamente para o CATCH, não executando o RETURN. Importante destacar que eu finalizei o bloco TRY CATCH antes do “UPDATE PRODUTO”. Isso foi proposital para fins didático, para mostrar o quanto é sensível essa abordagem e importante contemplar o bloco correto.

Beleza, então vamos fazer a correção deixando somente no local desejado que é na chamada da Logística:

Agora voltamos ter o comportamento desejado:

Transação:

E para finalizar nossa série de alterações, vamos para o último escopo (prometo, fica ai, está acabando).

Foi nos solicitados que precisamos garantir o ACID dessa execução. Para isso teremos que criar uma transação e “commita-la” ao final do processo caso haja sucesso e dar o rollback caso haja erro.

Iremos utilizar o BEGIN TRAN no ínicio do processo e o commit ao final da procedure para esse objetivo.

Vamos executar nosso código:

Tivemos problema com o DTC não estar habilitado. Para um breve resumo, o DTC é o componente responsável por garantir as transações em servidores diferentes. É através dele que a instância A vai enviar algo e receber a devolutiva do OK. Para mais conhecimento, vejam o curso do Rodrigo.

Corrigido o DTC, vou executar e:

“The Microsoft Distributed Transaction Coordinator (MS DTC) has cancelled the distributed transaction.”

Que erro é esse??? E se executar sem o DTC, o que acontece?

O que aconteceu aqui?

Bom, quando encapsulamos o bloco try catch com a transação “DTC” e o erro estoura do outro lado, recebemos apenas a informação de que a transação foi encerrada.

O motivo do erro? Só por aqui não saberíamos. Mas, você meu caro leitor, sabe que o Power Alerts aqui para te ajudar, né?

Olha lá, quem está logado mostrando a mensagem de erro, o comando do erro e o objeto responsável. Por mais que não teríamos essa informação no momento da execução, o Power Alerts está ai para nos apoiar e identificar esses problemas. E qual a visão que temos do servidor 01?

Olha que massa essa visão. Mostrando o caminho do erro.

E já que você chegou até aqui, olha o bônus de utilizar o Power Alerts para analisar o seus erros:

Retorno do Power Alerts:

Simplesmente o caminho das 13 procedures até o erro. Imagina essa facilidade para achar o ponto do erro dentro de várias e várias procedures.

Isso é puro ouro.

Bom, é isso pessoal.

Espero que tenham gostado e nos vemos nas próximas.

E lembrem-se, o Power Alerts está aqui para ajudar vocês.

Tags: , , , , , , , , , , , , , , , ,