Identificando Problemas – Congelamento e Conexões Presas – Parte 03

Introdução

No post anterior (https://siga0984.wordpress.com/2018/11/07/identificando-problemas-congelamento-e-conexoes-presas-parte-02), demos uma boa olhada sobre travamentos e congelamentos, desde a percepção do usuário, até algumas possíveis causas e alguns procedimentos de diagnóstico. Neste post, vou apresentar mais algumas possibilidades, e complementar alguns casos já vistos, e ver mais de perto o “temível” DEADLOCK 😀

Dicas para Todos os Casos

  1. Começamos procurando o processo no Protheus Monitor,e verificando se o total de instruções está sendo atualizado. Se não estiver sendo atualizado, o processo está esperando por “algo”.
  2. O processo tem conexão com o DBAccess? Verifique o que a conexão está fazendo. Se o DBAccess está “IDLE” faz algum tempo, seja lá o que o processo estiver esperando, não é um retorno do DBAccess. Elimine a conexão do DBAccess e espere ela sair do DBAccess monitor — isso pode demorar até 30 segundos, inclusive devido ao fato da aplicação não estar fazendo requisições para o DBAccess, ele somente verifica o flag de “derrubar a conexão” em intervalos de 30 segundos.
  3. O processo ainda está com o SmartClient aberto? Se tiver algum problema no SmartClient, e o server está esperando algo que deveria vir do SmartClient, derrubar o SmartClient também faria o processo terminar — porém com uma mensagem de erro de sincronismo, sem gerar log. Deixemos isso como ultima alternativa.
  4. Podemos também tentar derrubar o processo pelo Monitor do Protheus, mas lembre-se de não usar a opção “derrubar imediatamente”, senão o processo some do monitor, e você somente vai saber se ou quando ele saiu, depois de verificar o console.log do Application Server.
  5. Ao investigar ocorrências estranhas e com poucas pistas, procure obter mais informações, inclusive verifique os logs e configurações das aplicações envolvidas — DBAccess, LockServer (linux), License Server, Protheus Master, Slave(s), etc. — principalmente verifique se nestes serviços não está acontecendo algum ACCESS VIOLATION e FAILURE ON THREAD DELETE. Depois de ocorrências desta natureza, o comportamento da aplicação é imprevisível — mas normalmente os efeitos mais comuns são: Recursos bloqueados ou em uso por um processo que não está mais no Monitor, crescimento do uso da memória ao longo do tempo, inclusive congelamentos.

NÃO ACHEI … E AGORA ?

Beleza, você já olhou com uma lupa e não achou onde travou, ou pior, cada hora trava em um lugar diferente, só acontece na produção, ninguém reproduz no ambiente de desenvolvimento ou na homologação …

Abra um chamado na TOTVS, forneça os detalhes pertinentes, o analista de suporte pode pedir mais algumas informações, e se mesmo assim não for descoberto a causa ou o que está acontecendo, ainda assim é possível usar uma build Debug ou RWD (Release com informações de Debug) do Application Server, fornecida para a análise desta ocorrência, junto com um procedimento para gerar um “Core Dump” manualmente do Protheus Server, ou da aplicação em questão — no momento em que o travamento for reproduzido.

Através de um “Core Dump” gerado nestas condições, o time de Tecnologia consegue abrir este arquivo para análise, e determinar onde e o que cada processo dentro do servidor de aplicação estava fazendo no momento que o Dump foi gerado. Isso ajuda muito no diagnóstico, quando os demais procedimentos não deram resultados satisfatórios.

Outros Casos

Lembrando o caso clássico de “Impressão no Servidor” usando um Driver de geração de PDF, rodando o Protheus como serviço do Windows … O processo atual simplesmente TRAVA dentro do servidor. Este caso está bem detalhado no primeiro post — https://siga0984.wordpress.com/2015/08/01/identificando-problemas-memoria-no-advpl-parte-01/ — vale a pena dar uma lida nele, pois além de travar ele mantém vários recursos ocupados e abertos, como a conexão com o DBAccess , License Server, c-Tree, etc.

Existe também a possibilidade de haver algum erro de lógica ou falha de tratamento de eventos ou um estado de interface não previsto, onde o Loop ou o Travamento pode estar dentro do Application Server, ou mesmo dentro do SmartClient, disparados por alguma condição particular. São erros mais difíceis de serem diagnosticados, principalmente quando não existe — ou ninguém sabe como faz — uma receita de bolo para fazer o problema “aparecer” e ser reproduzido. Reproduzir bug em cativeiro é “de rosca”… Não tem como fugir das etapas do processo investigativo, e se nada deu certo, quando a ocorrência chegar até esta camada, cada caso é estudado individualmente no atendimento, onde outras medidas podem ser adotadas, desde uma build Debug, até uma build com uma instrumentação específica para levantar mais informações sobre a ocorrência pode ser fornecida para o cliente no ambiente em questão.

Outros tipos de Loop Infinito – O DEADLOCK

Esse é um dos tipos de ocorrência que dá mais trabalho de investigar, e seus efeitos são desastrosos … A aplicação AdvPL realiza as alterações de registro na base de dados obrigatoriamente solicitando um Lock de Registro, tratado pelo DBAccess. No ERP, usamos a função RecLock(), do FrameWork AdvPL, que possui um tratamento de retry para a obtenção do bloqueio.

Porém, uma vez que uma determinada aplicação esteja em JOB — Como um Scheduler ou um WebService, por default este retry é reiniciado em caso de falha. Caso dois processos diferentes tenham obtido cada um um determinado lock, e no momento atual um processo tenta obter o lock do registro que está com o outro processo, e vice-versa, temos um DEADLOCK na aplicação AdvPL.

Neste caso, se os dois programas estão em JOB — sem interface — ambos ficam tentando pegar cada um o lock que está com o outro processo, e como nenhum deles vai “desistir”, ambos ficam em loop até que um deles seja identificado e derrubado — pelo Monitor do Protheus ou do DBAccess.

Identificando os processos envolvidos

Normalmente dois ou mais processos entram em loop, fazendo várias tentativas de bloqueio de registro, e ninguém sai do lugar. Nestes casos, como eu não sei o que está acontecendo, uma das alternativas é verificar no DBAccess os processos com transação aberta a muito tempo — existe uma coluna nova para indicar isso — e então, usando o DBAccess Monitor, fazemos um TRACE de alguns segundos da conexão, para ver se ela está tentando pegar um lock e não está conseguindo. Depois de saber a tabela e registro envolvidos, você pode procurar quem é o dono do lock no Monitor de Locks do DBAccess, e ver o que esta conexão esta fazendo. Se ela também está tentando pegar outro lock, isso pode indicar um cenário de deadlock, onde basta chutar um dos processos para que o outro tenha a chance de ser finalizado.

WebServices e DEADLOCKs

Os WebServices do Protheus possuem uma configuração especial para fazer com que o retry para obter o lock seja executado apenas por um período de tempo determinado, e em caso de falha, o JOB do WEBSERVICE é encerrado com uma ocorrência de error.log, indicando que não foi possível obter o bloqueio de um determinado registro de uma tabela, inclusive fornecendo detalhes de qual era o processo que estava “segurando” este lock. A configuração chama-se ABENDLOCK=1, definida na seção de configuração das WORKING THREADS dos WEBSERVICES. De qualquer modo, a partir de Dezembro de 2017, esta configuração foi habilitada por DEFAULT nos WebServices, vide a nota de release da TDN.

DBAccess e DEADLOCKs

Devido a dificuldade de identificar os processos e registros envolvidos em uma ocorrência de DEADLOCK, seria muito interessante se o próprio DBAccess conseguisse identificar uma situação como essa, e avisar a um dos programas envolvidos que ele está envolvido em um DEADLOCK com um ou mais processos, onde a aplicação pode tratar a mensagem do DBAccess e finalizar-se automaticamente, gerando o log de erro correspondente e soltando os bloqueios obtidos, ou deixar que o DBAccess finalize uma das conexões automaticamente, para que as outras tenham chance de terminar.

Conclusão

Por hora, deixo as conclusões com vocês, eu apenas vou concluir este POST 🙂

Agradeço a todos novamente pela audiência, e desejo a todos TERABYTES de SUCESSO 😀

 

 

Anúncios

Acelerando o AdvPL – Parte 02 (ERRATA)

Pessoal, boa tarde,

Hoje eu estava lendo novamente o código do post anterior, referente ao exemplo de um cache em array, com tamanho limitado de elementos, e uma optimização para subir no array cada item pesquisado, para que os itens mais pesquisados sejam encontrados sequencialmente em primeiro lugar, e encontrei um erro de lógica em um ponto da rotina … vamos ver apenas este pedaço do código:

Function GetJurosPad( cTipoCtr )
Local nJuros := -1
Local cChave := xFilial('ZZ1')+cTipoCtr
Local nPos := ascan(aJurosPad,{ |x| x[1] == cChave })
Local aTmp
If nPos > 0 
 // Achou no cache. 
 If nPos > 1 
 // nao esta na primeira posição, sobe uma 
   aTmp := aJurosPad[nPos]
   aJurosPad[nPos] := aJurosPad[nPos-1]
   aJurosPad[nPos-1] := aTmp
 Endif
 // Incrementa cache HITS -- achei no cache, economia de I/O
 nJurosHit++
 Return aJurosPad[nPos][2]
Endif

Reparem que a busca foi feita na declaração da variável nPos, usando Ascan(). Porém, caso o item encontrado não seja o primeiro elemento do array, o elemento encontrado sobe uma posição (usando a variável aTmp para fazer o SWAP). E, no final das contas, o valor retornado é a segunda coluna do elemento após a troca. Neste caso, a função somente vai retornar o valor correto caso o valor buscado seja encontrado no primeiro elemento do array.

Para  corrigir isto, precisamos armazenar o resultado encontrado antes de fazer a troca no array, e retornar este valor, veja a correção abaixo:

Function GetJurosPad( cTipoCtr )
Local nJuros := -1
Local cChave := xFilial('ZZ1')+cTipoCtr
Local nPos := ascan(aJurosPad,{ |x| x[1] == cChave })
Local aTmp
If nPos > 0 
 // Achou no cache. 
 nJuros := aJuros[nPos][2]
 If nPos > 1 
   // nao esta na primeira posição, sobe uma 
   aTmp := aJurosPad[nPos]
   aJurosPad[nPos] := aJurosPad[nPos-1]
   aJurosPad[nPos-1] := aTmp
 Endif
 // Incrementa cache HITS -- achei no cache, economia de I/O
 nJurosHit++
 Return nJuros
Endif

Lembrem-se da importância de revisar e testar o código. Ser um bom programador e ter experiência não vai te livrar de cometer erros, uma revisão de código e um bom teste nunca é uma questão de desconfiança, mas sim procedimento.

Até a próxima, pessoal 😀

Pense fora da caixa e resolva problemas

Hoje não veremos nenhuma linha de código, separei este post para compartilhar um pouco das experiências diárias do profissional de TI em lidar com problemas, e contar um “causo” (pelo menos pra mim) interessante, onde pensar fora da caixa foi fundamental para chega a uma solução elegante.

Problemas existem, e podem acontecer

Na trajetória do desenvolvimento de Software, volta e meia aparecem problemas de diferentes magnitudes e complexidades. Normalmente existe solução para todos os problemas, porém algumas soluções podem ser mais caras do que conviver com o problema ou contorná-lo. A primeira coisa que aprendemos na informática é como utilizá-la para resolver problemas. A segunda coisa é como resolver o problema quando uma falha na implementação de uma solução informatizada torna-se o problema.

A primeira coisa que devemos entender sobre o problema é a sua gravidade. E, é claro que isto é uma medida totalmente relativa. Um cliente não conseguir emitir uma nota fiscal pode parecer algo não grave, porém se este cliente depende da emissão desta nota para liberar um caminhão com produtos perecíveis, com data e horário certos para entrega sob pena de multa ou rescisão do contrato, não emitir essa nota é gravíssimo.

Depois, precisamos pensar em uma solução para o problema. Se o problema é grave e urgente, precisamos de um contorno para o problema. Dá-se preferência a um contorno elegante, mas se o que você têm para o momento é um arame e um durepoxi, e isso vai contornar o problema, apodere-se do espírito McGuyver que existe em você, e contorne. Mas pelo amor de Deus, não venda a gambiarra que você fez como a solução do problema. Explique que você está botando uma fita isolante no problema para o cliente não ficar parado, e que você vai trabalhar na solução definitiva assim que você entender as causas do problema.

Se você já está na fase de trabalhar no problema, ou teve que ir pra ela direto pois nenhum contorno passa pela sua cabeça, pesquise o que você puder sobre o problema. Procure entender o problema e suas causas, a partir de que momento ele manifestou-se, sob que circunstâncias, quando e onde ocorre, com que frequência, se é generalizado ou localizado, se ocorre em outros pontos do sistema, em um servidor ou em todos, em um tipo de programa ou qualquer programa, quais foram os eventos recentes que antecederam o início do surgimento do problema. É quase uma atividade de detetive. Procure as pessoas certas para levantar estas informações, limpe eventuais “sujeiras” na comunicação, e não tenha medo de perguntar “o que você quer dizer com isso ?”, quando você tiver qualquer dúvida a respeito das informações que permeiam o assunto em foco.

Muitas vezes você encontra um contorno para o problema — e posteriormente a solução — durante a análise dos detalhes do problema, ou tomando banho, ou tomando um café depois do almoço — sim, essas coisas acontecem. Se você já avaliou as possíveis causas e tudo parece certo, procure alguém mais experiente para trocar uma ideia, ou divida o problema com seus colegas de equipe, às vezes um detalhe que você não viu nas informações fornecidas  é visto na hora por outro analista, e dá um novo norte na busca pela causa do problema.

Alguns tipos de problemas são mais difíceis de reproduzir em um ambiente controlado do que Tamanduá-Bandeira em cativeiro. A causa pode estar relacionada a alguma particularidade do ambiente ou da configuração do sistema, ou mesmo até um defeito físico em uma parte da infra-estrutura. Quando as causas prováveis foram analisadas sem sucesso, comece a analisar as improváveis. Olhe tudo com uma lupa, revalide as possibilidades que foram eliminadas durante a análise, mas não desista. Existem pequenos problemas que se escondem por trás de um problema maior, e grandes problemas por trás de pequenos problemas. Não está achando o problema grande, verifique se existem problemas menores, eles podem ser consequência ou parte da causa do problema grande.

O caso do CNAB

No final das contas, entrei tanto dentro do problema que quase fugi do foco principal do post, o pensamento “fora da caixa”. Um cliente do ERP estava com dificuldade de utilizar a integração CNAB (padrão Febraban para remessa e retorno de títulos para compensação bancária). Quando o arquivo de remessa era gerado e enviado ao banco, alguns títulos do arquivo eram rejeitados pelo banco, pois o código de verificação do título que era enviado no arquivo, segundo o banco, não estava com os valores corretos.

O código de verificação de cada título era gerado pelo ERP, na geração do arquivo de remessa, e este código era composto por uma série de operações aritméticas de soma e multiplicação a partir de determinadas informações do título, como valor, vencimento, código do cliente e afins. A conta realizada com os valores daquele título resultava em um número inteiro muito grande, que esbarrava em um limite aritmético da linguagem AdvPL. O cálculo feito pela calculadora do Windows batia com o valor esperado pelo banco, que estava correto, mas o cálculo feito dentro do programa usando a aritmética da linguagem não comporta um número com mais de 16 dígitos significativos, havendo perda de precisão e consequentemente valor incorreto do código.

Isto foi antes do uso do Protheus Server, hoje chamado de TOTVS Application Server, muito anos antes de serem implementadas funções no AdvPL para lidar com números decimais de ponto fixo, que possuem precisão de até 64 dígitos. No final das contas, a fórmula matemática de cálculo escrita em AdvPL da forma originalmente proposta somente seria executada corretamente se a linguagem usasse um tipo de dado numérico com precisão superior ao ponto flutuante.

Depois de sair da minha mesa e tomar um café, levei meu caderno de rabiscos para passear, e já estava pensando em fazer um programa em outra linguagem e chamar ele de dentro do ERP para fazer aquela conta, quando eu olhei para os números e percebi o óbvio: Eu consigo fazer essa conta em um pedaço de papel, usando um lápis e as mesmas regrinhas básicas de soma e multiplicação que aprendemos no colégio primário. Se dá pra fazer no papel, dá pra fazer no computador, exatamente da mesma forma.

Eu só precisava de soma e multiplicação, e não importa se a operação ficasse um pouco mais lenta do que um cálculo em ponto flutuante nativo da linguagem, afinal seriam apenas algumas chamadas para cada título do borderô. Então, em menos de uma hora eu fiz duas funções para somar e multiplicar números recebidos como String. Bastou re-escrever as fórmulas para o CNAB daquele banco para pegar os dados do título e passar para as novas funções, trabalhando com os números como String.

Conclusão

Análise, programação e desenvolvimento de sistemas é uma atividade que requer jogo de cintura e criatividade para lidar com o universo de desafios de resolução de problemas, é saber usar o conhecimento e as ferramentas que se têm na mão, ser capaz de apontar mais de uma alternativa para resolver um problema, e escolher a que melhor atende a necessidade.

Pensar fora da caixa é uma expressão onde a caixa normalmente significa o limite do seu pensamento criativo, ou os paradigmas assimilados e embutidos nos problemas que você normalmente lida no dia a dia. A busca por novas soluções é constante, e avaliar um problema por novos ângulos e abordagens pode fazer toda a diferença. Não desista, a resposta está lá fora, esperando que você a encontre !!!

Até o próximo post, pessoal 😉

Informações Adicionais

As funções desenvolvidas para cálculo numérico de soma e multiplicação com strings estão disponíveis no ERP Microsiga, e documentadas na TDN nos links http://tdn.totvs.com/display/public/mp/FUNCAO+GENERICA+-+SOMASTR
e http://tdn.totvs.com/pages/releaseview.action?pageId=6814818 , e foram mantidas por compatibilidade. As funções implementadas na linguagem AdvPL para cálculo com números que exigem precisão maior do que a suportada por variáveis numéricas com ponto flutuante lidam com o tipo numérico “F” do AdvPl (Fixed size decimals), e estão também documentadas na TDN, no link http://tdn.totvs.com/display/tec/Decimais+de+Ponto+Fixo