Acesso a dados – DBAccess

Introdução

Nos posts anteriores dos tópicos de acesso a dados, abordamos o mecanismo ISAM e o mecanismo relacional, e vimos alguma coisa do DBAccess da TOTVS, o Gateway de acesso a dados relacionais usado pelo Protheus Server. Hoje vamos aprofundar um pouco o conhecimento sobre esse Gateway, na forma de um FAQ, com muitas perguntas bem cabeludas …rs… seguidas das respectivas respostas.

O que é o DBAccess ?

O DBAccess é um gateway de acesso a dados para bancos de dados relacionais, utilizado pelo Protheus Server, para fornecer uma camada de abstração de acesso a arquivos usando a abordagem ISAM em um SGDB relacional, e uma camada de acesso de consulta relacional através de Queries.

Por que o DBAccess foi construído ?

As versões de ERP da Microsiga, antes do Protheus, originalmente foram concebidos para acessar os formatos de dados nativos do Clipper, que eram DBFNTX e DBFCDX, onde a aplicação acessava diretamente os dados. Com a utilização do driver do ADS Client no Clipper, foi possível utilizar o ADS Server, um SGDB ISAM com arquitetura client-server para o armazenamento dos dados do ERP.

Com o surgimento e popularização dos Bancos de Dados relacionais para baixa plataforma, onde os SGDBs relacionais permitiam mais recursos, consultas mais fáceis e mais rápidas, e novas funcionalidades, além de estender a capacidade do acesso a dados ISAM/xBase, houve a necessidade da Microsiga criar um Gateway de acesso a dados relacionais, que permitisse a execução do código legado (escrito em uma abordagem ISAM) em um SGDB relacional, e permitir acesso a novas funcionalidades disponíveis apenas no ambiente relacional.

Onde entra o TOPConnect nisso ?

O TopConnect foi a primeira geração de Gateways de acesso a dados, sua primeira versão foi concebida antes do Protheus, através de uma parceria comercial com uma empresa estrangeira de desenvolvimento de software. Ela permitia aplicações em Clipper acessar bancos de dados DB2/400 (IBM AS400), Sybase, Microsoft SQL 6.5, Sybase, PostgreSQL, Informix, DB2 UDB, Oracle 6, e MySQL. O TOPConect era escrito em C, e para cada SGDB existia uma versão específica do Gateway.

A Microsiga optou por desenvolver uma tecnologia própria de acesso, sendo assim desenvolvido o TOPConnect 4. Usando C++ com uma abstração de acesso a dados, o mesmo executável possuía as implementações para usar qualquer um dos SGDBs homologados. Como o Gateway é atrelado a versão de produtos do ERP Microsiga, a partir do Protheus 10 ele passou a chamar-se DBACcess, incorporando as implementações para as novas versões dos SGDBs. Com a fundação da TOTVS, o DBAccess passou a ser responsabilidade do Depto de Tecnologia da TOTVS.

O DBAccess consome muito recurso ?

Como um Gateway de acesso a dados, seu consumo de recursos de Memória é diretamente proporcional ao número de conexões, versus o número total de tabelas e queries abertas, versus o tamanho de registro da tabela. Seu consumo de CPU e Rede é diretamente proporcional à quantidade de requisições realizadas pela aplicação AdvPL ao gateway. O consumo é relativamente pequeno por tabela aberta, mas considerando um ambiente de 3000 conexões, onde cada pode abrir e manter abertas mais de 100 tabelas e queries, o consumo de memória pode atingir ou ultrapassar 4GB de RAM.

Como o DBAccess acessa o SGDB ?

O Acesso ao banco Oracle é feito via OCI (Oracle Client Interface), e todos os demais bancos são acessados via ODBC, usando a interface ODBC fornecida pelo fabricante do SGDB. É necessário que a ODBC ou OCI do SGDB em questão esteja instalada no equipamento onde o DBAccess será utilizado.

Por que o DBAccess não têm balanceamento de carga ?

Bem, como o acesso aos dados é feito apenas pelo SGDB, ele sempre será o destino final de todas as conexões. Então, não faz muito sentido “balancear” as conexões do DBAccess. Porém, em ambientes com mais de 2 mil conexões, pode ser muito interessante você usar mais de uma instância de DBAccess em máquinas distintas. Esta topologia é chamada de “DBAccess Distribuído”. Nesta topologia, um ou mais serviços de Protheus apontam para um DBAccess, e todos os DBACcess devem usar uma ODBC apontando para um único SGDB, além de cada DBAccess apontar para uma instância única, nomeada de “master”, que exerce o papel de servidor de locks de registro e locks virtuais.

O que acontece se eu colocar 2 DBAccess apontando para o mesmo Database ?

Se eles não estiverem configurados na topologia de “DBAccess distribuído”, cada um deles vai olhar para o seu próprio controle de emulação de Lock ISAM de registros. Neste caso, pode haver invasão de lock (duas conexões de instâncias distintas vão conseguir fazer RecLock() no mesmo registro), o que pode levar a quebra de integridade dos dados do registro, pois somente a última atualização irá prevalecer), e pode causar DeadLock em transações no SGDB.

Por que o DBAccess não usa o Lock do Banco de Dados ?

O mecanismo de bloqueio de registros do SGDB é de uso intrínseco do Banco de Dados, e não oferece a flexibilidade exigida para o mecanismo de acesso ISAM emulado. Mesmo se fosse construído um mecanismo de emulação direta no SGBD, a quantidade de IOs e instruções para emular isso no SGDB inivabilizariam seu uso por questões de desempenho. O mecanismo de Locks ISAM e Locks Virtuais é feito na memória do DBAccess, de forma muito rápida e eficiente.

Por quê existem os campos R_E_C_N_O_ e D_E_L_E_T_ ?

Como o TOPConnect surgiu devido a necessidade de executar um código escrito originalmente para engine ISAM, precisamos destes campos para emular o comportamento original do ISAM: Uma coluna interna para registrar a numeração sequencial de inserção (ordem física e identificador único de registro), e o campo “D_E_L_E_T” para marcar os registros que foram “marcados para deleção permanente” através da função DbDelete().

E por quê existe a coluna R_E_C_D_E_L_ em algumas tabelas ?

Quando foi implementado o conceito de criação de índice único nas tabelas do ERP, no Protheus 8 se eu não me engano, caso a tabela possua um índice de chave única definido pelo ERP, eu não posso ter um registro “ativo” na base com a mesma chave única. Porém, como as tabelas do DBAccess trabalham com o conceito de deleção “lógica” de registros, marcando os registros a serem eliminados fisicamente usando a função DbDelete(), eu posso ter um ou mais registros marcados para deleção, com a mesma chave única. Por exemplo, eu crio a tabela TESTE com a coluna CHAVE, e crio um índice de chave única com a coluna CHAVE. Se eu usar apenas esta coluna na minha chave única, se eu inserir um registro “000001”, depois deletá-lo ( campo D_E_L_E_T_ está com um “*”), e depois tentar inserir outro registro com “000001”, o SGDB não vai deixar …

Então, quando o ERP solicita ao DBAccess a criação de um índice de chave única, o DBAccess acrescenta na tabela a coluna de controle “R_E_C_D_E_L_”, coloca ela como último campo da chave única, e as colunas R_E_C_D_E_L_ de todos os registros marcados para deleção ( D_E_L_E_T_ = ‘*’) são alimentadas com o conteúdo do R_E_C_N_O_, e todos os registros não marcados para deleção ( ativos ) ficam com este campo ZERADO. Deste modo, eu posso ter um e apenas um registro não marcado para deleção com uma determinada chave única, mas eu posso ter um ou mais registros com o campo chave com este mesmo valor, caso eles estejam marcados para deleção.

Cada vez que o Protheus pede ao DBAccess para marcar um registro como “Deletado” através da função DbDelete(), o DBAcccess verifica se a tabela possui a coluna R_E_C_D_E_L_ , e atualiza ao mesmo tempo a coluna “D_E_L_E_T_” com ‘*’ e o R_E_C_D_E_L_ com o R_E_C_N_O_. Caso um registro seja recuperado, isto é, seja removida a marca de deleção, usando a função DbRecall(), a coluna R_E_C_D_E_L_ é atualizada para “0” zero. Deste modo, se você tentar inserir um registro ativo com uma chave duplicada, o SGDB não deixa fazer a inserção, e se você tentar desmarcar um registro deletado que tenha uma chave, e já existir um registro ativo (não marcado para deleção) com a mesma chave, o SGDB não permite a recuperação do registro, pois isto viola a chave única definida.

O que é mais rápido : Uma consulta ISAM ou por QUERY ?

Normalmente o acesso por Queries é mais rápido, sendo visivelmente mais rápido em leituras de registros sequencialmente. Um acesso de leitura no resultset de uma Query reflete os dados obtidos no momento que a Query foi executada. Já a leitura por acesso ISAM emulado retorna cada registro sob demanda, no momento que o registro é posicionado. A consulta por Query permite JOINS, para busca de dados de tabelas relacionadas. NO ISAM, você é obrigado a posicionar manualmente cada tabela relacionada e realizar a busca sob demanda.

Verdade que o DBSeek() é lento no DBAccess ?

Um DBSeek() no DBAccess procura posicionar no primeiro registro da ordem informada cujos campos que compõe a ordem do índice em uso que foram informados na instrução atendam a condição de busca. Se você faz um DBSeek() para posicionar no primeiro registro imediatamente superior a ordem desejada ( ou “SoftSeek” ) , DBSeek( cChave , .T. ) … o DBAccess pode submeter internamente várias Queries ao SGDB até achar o registro que satisfaça esta condição. Trocar um DBSeek() simples, que apenas verifica se um registro existe ou não, e trocar isso por uma Query, fatalmente vai ser mais lento, pois a abertua de uma Query para pegar apenas um registro vai usar 3 IOs, enquanto o DBSeek() faz apenas um. No caso da busca pelo último registro de uma determinada chave, uma Query pode ser mais rápida. Por exemplo, para saber qual foi a última data de um determinado evento, onde a tabela é indexada pelo código do evento mais a data, ao invés de pegar o código do evento, acrescentar uma unidade, procurar pelo primeiro registro da próxima sequência com uma chave parcial e SoftSeek, e depois fazer um Skip(-1) — coisa que é muito rápida e comum de ser feita no DBF — podemos simplesmente fazer uma query com “select max(datadoevento) as ultdata from tabela where codigodoevento = ‘xxxxxx’ and D_E_L_E_T != ‘*'”

E as inserções e Updates, são rápidas ?

Ao comparamos inserções via instrução direta “INSERT” e a inserção via DBAppend() — modo ISAM — , ambas são muito rápidas. A inserção tradicional de registros pelo AdvPL, usando DBAppend() e replace(s), possui um mecanismo de otimização que prioriza o envio das informações de inserção em apenas um evento de I/O. Porém, dependendo do que é executado durante a atribuição dos campos, enquanto o registro atual está em estado de inserção, o Protheus Server faz um “Flush” da inserção parcial, com os dados disponíveis até aquele momento, podendo executar mais I/Os de update para o mesmo registro enquanto a inserção não for finalizada. Este assunto será mais detalhado em um tópico específico de dicas de desempenho (Acelerando o AdvPL)

O que é o erro -35 ?

O código -35 é um código de erro genérico retornado pelo DBAccess quando a conexão com o SDGB não foi bem sucedida, OU quando a conexão não atendeu aos requisitos de operação com o SGDB. Para saber o que realmente causou o -35, o monitor do DBAccess deve ser acessado, e o registro de eventos de erro do DBAccess deve ser verificado. Pode ser desde o SGDB estar fora do ar, ou usuário e senha não configurados corretamente no DBAccess, falha de criação ou verificação de alguma tabela interna de controle do DBAccess, etc.

O que é o erro -2 ?

O erro -2 indica uma interrupção inesperada da conexão entre o Protheus Server e o DBAccess. Da mesma forma que o erro -35, o -2 pode ter diversas causas, desde um problema de rede entre o Protheus Server e o DBAccesse, ou entre o DBAccess e o SGDB, até um término anormal do processo que estava atendendo esta conexão no DBAccess (Assert Exception, Out Of Memory, Access Violation). Deve ser verificado o LOG do DBAccess para ver os detalhes do que aconteceu.

E os demais erros ?

Cada um possui um significado geral, para um tipo de operação que não foi bem sucedida. O código do erro apenas informa que uma operação falhou, porém somente descobriremos a causa efetiva da falha olhando o log de registro de erros do DBAccess. A lista de códigos de erro do DBAccess está na TDN, no link (http://tdn.totvs.com/pages/viewpage.action?pageId=6064500).

O DBAccess faz “leitura suja” de registros ?

Sim, para a grande maioria dos bancos. Devido a natureza das transações do SGDB, e da necessidade do comportamento esperado do AdvPL em ter acesso de leitura a qualquer registro sem espera ou bloqueio, mesmo que o dado esteja sendo alterado dentro de uma transação por outro processo, foi necessário definir explicitamente o nível de isolamento “READ UNCOMMITED”. O Banco Oracle não tem a possibilidade de leitura “suja”, ele é endereçado com “Read Commited”, mas ele permite que as demais conexões acessem a última versão committed, mesmo que exista uma transação aberta atualizando aquele dado.

O DBAccess “prende” conexões ?

Uma conexão feita pelo Protheus ao DBAccess faz o DBAccess abrir uma conexão no SGDB. Uma vez aberta, boa parte do tempo a conexão no DBAccess fica aberta, esperando o Protheus pedir alguma coisa ao DBAccess, como por exemplo: abrir uma tabela, uma query, posicionar em um registro, inserir um registro, executar uma Stored Procedure, etc. Ao receber uma requisição de abertura de Query, por exemplo, o DBAccess solicita ao SGDB a abertura da Query, e aguarda do SGDB um retorno ( sucesso ou erro). Enquanto isso, o Protheus fica esperando o DBAccess retornar. A Aplicação AdvPL estabelece a conexão com o DBAccess, e pode encerrar a conexão durante a execução da aplicação, e mesmo que o programa não desconecte, quando a Thread da aplicação AdvPL terminar, qualquer conexão com o DBAccess que tenha sido deixada aberta é encerrada. Existem algumas condições que o SGDB pode demorar a responder, como por exemplo um DeadLock no SGDB, a abertura de uma query muito complexa sobre um número muito grande de tabelas, a execução de uma Stored Procedure que fará muito processamento, etc.

Existe uma condição onde uma conexão pode ficar aberta no DBAccess, dando a impressão de estar “presa”: Caso a thread que está executando uma aplicação AdvPL, que consequentemente conectou no DBAccess, seja interrompida por uma ocorrência crítica de erro, como um Access Violation ou Segment Fault (Invasão de memória), e ocorra falha no destrutor da thread. O Processo em si não está mais no ar, mas a conexão permanecera aberta até que o serviço do Protheus seja finalizado.

E, existe também uma última condição, que pode fazer uma conexão no DBAccess permanecer aberta indefinidamente — ou até que o DBAccess seja finalizado, ou a conexão seja encerrada pelo DBAccess Monitor: Caso exista algum problema de rede entre a aplicação Protheus e o DBAccess, e ocorra uma queda na conexão TCP, quando o DBAccess está “IDLE”, esperando pelo Protheus pedir alguma coisa. Se a informação da perda da conexão não chegar ao Socket do DBAccess, ele vai ficar esperando indefinidamente, mantendo a conexão aberta e os recursos pertinentes alocados.

Existem estudos em andamento para criar novas funcionalidades na ferramenta, como permitir derrubar uma conexão do SGDB através do DBAccess, cancelando uma Query ou Stored Procedure, entre outros que eu não posso comentar agora …rs… Aguardem as próximas versões … 😀

Como o DBAccess encerra uma conexão pelo DBAccess Monitor ?

Um processo de conexão entre o DBAccess e um SGDB é mantido no ar enquanto existe a conexão entre o Protheus e o DBAccess. Logo, o DBAccess fica em um laço de espera por requisições. A cada intervalo de alguns segundos que o Protheus não envia nenhuma requisição, e a cada requisição recebida, ele verifica um flag de controle de processo, que indica se o DBAccess Monitor pediu para aquela conexão ser encerrada. Quando você pede ao DBAccess Monitor para encerrar uma conexão, ele apenas seta este flag no processo, que somente será considerado e avaliado quando o DBAccess está esperando por uma requisição do Protheus. Se  DBAccess pediu pro banco a execução de uma Stored PRocedure, que pode demorar de segundos a minutos, e neste meio tempo você pedir para a conexão ser encerrada através do DBAccess Monitor, ele somente vai encerrar a conexão com o SGDB quando a procedure terminar, e ele voltar ao loop de requisições. Se por um acaso você quer que a procedure ou query que está demorando seja interrompida, somente um DBA com acesso ao SGDB consegue derrubar o processo usando algum mecanismo de administração do SGDB.

E se eu derrubar o processo no SGDB ?

Se o DBAccess está executando algo no SGDB, e aguardando por um retorno, e o processo for finalizado no SGDB, o DBAccess receberá um retorno de erro, e repassa ao Protheus. Se o DBAcccess não estava fazendo nada no SGDB, e você derruba a conexão no SGDB …. O DBaccess somente vai “perceber” que a conexão foi pro espaço quando o PRotheus pedir alguma coisa, e o DBAccess for tentar pedir algo para o SGDB através da conexão que não existe mais …

Por que o DBAccess não traz campos MEMO em Query ?

Um campo MEMO pode conter muito mais dados em um campo do que em muitos registros. Uma tabela DBF por padrão traz todos os campos do registro atualmente posicionado a cada cada DbSkip() ou DBSeek(). Para ganhar desempenho e não onerar os processos do sistema que fazem leituras sequenciais em processamentos, os campos MEMO são trafegados somente sob demanda, quando abrimos a tabela em modo ISAM emulado, e somente quando a aplicação tenta ler ou acessar um campo Memo do registro atualmente posicionado.

Como os campos MEMO foram implementados no DBAccess para garantir a compatibilidade com o campo Memo do DBF, a forma de armazenar esta informação em cada SGDB é escolhida diretamente pelo DBAccess, a critério dele. Por questões de economia e otimização de recursos (estamos falando da aplicação que nasceu em um tempo onde uma rede 100 MBits era um “luxo”), foi decidido não retornar campos MEMO em Queries, o DBAccess não faz o Bind dos dados de campos usados como “Memo”. Por isso, se hoje você precisa da informação de um ou mais campos memo de uma tabela, você pode selecionar os dados necessários por Query, porém você recupera também o número do registro (R_E_C_N_O_) na Query, usando um outro nome para este campo, e mantendo a tabela original aberta em modo ISAM emulado, você posiciona no registro desejado usando DbGoto(), e então faz um Fieldget() do campo MEMO.

Quem alimenta o R_E_C_N_O_ da tabela na inserção de registros ?

O DBAccess possui um cache das estruturas de colunas e índices das tabelas, alimentado sob demanda, e um controle de numeração e locks. Ele guarda o número do ultimo registro inserido em uma lista em memória, e cada nova inserção incrementa o último registro da tabela na lista. Este processo é muito rápido, porém torna algo nada prático você fazer inserções através de uma Query ou Stored Procedure. Foi criado um mecanismo para permitir o DBAccess criar uma tabela com numeração automática de R_E_C_N_O_ pelo SGDB, disponibilizado para o FrameWork AdvPl, onde alguns novos módulos desenvolvidos no ERP Microsiga já se utilizam desta funcionalidade. Porém, isto ainda não é extensível para as demais tabelas dos módulos do ERP por questões de impacto. Todas as rotinas hoje escritas que alimentam tabelas, por exemplo via Stored Procedure, precisariam ser refatoradas para contemplar esta funcionalidade, pois nenhuma delas poderia mais fornecer um número de R_E_C_N_O_ ao fazer inserção na tabela, e imediatamente após a inserção, algumas delas precisam obter do SGDB qual foi o número do registro inserido.

Não é algo simples de ser feito, ainda mais “de uma vez”. Para cada SGDB o DBAccess define uma forma de criar o campo com auto-incremento, algumas usando um tipo de campo de auto-incremento do próprio SGDB, outas através de gatilhos criados internamente pelo DBAccess no momento de criação da tabela com estas características.

Para que serve a tabela TOP_FIELD, criada pelo DBAccess ?

Como a aplicação foi feita para emular ISAM, no momento da criação da tabela pelo Protheus, a estrutura da tabela é informada ISAM/DBF, onde especificamos campos do tipo “C” Caractere, “N” numérico (com precisão inteira ou decimal), “D” Data, “L” Lógico e “M” Memo. Porém, como o DBAccess escolhe cada tipo de campo que se adéqua melhor a necessidade, ele precisa guardar algumas definições que o Protheus forneceu na criação da tabela. E ele faz isso na tabenla TOP_FIELD.  Se você copia uma tabela diretamente de um Ddatabase para outro, no mesmo banco, mas não copia as definições da TOP_FIELD, todos os campos “D” data serão mostrados como “C” Caractere de 8 bytes, cmapos “L” lógicos serão “C” caractere de 1 byte, contendo “T” ou “F”, e todos os campos numéricos vão vir com uma precisão de 15 dígitos com 8 decimais.

Por quê as tabelas do DBAccess usam constraints DEFAULT ?

Não existe o valor “NULL” nas bases ISAM usadas pelo ERP Microsiga. Logo, mesmo que o campo esteja vazio, ele precisa ter o seu conteúdo default ( caracteres em branco, números com 0, data com string em branco, booleano com “F”). A aplicação conta com este comportamento, e as queries e joins foram construídas baseadas nesta premissa.

Como o DBAccess faz alteração estrutural na tabela ?

Através da função TC_Alter(), o DBAccess recebe a estrutura atual da tabela e a nova estrutura desejada, e determina para cada SGDB a sequência de operações necessárias para ajustar a tabela para ela ficar com a definição da nova estrutura, através do cruzamento das estruturas. Campos existentes na estrutura antiga e não existentes na nova são removidos, campos existentes na nova e não existentes na antiga são criados, e campos existentes nas duas podem ter suas características alteradas. Apenas as trocas de tipo de “C” Caractere para “N” numérico e vice-versa suportam manter os dados nos campos.

Como funciona a transação no DBAccess ?

Cada SGSB homologado possui transacionamento atômico por instrução. Isto significa que, caso seja disparado uma execução SQL de um Update que afete várias linhas, se uma não pode ser alterada, nenhuma será. Quando precisamos garantir que várias operações em um bloco sejam completas em conjunto, usamos as instruções BEGIN TRANSACTION e END TRANSACTION do Advpl, onde todas as instruções executadas dentro deste bloco não vão fazer COMMIT das informações no SGDB, isto será feito apenas no END TRANSACTION. Se ocorrer algum erro durante o processo, entre o Begin e o End transaction, todas as operações feitas a partir do BEGIN TRANSACTION serão descartadas. Por baixo destas instruções existe uma implementação que depende do ambiente ERP Microsiga, isto é, o processo em execução precisa ser um programa do ERP chamado a partir do Menu, ou um Job que faça a inicialização do ambiente ERP usando por exemplo o comando PREPARE ENVIRONMENT ou a função RcpSetEnv(). Estes tratamentos também estão atrelados às funções RecLock() e MsUnlock() do Framework AdvPL do ERP Microsiga.

O DBAccess pode conectar com outros SGDBs ?

Sim, ele pode. Porém, esta conexão é feita via uma conexão ODBC genérica, que não permite a emulação ISAM. Praticamente qualquer ODBC que você possa registrar como fonte de dados de ODBC no Windows pode ser acessada. Usando a build mais atual do DBAccess, existe uma aba de configuração de ODBC genérica. Você pode estabelecer a conexão usando a função TClink(), informando o banco “ODBC/” mais o alias da fonte de dados cadastrada no Gerenciador de Fontes ODBC do sistema operacional. Com esta conexão, voce pode abrir Queries, que devem ser montadas de acordo com a capacidade e regras da ODBC utilzada, onde os dados retornados podem ser char/varchar ou numéricos, usando DbUseArea() com TcGenWry(), e pode executar instruções diretamente no SGDB através da função AdvPL TcSqlExec(). Isto pode ser muito útil para realizar integrações com outras fontes de dados.

Conclusão

Eu acho que com estes parágrafos, dá pra matar um pouco a curiosidade sobre o DBAccess e seu papel no acesso a dados do ERP Microsiga. Caso algúem tenha mais alguma pergunta a acrescentar sobre este assunto, insira a sua pergunta como um comentário deste post 😀 PAra dúvidas e sugestões de outros assuntos, me envie um e-mail com o assunto “BLOG” para siga0984@gmail.com 😀

Novamente, agradeço a audiência, e desejo a todos TERABYTES de sucesso 😉

Até o próximo post, pessoal 😀

Anúncios

25 comentários sobre “Acesso a dados – DBAccess

  1. Julio, é verdade que quando o Protheus monta um browse, para cada linha a ser exibida é feito um SELECT no banco? Se sim, ainda é assim ou apenas em versões anteriores?

    Curtido por 1 pessoa

    • Opa, fejamos … Na verdade, para cada DbSkip() do Browse, para navegar entre as linhas, é feito um select para pegar o conteúdo atualizado da linha a ser mostrada na tela. Este select é pré-montado no DBAccess, e é mais rápido que sumbeter uma Query do Advpl. Os Browses do ERP Microsiga sempre foram “online”, se dois terminais abrem o mesmo cadastro, e você vê informações na tela do Browse, e outro operador altera uma informação, quando você focar no registro ou mover o ponteiro de registro no Browse, o conteúdo é atualizado. Este tipo de atualização em tempo real exige este comportamento. Estamos falando dos Browses do Padrão, existem Browses específicos que são feitos sobre tabelas temporárias e até Array.

      😉

      Curtir

  2. Conexão “presa” de fato gera algumas dores de cabeça.
    Muitas vezes encerro a conexão pelo monitor do DBAccess, mas na seção de comentários inclui um “Terminate” e fica lá. Para eliminar de vez preciso intervir pelo SGBD com a query (Oracle) ALTER SYSTEM KILL SESSION ‘999,99’ IMMEDIATE, onde ‘999,99’ é o SID que aparece na seção DB thread.
    Novas funcionalidades para ajudar nesse processo serão muito bem-vindas. 🙂

    Curtido por 1 pessoa

  3. Cada vez que leio os post aqui, vejo que ainda tem um mundo de coisas que preciso aprender.

    Cara, quanto a “Leitura Suja”, rodando uma query no MS-SQL, em uma tabela que está sendo manipulada, as informações não serão tão precisas, então?
    No caso, de montar um relatório através de querys rodadas direto no SQL.

    Valew, Júlio!!

    Curtido por 1 pessoa

    • Opa, o aprendizado é constante, inclusive pra mim, que preciso me aprofundar mais para garantir a consistência do conteúdo publicado …rs…

      Vejamos, um cursor retornado de uma Query reflete os dados nas tabelas no momento da abertura do cursor. Uma leitura suja pode trazer dados que estão sendo colocados no banco por uma transação que ainda não foi confirmada no SGDB. Normalmente as transações encerram com successo. A única chance de um determinado relatório apresentar alguma inconsistência, é voce tirar por exemplo, durante o dia, um relatório baseado em registros que estão sendo inseridos ou alterados em um arquivo, onde a transação estava aberta durante a abertura da Query, e os dados alterados ou inseridos não serem efetivados no banco por um erro durante a execução da rotina transacionada. Não é algo comum de acontecer, apenas pode acontecer.

      😉

      Curtir

  4. Ótimo artigo! Obrigado por continuar a compartilhar o seu conhecimento com a gente 🙂

    Segue a pergunta:

    Usar ODBC não é relativamente lento? Um colega sempre comenta comigo ” Porque a TOTVS não faz a conexão direta com o SGBD? Podia criar o driver pelo menos pra SQL que é o mais usado e conectar direto, ia ser muito mais rápido!”. Ele está certo?

    Abraço!

    Curtido por 1 pessoa

    • Opa, boa noite 😀 Grato pelas considerações …rs…

      Vejamos: O TOPConnect chegou a usar ADO em algum momento, era mais rápido, porém faltavam alguns tratamentos, que quando foram feitos deixaram a conexão mais lenta que via ODBC. O Driver do MSSQL possui algumas versões de ODBC, uma delas inclusive chamada de “Native Client”. Nos meus testes, não vi mudança de desempenho entre versões de ODBC. Devido a natureza das operações executadas, o OverHead na camada de rede acaba “qualizando” pra baixar os tempos de acesso. As instruções que o DBAccess envia para o SGDB são relativamente simples e curtas, mas fazem muito I/O. O uso da ODBC permite parametrizações internas e abordagens que deixam a integração com o SGDB bem “espertinha” …rs…

      Curtir

  5. Júlio,

    O Monitor do DBAccess exibe mensagens de erros e avisos. A alguns anos, a Totvs disponibilizou uma versão que, conforme ela, poderia ser desativado os avisos, porém, não vi este recurso presente.

    Você teria alguma dica a dar para que os avisos (Warning) sejam desativados?

    Att,

    Fábio Viana

    Curtido por 2 pessoas

    • Olá Fábio, tudo bom ?

      Acho que você está se referindo a Build 20130225. Achei o link deste release na TDN (http://tdn.totvs.com/pages/viewpage.action?pageId=51249493) . A partir desta build, algumas mensagens de advertência menos significativas foram removidas, mas não todas. A build que removeria todas as advertências dos logs, mediante parâmetro, pelo que eu vi ainda não saiu, ou não saiu nenhuma nota de release sobre este parâmetro.

      Se eu descobrir mais alguma coisa, eu aviso 😀

      Abraços

      Curtir

  6. Ótimo post!!
    Ajudou a fazer algumas pesquisas aqui.
    Júlio, você conhece alguma maneira de eu encerrar as conexão para uma tabela específica?
    Tenho uns campos na CNB que estão somente na X3 e apesar de não ter pessoas usando o módulo de contratos não consigo dropar a tabela para recriá-la, sempre que tento o sistema diz que a tabela está em uso.
    Mesmo que eu exclua ela diretamente no banco, o DBAcces não entende que ela foi excluída, ele tenta procurar uma tabela que não existe e aí o problema é maior ainda.
    Tentei usar a função X31UPDTABLE(‘CNB’), mas sem sucesso também.
    Já passou por situação similar?
    A minha janela para parar o sistema é bem curta e isso me ajudaria a otimizar meu tempo e trabalho.

    Curtido por 1 pessoa

    • Olá Uilon, bom dia, e obrigado pelo feedback 😀

      Agora, vejamos a sua questão … O DBAccess parte da premissa que ele é o único que deve controlar o acesso a existência e manutenção da tabela, inclusive controlar a emulação de acesso exclusivo / compartilhado.

      Você consegue dropar a tabela diretamente no banco, furando este bloqueio, porém como esta operação foi feita diretamente no Banco, o DBAccess ainda têm a definição da estrutura da tabela em um cache interno, e “acha” que a tabela ainda existe. ( A função TCCanOpen() continua retornando .T. ).

      Existe uma forma de você fazer um refresh no cache de estrutura de uma tabela, você pode usar a função TCRefresh(), informando o nome da tabela no banco, por exemplo TCRefresh(“CNB990”).

      Uma vez feito isso, o DBAccess identifica que a tabela não existe mais, e remove a estrutura do cache.

      Então, quando você abrir uma nova conexão no ERP e tentar abrir a tabela — fazendo um DbSelectArea() ou chamando a função ChkFile(“CNB”), o Framework vai recriar a tabela vazia, com a estrutura definida no SX3.

      Feito isso, e voce já tendo realizado uma copia da tabela origianl pelo APSDU, eu acredito que você consiga repopular o conteúdo da tabela fazendo um ‘append from’ pelo APSDU mesmo.

      Porém, esta é uma operação não recomendada, pois existem vários riscos, que justamente o sistema de bloqueio de acesso do DBAccess visa proteger, por exemplo :

      1) Se a tabela foi dropada direto no banco, e existem programas em execução que estão com a tabela aberta, estes programas vão ser finalizados com erro se tentarem usar a tabela.

      2) Após recriar a tabela, ela vai estar vazia, e enquanto os dados não forem colocados de volta, e novas conexões do ERP forem realizadas e usarem esta tabela, qualquer programa que toma decisões baseada nos dados desta tabela pode não ter o comportamento esperado, pois não há dados na tabela.

      3) Qualquer procedimento direto de manutenção de tabela por fora do sistema não é recomendado pela Totvs, justamente pelos efeitos colaterais não serem previsíveis, e podem comprometer a integridade do ambiente.

      Espero ter ajudado de alguma forma 😉

      Curtir

    • Opa, beleza ? Existe a TCGetDB(), mas ela retorna apenas o Database ( MSSQL , DB2, etc… ) , ainda não tem nenhuma que retorna o DBALIAS da conexão corrente.

      Curtir

  7. Tenho o server configurado com ADSSERVER cuidando da integridade do dicionario de dados.
    localFiles=ADSSERVER
    ADSShare=\\producao\Totvs11\Microsiga

    É possível configurar um ambiente de homologação com o ADSSERVER, usando o mesmo ADSSERVER que está instalado no servidor de produção ?

    Curtido por 1 pessoa

    • Fala Marcelo, beleza ? Olha, faz tempo que eu não mexo com ADS Server … MAS, até onde eu lembro ( e consultei no TDN ..rs.. http://tdn.totvs.com/display/tec/ADSShare ), a configuração ADSShare indica a pasta compartilhada onde está o ADS Server. Logo, eu acredito que você não tenha problemas em criar dois ambientes, com rootpath e dicionários diferentes, sendo atendidos pelo mesmo ADS Server 😉

      Abraços

      Curtir

  8. Olá Julio,

    Muito interessante alguns detalhes mencionados no artigo. Você saberia dizer se existe alguma forma de sinalizar diretamente ao DBAcess para que (forçadamente) atualize seu cache de controle de numeração das tabelas? Isso seria especialmente últil em algumas situações específicas onde é necessário efetuar adição de registros diretamente no banco de dados.

    Já precisei realizar esse tipo de operação para transporte direto de informações de um DB legado para as tabelas do Protheus e em geral funciona, exceto pela necessidade de ter que interromper o DBAccess no final do processo e reiniciá-lo para que não fique ocorrendo erros nas rotinas do Protheus por conta de uso de RECNO já usados.

    Uma outra idéia que me ocorreu é configurar o DBAccess em modo distribuído, mesmo utilizando apenas uma instância do DBAccess. Isso faria com que ele verificasse/atualizasse as informações de cache de forma ativa?

    Abs,

    Curtido por 1 pessoa

    • Olá Gabriel,

      Uma vez que você faça uma inserção de registro diretamente na tabela, ou mesmo por TCSqlExec(), você pode usar a função TCRefresh() para forçar ao DBAccess atualizar o cache de numeração de registro. Porém, isso deve ser usado com parsimônia, pois esta função não apenas atualiza o LastRec() da tabela para as APIS do Protheus, mas também REFAZ um “describe” da estrutura da tabela e de seus indices. Em resumo, se você começar a espalhar TCRefresh pelo seu código sem necessidade, você fatalmente vai deixar suas APIs mais pesadas.

      O DBAccess prevê a possibilidade dele estar com o cache de numeração de registros desatualizado. Quando o DBAccess tenta fazer uma inserção re registro, e a inserção não for possível por violação da Primary Key (_E_C_N_O_), ele automaticamente busca na tabela o último numero atualizado de registro, atualiza seu cache, soma 1 e refaz a inserção.

      Resumindo, mesmo que o DBAccess possa registrar um erro de violação de Primary Key no momento de uma inserção, esse erro não deveria derrubar a aplicação AdvPL 😀

      Quando ao DBAccess distribuído, o cache de definição de tabelas e controle de numeração de registros é centralizado no DBAccess “Master”, mas segue as mesmas premissas do DBAccess “StandAlone” 😉

      Abraços

      Curtir

      • Olá Júlio,

        O meu caso é um pouco diferente pois efetuei inserção de registros por fora do Protheus (diretamente no banco de dados). Dessa forma o DBAccess não consegue identificar as alterações de imediato. Curiosamente, na maioria das vezes o próprio DBAccess detecta a violação de R_E_C_N_O_ e faz os acertos sem provocar maiores problemas, porém isso não ocorre sempre. Não raramente ele derruba a aplicação por erros relacionados a violação de chaves primárias e apenas reiniciando o DBAccess se consegue retornar a normalidade.

        Tentei criar uma pequena rotina com TCRefresh() para chamada direto do smartclient, permitindo assim que forçasse uma atualização do DBAccess pontualmente, porém aparentemente o TCRefresh não permite execução sem que um ambiente do Protheus esteja montado. Montar um ambiente apenas para disparar um TCRefresh exigiria processo de login, inviabilizando uma automação. Não explorei criar uma JOB no appserver para isso. Talvez seja uma forma de contornar.

        Você saberia dizer se há alguma forma de sinalizar o DBAcess diretamente, por exemplo enviando uma mensagem direto em sua porta 7890?

        Abs,

        Curtido por 2 pessoas

      • Rapaz, até onde eu conheço do DBAccess, somente via uma rotina dvPL usando TCRefresh() , e informando como parametro o nome fisico da tabela no banco, sera possivel o DBAccess atualizar o cache de Ultimo Recno da tabela. Porem, a rotina AdvPL nao precisa necessariamente fazer um RPCSetEnv() inteiro e/ou um “login”, basta fazer um tclink() para o DBAccess, e chamar a TCRefresh() informando o nome da tabela ao inves do Alias.. Por exemplo, para a tabela SA1 da empresa 01, basta chamar a TCRefresh() informando “SA1010” como parametro 😀

        Abraços

        Curtido por 1 pessoa

  9. Olá Julio,
    Estou enfrentando um grande problema com os Locks, tenho uma rotina de importação de pedidos e nela faço diversas verificações em tabelas variadas, geralmente algum registro dessas tabelas estão em Lock, gerando uma lentidão enorme em horários de pico.
    Tem alguma boa pratica ou sugestão para que eu consiga amenizar o problema?

    Curtido por 1 pessoa

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s