Transações usando MIDAS

02/01/2006

Olá,
estou tentando migrar um sistema convencional para usar o MIDAS (três camadas), porem o controle de transações se torna um pouco confusa.
Não é viável ficar dando edit, insert, delete, post, cancel com o ClientDataSet. Muitas vezes existem múltiplos comandos SQL que precisam ser executados dentro de uma transação controlada pelo programador.
Eu utilizo conexão DCOM, e já tentei fazer chamadas remotas a procedimentos para fazer um StartTransaction, um Commit ou um rollback. Funciona muito bem, porém com um detalhe meio que fatal. Quando, em máquinas diferentes, eu inicio a transação e insiro registros com chaves iguais as duas máquinas travam até dar um limite de ociosidade em uma máquina liberando a outra. ex.:

faço um ´for´ de 1 atá 500...
dcomConexao.AppServer.StartTransaction;
try
for ik := 1 to 500 do
begin
cds.Close;
cds.CommandText := ´insert into Teste (Cod) values(:Cod)´;
cds.Params.ParamValues[´Cod´] := ik;
cds.Execute;
end;
dcomConexao.AppServer.Commit;
except
dcomConexao.AppServer.RollBack;
end;
executo esse código na máquina 1 e logo depois na máquina 2
a máquina 1 executa normalmente até a máquina 2 tentar inserir um registro que já existe travando as duas. Depois de muito tempo, a máquina 2 dá um erro de timeout liberando a máquina 1.

Isso só acontece quando é na mema tabela e com o mesmo registro que já foi inserido na transação atual. Se as duas máquinas derem erros inserindo registros diferentes, cada máquina exibirá, no mesmo instante, o erro de key violation, podendo fazer um rollback normalmente atravez das chamadas remotas.

Na camado do servidor eu uso TDataBase TQuery TClientDataSet e TSession
Se alguém puder me ajudar, ficaria muuuuito grato.
Miudo


Miudo

Respostas

02/01/2006

Thomaz_prg

Eu uso normalmente as chamadas de Insert, Edit, delete... e funciona muito bem.. Mas quanto ao seu caso, acho que você deveria fazer sua função retornar um valor, por exemplo, ao executar, crie um método no DataSetProvider (AfterExecute) que faça a verificação do código, e então permita ou não a inserção dos dados. Trabalho usando Insert, Edit, Post, Delete e depois dou um ApplyUpdates, que me retorna os erros que tiveram nas alterações, e se houver alguma falha, simplesmente cancelo os dados (CancelUpdates).


Responder Citar

03/01/2006

Miudo

Muito obrigado pela ajuda thomaz...
mas assim...

isso de verificar se pode inserir ou não, num sistema mais complexo, vai baixar muito o desempenho, podendo deixar até impraticável.

quanto aos edits, posts etc... toda vez que vc quizer fazer uma inserção casual, vc terá que antes baixar as informações do servidor e trabalhar em cima delas. Isso eu não entendo, todo mundo aceita essa ideia como resolvida. Na verdade isso não atende a qualquer sistema.

Eu começei pesquisando o MIDAS achando que seria uma coisa bem transparente e que poderia facilmente converter um sistema para usar essa tecnologia, mas estou achando muitas dificuldades (pode ser falta de maturidade).

Isso que eu estou usando de chamadas remotas também tem detalhes.
Se uma máquina fizer uma chamada remota de StartTransaction e logo depois essa máquina cair, uma transação ficará aberta indefinidamente.

Outra dificuldade que tenho é quanto ao prepare. Tem formulários que uso com mais de 10 querys ´preperadas´. Se eu for converter esse formulário para usar o MIDAS... eu nem sei como vai ser

muito grato pela atenção...
espero que ainda possa me ajudar se eu estiver num engano :)


Responder Citar

04/01/2006

Miudo

Eu consegui uma solução transparente um pouco dificil de explicar...
se alguem quizer que eu explique...


Responder Citar

05/01/2006

Thomaz_prg

isso de verificar se pode inserir ou não, num sistema mais complexo, vai baixar muito o desempenho, podendo deixar até impraticável.


Não necessariamente, pois uma das regras da implementação em Multicamadas (3 ou mais) e a independência de banco, sendo dessa forma, a verificação seria feita na aplicação. Tenho aplicações que rodam na web dessa forma, e funcionam muito bem (com mais de 10.000 lançamentos diários, sendo acessados por mais ou menos 200 pessoas).Claro que não se compara com o controle no banco, mas tenho mais controle e mantenho menos transações ativas com o banco.

quanto aos edits, posts etc... toda vez que vc quizer fazer uma inserção casual, vc terá que antes baixar as informações do servidor e trabalhar em cima delas. Isso eu não entendo, todo mundo aceita essa ideia como resolvida. Na verdade isso não atende a qualquer sistema.

Creio que você está equivocado. Trabalho trazendo o mínimo de registros por vez, e isso, apenas em pesquisas, pois na abertuira de cadastros simples (para dar um insert, oiu pesquisar) não carrego nenhum registro. Além de usar select´s do tipo: SELECT * FROM TABELA WHERE ID = :ID, onde ID é minha chave primária, me obrigando a trazer apenas um registro por vez, ainda faço uso da propriedade PacketRecords, disponível no clientdataset. Até agora não encontrei nada onde não possa usar.

Isso que eu estou usando de chamadas remotas também tem detalhes. Se uma máquina fizer uma chamada remota de StartTransaction e logo depois essa máquina cair, uma transação ficará aberta indefinidamente.

tente criar uma instância para cada cliente (isso apenas para os datasets, para a conexão use uma conexão compartilhada). Isso fará com que, se o cliente cair, os objetos e, por consequencia, as conexões serão desfeitas.

Outra dificuldade que tenho é quanto ao prepare. Tem formulários que uso com mais de 10 querys ´preperadas´. Se eu for converter esse formulário para usar o MIDAS... eu nem sei como vai ser

Se o problema for apenas para se digitar o SQL e usar parâmetros, não existe problema. Ou motivo seria o fato de você também poder deixar o SQL já preparado, no componente de Dataset (SQLDataset, SQLQuery, etc).

Eu consegui uma solução transparente um pouco dificil de explicar... se alguem quizer que eu explique...

Seria ótimo... conhecimento, quanto mais melhor.


Responder Citar

05/01/2006

Thomaz_prg

isso de verificar se pode inserir ou não, num sistema mais complexo, vai baixar muito o desempenho, podendo deixar até impraticável.


Não necessariamente, pois uma das regras da implementação em Multicamadas (3 ou mais) e a independência de banco, sendo dessa forma, a verificação seria feita na aplicação. Tenho aplicações que rodam na web dessa forma, e funcionam muito bem (com mais de 10.000 lançamentos diários, sendo acessados por mais ou menos 200 pessoas).Claro que não se compara com o controle no banco, mas tenho mais controle e mantenho menos transações ativas com o banco.

quanto aos edits, posts etc... toda vez que vc quizer fazer uma inserção casual, vc terá que antes baixar as informações do servidor e trabalhar em cima delas. Isso eu não entendo, todo mundo aceita essa ideia como resolvida. Na verdade isso não atende a qualquer sistema.

Creio que você está equivocado. Trabalho trazendo o mínimo de registros por vez, e isso, apenas em pesquisas, pois na abertuira de cadastros simples (para dar um insert, oiu pesquisar) não carrego nenhum registro. Além de usar select´s do tipo: SELECT * FROM TABELA WHERE ID = :ID, onde ID é minha chave primária, me obrigando a trazer apenas um registro por vez, ainda faço uso da propriedade PacketRecords, disponível no clientdataset. Até agora não encontrei nada onde não possa usar.

Isso que eu estou usando de chamadas remotas também tem detalhes. Se uma máquina fizer uma chamada remota de StartTransaction e logo depois essa máquina cair, uma transação ficará aberta indefinidamente.

tente criar uma instância para cada cliente (isso apenas para os datasets, para a conexão use uma conexão compartilhada). Isso fará com que, se o cliente cair, os objetos e, por consequencia, as conexões serão desfeitas.

Outra dificuldade que tenho é quanto ao prepare. Tem formulários que uso com mais de 10 querys ´preperadas´. Se eu for converter esse formulário para usar o MIDAS... eu nem sei como vai ser

Se o problema for apenas para se digitar o SQL e usar parâmetros, não existe problema. Ou motivo seria o fato de você também poder deixar o SQL já preparado, no componente de Dataset (SQLDataset, SQLQuery, etc).

Eu consegui uma solução transparente um pouco dificil de explicar... se alguem quizer que eu explique...

Seria ótimo... conhecimento, quanto mais melhor.


Responder Citar

05/01/2006

Thomaz_prg

isso de verificar se pode inserir ou não, num sistema mais complexo, vai baixar muito o desempenho, podendo deixar até impraticável.


Não necessariamente, pois uma das regras da implementação em Multicamadas (3 ou mais) e a independência de banco, sendo dessa forma, a verificação seria feita na aplicação. Tenho aplicações que rodam na web dessa forma, e funcionam muito bem (com mais de 10.000 lançamentos diários, sendo acessados por mais ou menos 200 pessoas).Claro que não se compara com o controle no banco, mas tenho mais controle e mantenho menos transações ativas com o banco.

quanto aos edits, posts etc... toda vez que vc quizer fazer uma inserção casual, vc terá que antes baixar as informações do servidor e trabalhar em cima delas. Isso eu não entendo, todo mundo aceita essa ideia como resolvida. Na verdade isso não atende a qualquer sistema.

Creio que você está equivocado. Trabalho trazendo o mínimo de registros por vez, e isso, apenas em pesquisas, pois na abertuira de cadastros simples (para dar um insert, oiu pesquisar) não carrego nenhum registro. Além de usar select´s do tipo: SELECT * FROM TABELA WHERE ID = :ID, onde ID é minha chave primária, me obrigando a trazer apenas um registro por vez, ainda faço uso da propriedade PacketRecords, disponível no clientdataset. Até agora não encontrei nada onde não possa usar.

Isso que eu estou usando de chamadas remotas também tem detalhes. Se uma máquina fizer uma chamada remota de StartTransaction e logo depois essa máquina cair, uma transação ficará aberta indefinidamente.

tente criar uma instância para cada cliente (isso apenas para os datasets, para a conexão use uma conexão compartilhada). Isso fará com que, se o cliente cair, os objetos e, por consequencia, as conexões serão desfeitas.

Outra dificuldade que tenho é quanto ao prepare. Tem formulários que uso com mais de 10 querys ´preperadas´. Se eu for converter esse formulário para usar o MIDAS... eu nem sei como vai ser

Se o problema for apenas para se digitar o SQL e usar parâmetros, não existe problema. Ou motivo seria o fato de você também poder deixar o SQL já preparado, no componente de Dataset (SQLDataset, SQLQuery, etc).

Eu consegui uma solução transparente um pouco dificil de explicar... se alguem quizer que eu explique...

Seria ótimo... conhecimento, quanto mais melhor.


Responder Citar

05/01/2006

Miudo

Muito obrigado pela ajuda thomaz... me esclareceu algumas dúvidas.

Quando vc falou que a verificação da chave fica na parte da aplicação, muito bom. Mas quando eu falava que ficaria impraticavel é porque tem vezes que um cadastro leva a fazer muitos inserts de uma só vez (em torno de 600). Acredito que tratando pela aplicação não seria assim tão impraticável como eu disse. Eu vou testar isso direito, tenho a impressão que ficaria um pouco lento.

Quanto aos prepares, é realmente deixar as querys em prepare, não é deixar os parametros. Mas isso poderia se resolver tirando o uso do BDE.


Responder Citar

05/01/2006

Miudo

Minha solução foi a seguinte:
Coloquei algumas chamadas remotas no servidor que é Logar e Deslogar, passando uma chave como parametro. Essa chave simboliza um cliente.
Quando o cliente conectar, ele gera uma chave (pode ser até seu ip)
e executa Logar. No servidor o logar criará um TDataBase uma TQuery e um TDataSetProvider com os nomes ´db´ + CHAVE, ´qe´ + CHAVE e
´dsp´ + CHAVE
Ou seja, cada cliente terá sua própia conexão. Eu imaginei que usando o TSession e colocando a propriedade AutoSessionName pra true, ele faria isso automaticamente. Mas estava dando aquele primeiro problema na inserção de chaves iguais.

Existem agora outras chamadas remotas que são StarTransaction Commit e RollBack. Todas passando a chave como parametro.
Quando um cliente chama ´StartTransaction(chave);´ o servidor vai dar um FindComponent(´db´ + CHAVE) e vai executar um StartTransaction nessa conexão, igual para as outras chamadas.

Quando chamar Deslogar(Chave) o servidor apagará esses componentes.

Criei um Componente TMDataBase no cliente que gera a chave e deixa tudo transparente pro programador. Como se fosse um TDataBase
Depois criei outro componente que eh TMQuery herdando de TClienteDataSet para deixar ainda mais parecido com o normal por fim o poderia usar o programador poderia usar o seguinte código

database: TMDataBase;
qe: TMQuery;

database.StartTransaction;

try
qe.SQL.Clear;
qe.SQL.Add(´insert into Teste (Cod) values(:Cod)´);
for ik := 1 to 1000 do
begin
qe.Close;
qe.ParamByName(´Cod´).AsInteger := ik;
qe.ExecSQL;
end;
database.Commit;
ShowMessage(´Commit´);
except
ON e: Exception do
begin
database.RollBack;
ShowMessage(e.Message);
end;
end;

Nesse lugar alcancei a transparencia que eu queria. Mas tem problemas.
Se um cliente abrir uma transação e faltar energia, essa transação ficaria aberta infinitamente. Estou tentando resolver isso :)
Se alguem puder ajudar, e criticar...

obrigado pela atenção


Responder Citar

05/01/2006

Thomaz_prg

Olá colega... é como eu disse:

tente criar uma instância para cada cliente (isso apenas para os datasets, para a conexão use uma conexão compartilhada). Isso fará com que, se o cliente cair, os objetos e, por consequencia, as conexões serão desfeitas.


Tente usar mais de um RemoteDataModule (ou TransactionDataModule), sendo 1 (o principal) apenas para a conexão com o banco, e o outro (ou outros) contendo os datasets e transações, dessa forma, se o cliente cair, com uma transação ativa, a mesma será destruída.


Responder Citar

23/01/2006

Miudo

olá thomaz...
tive que dar um tempo no MIDAS mas estou voltando...

interessante o que você falou de vários datamodules. resolve alguns problemas.
mas por uns testes que fiz, se o cliente cair, o datamudole não percebe.
no caso de falta de energia, não consigo ver como tratar o problema do risco da transação ficar aberta.


Responder Citar

23/01/2006

Thomaz_prg

Estranho, pois o MIDAS trabalha da seguinte forma, abre a transação pega os dados, e fecha a transação. Ao alterar (Applyupdates), Abre a transação, aplica as alterações e fecha a transação, ou seja, não as mantém abertas.
Para o caso de você estar usando IBX, sugiro que suas funções de Abertura de transação e finalização (Commit E/Ou Rollback) sejam ativadas automaticamente. Nos eventos BeforeApplyUpdates (Inicia a transação) e AfterApplyUpdates( Finaliza a transação). Dessa forma, as transações estarão abertas por um curto prazo de tempo, não causando problemas.


Responder Citar

24/01/2006

Miudo

no caso a transação é controlada pelo cliente...


Responder Citar

24/01/2006

Thomaz_prg

Olá amigo...
Você criou um RDM (SingleInstance) para os Dataset´s como eu havia dito?

Se sim, acho estranho que não esteja funcionando a contento, mas mesmo assim, dá pra se implementar um tipo de validação usando Sockets.
Sinto não poder lhe ajudar muito, pois você está usando BDE, e eu, nunca coloquei o BDE com Multi-Camadas.
Outra observação interessante, é o fato de você ter que colocar um TSession no RDM dos dataset´s. [/quote]


Responder Citar

25/01/2006

Miudo

oi thomaz...
eu fiz o que vc falou. criei um TRemoteDataModule para cada dataset, simbolizando um cilente.

Isso funciona só se o cliente cair normalmente...

o TSession, como já falei, não funciona bem.

mas muito obrigado pela atenção.
qualquer ajuda...


Responder Citar

25/01/2006

Thomaz_prg

Amigo, creio não ter me expressado direito...

A estrutura do servidor ficaria assim:

+------------------+
| RDMCONEXAO  |
+------------------+
         |
         +-----+-----------------------+
                  |   RDMDATASETS     |
                  +-----------------------+


Ou seja, teríamos apenas 2 RDM´s para todo o servidor. Sendo que 1 conteria apenas a conexão com o Banco (No daso o TDatabase), e o Outro conteria TODOS os seus datasets. Para cada rdm coloque um TSession. Na conexão cliente, provavelmente você terá que usar um SharedConnection (palheta DataSnap). Você deve estar usando DComConnection, mas, usando essa estrutura, você terá que substituir por um SharedConnection. Se quiser mando um pequeno exemplo pra você de como ficaria o servidor.
Mas faça o seguinte, ao invés de tentar refazer a estrutura, se você estiver usando DComConnection, troque por SocketConnection. Para que ele funcione corretamente, você deve estar com a aplicação SocketServer ativa no servidor. Então ao derrubar um cliente, verifique no SocketServer se o mesmo caiu ou ainda está pendente. Você só precisa trocar a conexão, mais nada.


Responder Citar