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

Introdução

No primeiro post sobre identificação de problemas — Identificando Problemas  – Memória no AdvPL – Parte 01 — falamos sobre uso de memória e Leaks de memória. Hoje, vamos obter mais detalhes sobre travamentos, congelamentos, conexões e licenças “presas”, e ocorrências desta natureza.

IGH, TRAVOU…

Bem, um operador do ERP executa uma rotina ou sub-rotina qualquer, normalmente através do SmartClient, certo? Por sua vez, o SmartClient em si é uma aplicação em C++ que, em poucas palavras, foi feita para desenhar os componentes que a aplicação AdvPL criou dentro de uma caixa de diálogo ou Janela, e uma vez que a janela torna-se ativa — método ::Activate() do diálogo — os controles de entrada de dados e interação com a aplicação estão do lado do cliente, e o servidor aguarda pelo disparo de ações a partir dos componentes de tela, como clicar em um botão ou preencher um campo com dados.

Já o Protheus Server, executando um programa AdvPL, ao receber uma ação do SmartClient, executa o bloco de código correspondente a ação, que pode chamar rotinas e sub-rotinas, interagir com a interface atual, e até mesmo montar uma nova caixa de dialogo sobre a interface atual e torná-la ativa.

Do lado do usuário, no SmartClient, a percepção do usuário de “travamento” ou “congelamento” normalmente é percebida como “eu apertei um botão, que deveria fazer X, e nada aconteceu, e eu não consigo clicar ou fazer mais nada”.

Onde, quando, como e o quê travou ?

A resposta de cada uma destas perguntas leva para a próxima pergunta. Onde travou, a informação de qual botão em qual tela que foi apertado e percebido o travamento. Quando, é a informação sobre a periodicidade que isso ocorre. Sempre trava ao apertar este botão? Se não trava sempre, como faz para ele travar? Somente trava quando um campo da tela estava com o valor Y? Ou é uma ocorrência esporádica?  O botão funciona o dia inteiro, e de repente trava…

A resposta da última pergunta é a questão de um milhão …  risos … existem muitas coisas que podem ter acontecido. Vamos enumerar aqui boa parte das possibilidades.

Loop Infinito

Aquele botão dispara uma rotina, com um determinado conjunto de parâmetros, e existe um erro de lógica na rotina, onde uma parte do código entra em loop, realizando um determinado processamento e esperando por uma determinada condição para finalizar o loop. Por exemplo, a rotina abriu uma Query, e está lendo campos da Query e acrescentando um valor em uma variável, até que a Query termine, mas dentro do laço o programador esqueceu de colocar um DBSkip() — ou fez esta operação sem querer em um outro ALIAS, ao invés de fazer na QUERY —  ou ainda um laço FOR … NEXT que usou uma variável N, que inadvertidamente foi alterada dentro do loop, para um outro valor menor, fazendo com que o laço não termine.

Nestes casos, ao abrir o Monitor de Processos do Protheus, e localizar o usuário, a coluna que indica o número total de instruções está sempre crescendo, normalmente a CPU fica mais alta no serviço do Protheus que está executando este processo, e o número de instruções por segundo mostrado no Monitor do Protheus também é alto.

Esse é um caso simples de descobrir onde está o problema, basta finalizar o processo pelo Monitor do Protheus, preferencialmente sem marcar a opção “derrubar imediatamente”. A aplicação AdvPL em loop vai perceber entre uma instrução e outra que ela foi marcada para ser finalizada — é isso que o Monitor do Protheus faz quando você manda finalizar um processo. No momento que a aplicação perceber isso, ela finaliza o processo, com uma ocorrência de erro “Process terminated by Administrator” ou algo assim, gerando uma ocorrência de erro com o stack ou pilha de chamada de funções, para mostrar onde foi que o Protheus “percebeu” que o processo foi marcado para ser finalizado.

Nesta situação, quando o Programa AdvPL está em LOOP e não está interagindo com a Interface, a cada 10 segundos o Protheus Server verifica se o SmartClient ainda está lá, aguardando pela resposta. Caso o Server perceba que o SmartCient caiu, ou a conexão de rede foi interrompida, ele finaliza o processo atual com a ocorrência de erro fatal em AdvPL “Remote Connection BROKEN”.

Latência alta de rede entre Protheus e SmartClient

Normalmente quando isso acontece, o processo dá a impressão de ter “travado”, porém em alguns segundos a tela que deveria aparecer simplesmente “aparece”. Imagine que o programa em AdvPL está desenhando uma nova caixa de diálogo, com muitos componentes, e durante a montagem da tela o programa pede ao SmartClient coordenadas de tela e informações sobre as dimensões dos componentes em fase de montagem. Uma latência de rede momentânea de 500 ms (milissegundos) pode fazer uma tela que, durante sua montagem, faça 10 requisições ao SmartClient, demorar quase 5 segundos para ser finalizada. Num caso como esse, o monitor do Protheus mostra um numero de instruções por segundo perto de zero, e o número de instruções total sobe bem devagar.

Instrução em execução no Banco de Dados

Imaginem o cenário, onde a aplicação AdvPL monta uma Query dinâmica, porém devido a um erro de lógica ou validação de parâmetros, a Query fez um produto cartesiano da tabela, ou um INNER JOIN sem WHERE …. ou uma daquelas queries rebuscadas, que fazem múltiplas buscas em uma tabela enorme, usando um ou mais campos que não possuem um índice para o Banco de Dados otimizar a busca, e o Banco de Dados precisa fazer FULL SCAN (ler a tabela inteira) para retornar os dados solicitados.

Quando a aplicação AdvPL submeter a Query ao DBAccess, e este por sua vez submeter a query ao Banco de Dados, o AdvPL espera o retorno da API do DBAccess, e o DBAccess por sua vez está esperando o Banco de Dados. Isso também pode acontecer, por exemplo, com a chamada de uma Stored Procedure de processamento, quando parametrizada para rodar sobre grandes volumes de dados, ou mesmo falta de optimização de índices no banco para roda as queries submetidas por dentro da Stored Procedure.

No monitor do Protheus, será mostrado que o número total de instruções não aumenta, e o número de instruções por segundo permanece em 0 (zero). Ao abrir o Monitor do DBAccess — última versão do Portal — nós temos duas novas colunas de monitoramento muito úteis para casos como esse: A coluna “IDLE” e a “RUNNING”. A coluna IDLE indica a quantos segundos esta conexão do DBAccess não recebeu nenhum pedido de dados do programa AdvPL que a abriu, e a coluna “RUNNING” mostra naquele instante se e qual a rotina do DBAccess que está sendo executada.

Com isso, se a Query está ainda rodado dendo do Banco de Dados, a coluna RUNNING deve mostrar a operação OP_QUERY. No caso de uma Stored Procedure, se eu não me engano é a operação OP_SPEXEC.

Uma das colunas do DBACCESS Monitor — se eu não me engano DBSID ou apenas SID — mostra um identificador da conexão do DBAccess junto ao Banco de Dados. Esse identificador permite o DBA ou o Administrador do Ambiente a abrir uma conexão diretamente com o Banco de Dados, usando uma ferramenta de monitoramento nativa do Banco, e associar uma conexão mostrada pelo Monitor do SGDB com uma conexão do DBAccess.

Em um caso como esse, não adianta tentar matar a conexão do DBAccess com o Banco de Dados pelo Monitor do DBAccess, vai cair no mesmo problema do Protheus: Enquanto o DBAccess não receber um retorno da API do Banco, nada acontece … Mesma coisa derrubar com o Protheus Monitor … mesmo que você use a opção “derrubar imediatamente”, o SmartClient pode ser finalizado na hora, ao perceber que a conexão dele com o Protheus foi encerrada do lado do servidor, mas o programa AdvPL ainda vai estar esperando um retorno do DBAccess.

Neste caso, a última alternativa — antes de derrubar o serviço do Protheus e do DBAccess — é pegar o número ou identificador da conexão com o Banco de Dados, e usando uma ferramenta administrativa do Banco de Dados, pedir para o SGDB encerrar esse processo. Assim que isso foi feito, o SGDB retorna um erro de “Processo Interrompido” para o DBAccess, que por sua vez retorna este erro ao programa AdvPL.

Instrução em execução no SmartCient

Ao clicar naquele botão, o programa AdvPL em execução no Protheus Server pediu para o SmartClient abrir, por exemplo, uma URL a partir da estação onde o SmartClient está sedo executado — função HTTPCGet() — e o endereço solicitado está congestionado de requisições e coloca a sua na fila … Esta função tem um time-out de 120 segundos por default, o que pode “brecar o sistema” onde por 2 minutos.

Em um caso como esse ou similar, onde o Application Server está esperando por alguma coisa do SmartClient, quando você derruba (chuta) o SmartClient, finalizando o processo, o Protheus Server identifica que a interface de rede entre eles foi interrompida, e finaliza o programa AdvPL em execução com aquela ocorrência “Erro de Sincronismo”.

Conexão parcialmente fechada

Este é um cenário bem ingrato … você abriu um SmartClient, e iniciou aquele relatório que demora pelo menos uma hora … Você manda imprimir no SPOOL, e vai tomar um café. O programa que emite o relatório atualiza de vez em quando uma régua de processamento na tela do SmartClient. Passou uma hora, e a régua nem se mexeu … Você vai no Monitor do Protheus … e não encontra esse usuário. Vai no DBAccess, e também não acha nada … mas o SmartClient está ali, aberto, e a régua parada … e você não consegue nem clicar no botão cancelar … o que aconteceu?

Um caso como esse pode indicar uma conexão de rede parcialmente fechada. Durante o processamento do relatório, houve uma falha na rede, porém o encerramento da conexão TCP somente foi percebido pelo Protheus Server, quando ele foi atualizar a régua. Já o SmartClient, que fica somente esperando o Protheus pedir alguma coisa quando o controle de interface não está com ele, e existe um processamento no Protheus Server em andamento, caso a conexão TCP do lado do SmartClient não acuse o erro ou fechamento da outra ponta, o SmartClient fica esperando pra sempre um retorno que nunca vai chegar.

Cenários como esse podem ser contornados com a utilização de um aplicativo fornecido pela TOTVS para o Protheus Server chamado BROKER — ele serve de ponte e proxy reverso para as conexões do SmartClient para o(s) Protheus Server, inclusive para fazer balanceamento de carga. Ele entra na frente das conexões, tanto no SmartClient como no Application Server, e mantem uma conversa “constante” entre as pontas, sendo capaz de detectar com maior precisão quando uma das pontas foi desconectada,  e inclusive pode conseguir reconectar uma conexão encerrada devido a eventual e momentânea instabilidade na rede, sem que nenhuma das pontas (APPServer ou SmartClient) perceba o que aconteceu.

Ocorrências críticas

Um cenário difícil, mas plausível. Uma aplicação AdvPL reproduz um erro no Protheus Server, mostra uma caixa de diálogo com detalhes do erro no SmartClient, e quando você clica no “Ok” para fechar a janela, ela fecha. Então, você vai no DBAccess, e a conexão está lá .. e no license Server também … mas no Monitor do Protheus, esse usuário “sumiu”, e você tem certeza que ninguém usou aquele recurso de “derrubar imediatamente” aquele processo. O que pode ter acontecido?

Ao consultar o log de console do Protheus Server (console.log), você encontra o registro do Erro Advpl, logo depois uma mensagem parecida com “Critical Error”, seguida por “Falha no Delete da Thread” ou similar. Isto significa que, durante a descarga do ambiente, programas e recursos, ocorreu um erro critico na aplicação AdvPL, como pr exemplo invasão de memória, justamente enquanto aquele contexto de execução estava sendo limpo — executando os destrutores internos do Protheus. Se isso acontecer, uma parte dos recursos que seu processo continuam ativos neste processo mas o processo parcialmente não existe, somente o próprio processo consegue finalizar-se de modo elegante. Então, o processo some da lista de monitoramento, mas cai na malha dos processos com falha no destrutor.

Ocorrências desta natureza devem ser reportadas para a TOTVS, para a melhoria contínua do software. Normalmente a utilização de uma build DEBUG nestes casos ajuda a gerar um arquivo de CORE DUMP no momento que uma ocorrência crítica aconteça, gerando postas sólidas do que pode ter acontecido.

Conclusão

Espeto que estas poucas linhas ajudem aos analistas que procuram fantasmas nos ambientes do Protheus pelo mundo afora !!

Desejo novamente a todos TERABYTES DE SUCESSO !!! 

 

Anúncios