Página 1 de 2

DBF compartilhado em REDE

Enviado: 16 Jul 2013 12:11
por VanderSimples
O motivo do meu registro nesse forum é um problema que detectei no DBF e pelos testes que executei parece ser mesmo um defeito muito grave do DBF.

Bom eu não sabia usar o Harbour, então eu aprendi hoje mesmo como compilar um sistema básico e compilei o mesmo exemplo que tinha feito no Clipper e no VFP.

Primeiramente eu criei o exemplo que chamo de TESTE3:
Este exemplo cria 1 arquivo e gera 1000 registros no mesmo, em modo exclusivo e em modo compartilhado.
Ao executar o TESTE3 em meu computador um I5 deu o seguinte resultado:

XHARBOUR:
Exclusivo: 0.01
Compartilhado: 0.03

CLIPPER:
Exclusivo: 0.00
Compartilhado: 0.05

VFP 9.0:
Exclusivo: 0.01
Compartilhado: 0.07


Até aqui tudo bem, é sabido que o DBF aberto em modo compartilhado tem de executar funções de controle e proteção o que gasta mais tempo.

Agora a grande surpresa esta quando eu executo em outro terminal da rede o programa do TESTE2, que é um programa simples que apenas abre o arquivo também compartilhado e não faz nada mais.

Com o TESTE2 executando em outro terminal o modo exclusivo não funciona então o resultado é apenas do modo compartilhado.

XHARBOUR: Compartilhado: 4.09

CLIPPER: Compartilhado: 4.06

VFP 9.0: Compartilhado: 3.24


CONCLUSÃO:
Comprovadamente é uma limitação do DBF, porque as linguagens apresentaram o mesmo problema, tendo uma perda de velocidade muito grande.

Será que alguém sabe uma solução que não seja abrir e fechar sempre que vai manipular um arquivo?

O problema no meu sistema esta na hora de fazer relatório, porque se alguém estiver com a tela de produto aberta quando outro for fazer um relatório demora muito, passando de segundos para minutos o tempo de visualizar um relatório.

Obs1.: Ja fiz o teste usando DBFNTX e DBFCDX, e o resultado é o mesmo.
Obs2.: Se alguém tiver outras sugestões de teste irei analisar.


* TESTE 3

Código: Selecionar todos


Function MAIN()

aCria := {}
Aadd( aCria, { 'PR_PTMP', 'N',17,2 } ) // TMP
Aadd( aCria, { 'PR_VTMP', 'N',17,2 } ) // TMP
if !file('PRO.DBF')
   DbCreate( 'PRO',aCria )
endif

use PRO EXCL
if used()
nTempo = seconds()
For i=1 to 1000
   APPEND BLANK
   Repl PR_PTMP with 0
   Repl PR_VTMP with 0
   unlock
Next i

? 'EXCLUSIVO'
? seconds() - nTempo   
?
zap
endif

use PRO shared
go top
nTempo = seconds()
For i=1 to 1000
   APPEND BLANK
   Repl PR_PTMP with 0
   Repl PR_VTMP with 0
   unlock
Next i
? 'COMPARTILHADO'
? seconds() - nTempo   
?

Return
* TESTE2.PRG

Código: Selecionar todos

Function MAIN()

cNada = space(20)
use pro shared
@ 10,10 get cNADA
read

Return .T.

DBF compartilhado em REDE

Enviado: 16 Jul 2013 13:45
por Eolo
Sem qualquer ordem de importância, aqui vão uns palpites, pra engrossar o caldo.

UM
O seu exemplo gera – em uma única tacada - 1.000 registros no DBF, em modo exclusivo. Eu acho que, para efeito de teste, pode ser válido mas, numa operação “normal”, eu pelo menos nunca me deparei com algo do tipo. Acontece, sim, de novos produtos ou alterações serem absolutamente necessárias no cadastro, em “run time”, mas não 1.000 em uma só tacada. Eu já passei por isso, mas foi na pré-operação, na fase de construção das tabelas, antes do usuário abrir as portas pros clientes finais.

DOIS
Modo exclusivo: só uso isso quando mando refazer os índices ou becape (quando o EXCL é mandatório). Nos demais casos, duas opções. Primeiro, o FLOCK, quando é necessária uma “fotografia” estática do DBF, naquele momento: ninguém mais consegue fazer alterações no DBF mas os seus dados ficam totalmente visíveis e acessíveis na rede. Segundo, o RLOCK, quando só aquele registro em particular precisa ter acesso exclusivo, o resto do DBF fica livre.

TRÊS
Quando vc diz “... se alguém estiver com a tela de produto aberta e outro for fazer um relatório...”, eu entendo que o tal relatório é sobre produtos. Mas exatamente qual relatório? Vendas? Margem de Lucro? Lista de produtos cadastrados?

Meu palpite é que talvez o problema esteja antes, no desenho da sua base de dados e na política de uso das tabelas. No meu caso, eu sempre faço DBFs separados. Os produtos estão em um DBF, as vendas em outro, margens em outro etc. Se o “mala” do financeiro tá com tempo ocioso e inventa de imprimir a lista de produtos em modo exclusivo, durante o expediente, só pra arrumar o que fazer, tranca ele o banheiro! E amordaçado. Aí o pessoal do balcão pode continuar vendendo normalmente.

Se o controller quer examinar as margens de lucro, produto por produto, nos últimos 15 anos, mês a mês, dia a dia, ele abre o Margens.dbf, atualizado até ONTEM. Pode surfar à vontade, não vai atrapalhar ninguém. E nem precisa dos dados de HOJE, que não vão ser significativos pra análise dele.

Se o cara de compras quer projetar seus números, abre o Xpto.dbf. Lá estão as médias de consumo do último milênio, dia a dia, semana a semana, atualizadas até ONTEM. Ele pode brincar à vontade, não vai atrapalhar ninguém.

TRÊS.1
Por que faço isso? As vendas passadas não vão mudar. Então, pra que, a cada necessidade, vasculhar o Vendas.dbf inteiro, registro a registro, eventualmente em modo EXCL? Quer as vendas de 01-jan-1975? É só abrir o Vendas1975.dbf.

QUATRO
Append Blank. Quando vc usa esse comando, o Clipper adiciona um registro, bloqueado pra vc, e depois dos Replace precisa dar o UNLOCK, certo? Aqui, novamente, eu acho que é problema de administração e não do software. No lado do software, no meu caso, se é inclusão de produto (UNIQUE) eu uso o FLOCK e o APPE BLAN (só na hora de gravar!). Quem chegar primeiro, grava. O mais lerdo vai receber um “produto já cadastrado”. Funciona.

Mas vem a pergunta: esta é a hora certa de fazer o APPEND? Você vai incluir um produto ou alterar o preço de outro num horário de pico? Não é software mais. É administração.

CINCO
...

DBF compartilhado em REDE

Enviado: 16 Jul 2013 14:05
por Jairo Maia
Olá Pessoal,

Pegando carona, eu mudaria o bloco de ambos exemplos que está assim:

Código: Selecionar todos

For i=1 to 1000
   APPEND BLANK
   Repl PR_PTMP with 0
   Repl PR_VTMP with 0
   unlock
Next i
Para esta forma:

Código: Selecionar todos

For i=1 to 1000
   APPEND BLANK
   Repl PR_PTMP with 0
   Repl PR_VTMP with 0
Next i
Commit
Como o colega Eolo já disse, o Append Blank bloqueia o registro, e liberá-lo a cada vez é que demora muito. Assim, após gravar os registros, basta usar o Commit, que grava no banco de dados os registros de memória e desbloqueia todos que foram bloqueados pelo Append Blank. Pelo menos no caso deste exemplo o tempo irá reduzir muito.

DBF compartilhado em REDE

Enviado: 16 Jul 2013 14:40
por Eolo
Assim, após gravar os registros, basta usar o Commit, que grava no banco de dados os registros de memória e desbloqueia todos que foram bloqueados pelo Append Blank.
Jairo, o COMMIT grava em disco e torna visíveis as alterações pra rede, mas não desbloqueia nada, não. Fui inclusive conferir no HELP do xHarbour:
The COMMIT command flushes the memory buffers of all used work areas and writes them to disk. Data held in memory for database and index files are permanently written to the respective files. Changes to the data become visible to other processes in a network environment.
It is recommended to call COMMIT before UNLOCK. To flush the memory buffers of a single work area, call function DbCommit().
Tá lá: "é recomendado usar COMMIT antes do UNLOCK".

E o Clipper diz a mesma coisa...


Em tempo: o COMMIT atua sobre TODAS as áreas de trabalho abertas, da mesma forma que o DBCOMMITALL(). Se é só UMA área, melhor o DBCOMMIT().

DBF compartilhado em REDE

Enviado: 16 Jul 2013 18:28
por Eolo
Vander, continuando...

CINCO
Sobre a opção de abrir ou não todos os DBFs + NTX/CDX de uma vez, acho que já há discussões sobre o assunto aqui no Forum, abordando performance, segurança, integridade dos dados etc etc. É dar uma pesquisada.

Eu, nos programas mais antigos, com o Clipper, abria todos os DBF, no início do EXE, e só os fechava na saída. Nos programas mais recentes, Clipper ou xHarbour, mudei a forma de organizar os dados e só abro os que são necessários no momento, fechando imediatamente após o uso. Até porque tem DBF (vendas de 1975, por exemplo) que só é aberto sei lá quando...

CINCO.1
Vou mais longe: se um usuário vai abrindo janelas (e, eventualmente, os respectivos DBFs) e, lá no nível Z (com trocentas janelas abertas) resolve ir pra casa deixando tudo ligado, não deixo. Se qualquer janela fica X minutos inativa, sem uso, o EXE a fecha automaticamente. E vai fechando cada janela nível acima até sair por completo do sistema. Fiz isso porque, quando ia dar alguma manutenção depois do expediente (leia-se 2 da manhã), descobria meia dúzia de estações ligadas à toa...

SEIS
...

DBF compartilhado em REDE

Enviado: 16 Jul 2013 19:52
por Jairo Maia
Caro Mestre Eolo,
Eolo escreveu:Jairo, o COMMIT grava em disco e torna visíveis as alterações pra rede, mas não desbloqueia nada, não. Fui inclusive conferir no HELP do xHarbour: [...]
Se não está documentado em [x]Harbour, basta fazer os testes:

Todo registro criado pelo comando APPEND BLANK, serão liberados após usar um dos seguintes comandos:

COMMIT (grava os registros da memória da área atual, e libera todos na rede e liberados para alteração), CLOSE (fecha a área atual, e ademais idem ao caso anterior), CLOSE ALL (para todas as áreas, fecha todos, e libera na rede para alteração), CLOSE alias (idem as anteriores mas apenas para a área do aliás especificado).

Além dos convencionais para liberar a rede, tipo UNLOCK etc.

DBF compartilhado em REDE

Enviado: 19 Jul 2013 14:56
por VanderSimples
Ola Agradeço ao pessoal que respondeu, e peço desculpas mas a semana foi muito corrida e só puder ler hoje.

Bom, eu usei este exemplo apenas para que se alguém desejar gerar o teste em seu ambiente poderia fazer sem necessidade de uma tabela.

O trecho do sistema não faz Append Black, é um Do/While que zera 2 campos da tabela de produtos que são populados logo após. se deixar um tabela pronta com 1000 registro e executar o teste irá ocorrer o mesmo delay.

Este trecho que eu peguei também não é o único. O que eu estou querendo provar é que isso não é uma questão de código ou forma de implementar, esta degradação ocorre, sempre que se processa uma tabela quando tem algum terminal com a mesma aberta, seja dando append, replace repetidos ou mesmo lendo os registro.

Se alguém puder por gentileza compilar os exemplos que mandei e dizer as velocidades que obteve em seu computador me ajudaria muito.

Quanto a abrir o arquivo eu não abro no inicio do sistema, mas abro na tela de cadastro, porque uso Skip para mudar de registro e tela de pesquisa e quando esta nessa tela uso a tabela REAL e não uma cópia ou apenas parte como é de costume usar em SQL. "Se comprovar que não tem solução terei de mudar todo o meu sistema"

O Relatório em questão é um relatório de pedidos não faturados, se eu entrar num terminal e pedir este relatório tenho de contar no máximo até 10 para que o mesmo apareça na tela, porque é executado numa tabela de produto com menos de 1000 registro. Mas se alguém estiver na tela de produto em outro terminal simplesmente com ela aberta, vendo um produto qualquer ou na tela de notas fiscais incluindo uma nota, aí a contagem passa dos 60.

Resumindo, a menos que eu esteja muito enganado o DBF fica mais lento muito mais lento quando esta aberto em outro ponto da rede, o que no meu entender não faz muito sentido, não era para ser.

DBF compartilhado em REDE

Enviado: 19 Jul 2013 15:43
por pauloa1
Vander.

O dbf realmente fica lento se você abrir o mesmo arquivo em mais de um pc ao mesmo tempo.

Mas você tem opções.

Se quiser melhorar muito, muda para algum banco de dados via rdd, aí o salto é gigante, pode usar comandos sql etc...

Mas se isso está fora de cogitação, tem o letodb, pesquisa no forum, fácil de usar e fica rápido no seu caso.

Tem também uma rdd no xhb.com para dbfcdx "pago" que melhora esse desempenho.

Paulo

DBF compartilhado em REDE

Enviado: 15 Mai 2014 08:56
por lugab
Eolo, bom dia...

Como é q vc faz isso ?
Se qualquer janela fica X minutos inativa, sem uso, o EXE a fecha automaticamente. E vai fechando cada janela nível acima até sair por completo do sistema. Fiz isso porque, quando ia dar alguma manutenção depois do expediente (leia-se 2 da manhã), descobria meia dúzia de estações ligadas à toa...
Eu uso harbour ou xharbour em modo console. Mostra como é pra gente..

Gabriel

DBF compartilhado em REDE

Enviado: 15 Mai 2014 10:57
por Eolo
keysec(27,60,,.T.)

DO WHILE .T.
oTBrowse:forceStable()
nKey := Inkey(0)

IF nKey==27
EXIT
else
* usuário teclou alguma outra coisa
ENDIF
ENDDO
Só adicionei, ao exemplo do Tbrowse que está no help do HB, a função KEYSEC() com o tempo desejado.

Passado esse tempo e não havendo intervenção do usuário, KEYSEC() gera um chr(27) e o browse é terminado.

Se o usuário teclar alguma coisa dentro desse tempo, o KEYSEC() é terminado e aí vc segue conforme ele pediu.

DBF compartilhado em REDE

Enviado: 15 Mai 2014 11:06
por Eolo
Aliás, ao invés de INKEY(0) eu faço INKEY(X): passado esse X tempo, volto ao topo do loop e a tela é reconstruída.

Faço isso porque meus sistemas começam sempre pelo Tbrowse, é nele que tudo começa (edição, inclusão, exclusão etc).
Então, se o DBF está sendo atualizado na rede, é desejável que a tela seja atualizada periodicamente sem precisar de intervenção do usuário.

DBF compartilhado em REDE

Enviado: 15 Mai 2014 13:39
por lugab
Simples e funcional, Eolo, bacana..

Será q existe uma função de "Contagem de Tempo com Encerramento Automático" mais abrangente, a ser carregada no Function Main() , q atue para todo o sistema ( nos menus, p.ex). em vez de algo mais específico, como essa sugestão do Eolo paraTbrouse() ?

DBF compartilhado em REDE

Enviado: 15 Mai 2014 16:23
por Eolo
q atue para todo o sistema ( nos menus, p.ex)
Lugab, a KEYSEC() não funciona SÓ no Tbrowse. Pode usar em outra situações, sempre que vc precisar jogar alguma tecla no buffer, considerando um tempo.
Ela é como a KEYBOARD, só que aceita o parâmetro tempo.

Eu uso no ACHOICE, também. Se o usuário entra na sistema e, na tela inicial, não se manifesta, depois de X tempo o ACHOICE é encerrado e o programa sai do ar...

DBF compartilhado em REDE

Enviado: 15 Mai 2014 18:01
por alxsts
Olá!

Obrigado por compartilhar informações sobre a KeySec(). Sei que é antiga mas nunca utilizei.

Poderia informar em qual lib está esta função?

DBF compartilhado em REDE

Enviado: 15 Mai 2014 19:38
por Eolo
Alexandre,
Usei o XH 100 e 121, console, e não tive que apontar nenhuma LIB adicional.
Essa função deve fazer parte da LIB padrão.

Aqui vai um exemplo rápido, de como eu aninho vários Keysec(), um dentro do outro. Se o usuário for dando ENTER até o nível mais baixo e aí resolver ir embora pra casa deixando o sistema aberto, cada função vai sendo terminada após o tempo de cada Keysecc, até sair por completo... Como mencionei antes, cada FUNCTION pode conter Tbrowse, Wait, Achoice, o que for.

Montei o exemplo meio na pressa, se não me engano tem que limpar o teclado após cada saída. A conferir.

Código: Selecionar todos

function main
local lk
do whil .t.
  keysec(27,60,,.T.)
  inkey(0)
  lk=lastkey()
  if lk=13
    sub001()
  elseif lk=27 
    retu
  else
    loop
  endi
endd

funcion sub001
local lk
do whil .t.
  keysec(27,60,,.T.)
  inkey(0)
  lk=lastkey()
  if lk=13
    sub002()
  elseif lk=27
    retu
  else
    loop
  endi
endd

funcion sub002
local lk
do whil .t.
  keysec(27,60,,.T.)
  inkey(0)
  lk=lastkey()
  if lk=13
    sub003() // etc etc etc
  elseif lk=27
    retu
  else
    loop
  endi
endd