Uma luz sobre lock pessimista no hibernate pelo amor de deus

21 respostas
M

Galera, eu ja to doido aqui de tanto procurar, entao pesso que qualquer informação que possam me dar ja ajuda :slight_smile:

Bom, eu gostaria de saber basicamente como que implementaria o lock pessimista no hibernate pra web. O meu grande problema é que quando loco um registro em uma transacao, ele so sera liberado quando eu comitar essa transacao. Porem pra web é dificil guardar uma transacao para ser comitada em outra requisicao. O maior problema mesmo é que se essa transacao nao for comitada o registro continua locado ate que o java libere a instancia dela. Em fim, como eu poderia implementar o lock pessimista na web? Por favor, alguem da uma luz.

Eu implementei o lock pessimista, funciona bem. O usuario nao vai conseguir atualizar se alguem ja atualizou na frente. Mais ai surge o problema de que o coitado do usuario que nao vai poder atualizar poderia ter sido o primeiro a pegar o registro, alterou tudo em sua tela mais nao vai conseguir comitar so pq alguem foi mais rapido que ele. O que eu gostaria eh que quando um usuario marco um registro para ser alterado, ninguem consiga altera-lo ate q o registro seja liberado. Isso em desktop eh facil, agora pra web ta dificil achar uma boa solução. Vlw ae galera, to na aguarda.

21 Respostas

T

http://www.hibernate.org/hib_docs/reference/en/html/transactions.html

Leia:
11.1.2. Long conversations

M

thingol, eu ja li que so essa parte mais nao cheguei ao que eu queria, veja so

A primeira opcao é o lock otimista, que seria versionando, nao é interessante para mim, pois ele so verifica a validade quando se tenta atualizar.

Os outros dois eu nao entendi, se vc puder me explicar eu agradeço.

T

Que tipo de aplicação é a sua?
Digamos que o vendedor de uma loja de materiais de construção queira vender 100 metros quadrados de azulejos, e o estoque tenha 150 metros quadrados; além disso, ele possa ir entrando com outros itens (cimento, areia. etc.)
Se ele simplesmente bloquear a atualização do estoque de azulejos, ele não deixará os outros vendedores venderem azulejos.
Em vez disso, ao entrar com o item “azulejos”, na verdade ele deixa já reservado no estoque 100 metros quadrados (ou seja, enquanto ele está entrando com os outros itens os outros vendedores só podem contar com 50 metros quadrados no estoque), e só quando ele acabar de entrar com os outros itens, e receber a confirmação da financeira é que a transação será efetivamente concluída (removendo 100 m quadrados do estoque).
Veja se a sua transação não é algo parecido com isso (ou seja, você tem de levar isso explicitamente na aplicação).

Outra forma que foi apontada é essa história de versionamento. Acho que você já ouviu falar - se a versão da transação mudou de uma tela para outra, você aborta a transação, porque a base foi alterada por outra transação.

M

Certo entendi. Mais o meu caso seria assim:

Eu tenho digamos uma tabela com informacoes de uma cidade. Um usuario vai e seleciona uma cidade para poder editar. Ai digamos que outro usuario tambem faz isso em seguida. Se eu trabalhar com versionamento, apenas o primeiro usuario que salvar as alteracoes poderam de fato alterar. Quando o segundo usuario tentar, vai da erro de versao, pois a versao foi atualizada. O problema é que o cara pode ter pasado 1 hora para digitar os dados e no final nao vai poder atualizar pq alguem foi mais rapido que ele.

O que gostaria é que quando alguem selecionase o registro para edicao, nenhum outro usuario pode-se fazer o mesmo ate q o registro fosse salvo, comitado ou cancelado a edicao entende?? Ai que entra o lock pesimista, ele faz isso, o problema é que como eu persisto uma transacao na web? Pq ai eu teria q abrir uma transacao, locar o registro e aguardar ate o usuario dar um comit nele ou um rool back ou fechar a transacao, mais ai eu teria q persistir essa transacao, como fazer isso?? :confused:

Mauricio_Linhares

Você não acha que travar a linha da tabela é uma péssima idéia não?

O ideal seria utilizar o lock otimista e em vez de voce jogar a exceção pro usuário, mostrar a ele que a informação foi atualizada e mostrar as diferenças pra ele.

M

Sim, é uma opcao tambem. Porem em certos casos no meu contexto seria interessante travar a linha.

Um exemplo:

Digamos que meu sistema seja de uma compania de telefone. Eu tenho meu telefone residencial la. Ai digamos que eu ligue para a central para atualizar a data de vencimento da fatura. E digamos que no mesmo instante minha esposa ligue tambem. Nesse caso o primeiro que salvar realizara a alteração. O segundo ficara conversando no telefone um tempao pra no final receber a mensagem q alguem ja alterou.

O ideal seria que o primeiro q entrar travar o registro, e entao se alguem tentar travar naquele momento receba logo a mensagem que alguem ta tentando alterar o registro, seria mais viavel entende?

M

O que eu gostaria é teoricamente simples. Seria locar um registro. Posteriomente quando for comitar, saber atravez do numero da versao que fui eu quem locou o registro, e entao liberar o update. A questao é que o banco so sabe quem locou o registro pela sessao da transacao, se a transacao nao for a mesma, vc nunca poderá comitar o registro. Ai eu me pergunto, teria que guardar o objeto Transaction na sessao do usuario HTTPSession? :confused: O Hibernate nao teria como recuperar uma transacao por um dado locado pela versao, sei la, algo do tipo?

Mauricio_Linhares

E se o cara abrir a aplicação, carregar os dados (travando a linha) e for tomar um cafezinho?

Isso acontece bem mais do que você imagina :slight_smile:

De qualquer forma, se você quer fazer isso usando o Hibernate, é do jeito que o Thingol já falou, mas você também pode fazer no braço com uma coluna do tipo “sendo_atualizado”. Só não se esqueça de um pequeno detalhe, se você estiver usando uma coluna pra fazer o controle e a conexão do cara cair ou acontecer algum problema na aplicação, essa linha nunca mais vai ser liberada e se você estiver usando um banco de dados como SQL Server ou MySQL que começam a travar páginas de dados quando os locks aumentam muito você pode travar outras linhas que não tem nadas haver com a história.

Eu ainda não vi casos onde locks pessimistas são uma boa opção, tenha cuidado com o que você vai fazer.

M

Mauricio, eu estou ciente desses problemas, e ja encontrei solucao para todos.

Tipo, o hibernate tem um setTimeout para cada transacao, o que evita do usuario ir tomar um cafezinho ehehhe, se ele nao terminar o serviço dele em tantos minutos, ele perde a transacao.

A questao do registro ficar locado pra sempre, isso se resolveria de duas formas: quando o usario logar, eu posso verificar na sessao do oracle se existe algum lock pendente para ele, e entao kila-lo. Junto com essa opcao, seria com o timeout da transacao, entao nao correria tantos riscos. A ideia do “esta atulizando” seria ruim pq ficaria dificil controlar quando o usuario realmente ta atualizando. Se por algum problema ficar sempre TA ATUALIZANDO lasca tudo. O ideial mesmo seria locar e poder recuperar a transacao.

Mauricio_Linhares

Se você não quer usar locks otimistas, entao coloque a sessão aberta na sessão http do usuário, fazendo um lock nessa linha, não tem outro jeito.

M

É, to vendo que vai ser a unica solucao. Gerencias as transacoes na sessao de usuario. O lock otimista sera usado, porem pra alguns casos o pesimista tbm é necessario.

danieldestro

No JPA tem o “version”. No Hibernate não tem isso também? Não ajuda?
Esse é o típico caso complexo que muitos enfrentam e a solução vai depender muito do que o usuário pode ou não aceitar no uso do aplicativo.

M

o version versiona o dado, de forma q nao pode ser atualizado com versoes diferente, mais nao tem muito haver com o lock pessimista nao. Resolve alguns casos, outros nao

sergiotaborda

mizael86:
Certo entendi. Mais o meu caso seria assim:

Eu tenho digamos uma tabela com informacoes de uma cidade. Um usuario vai e seleciona uma cidade para poder editar. Ai digamos que outro usuario tambem faz isso em seguida. Se eu trabalhar com versionamento, apenas o primeiro usuario que salvar as alteracoes poderam de fato alterar. Quando o segundo usuario tentar, vai da erro de versao, pois a versao foi atualizada. O problema é que o cara pode ter pasado 1 hora para digitar os dados e no final nao vai poder atualizar pq alguem foi mais rapido que ele.

O que gostaria é que quando alguem selecionase o registro para edicao, nenhum outro usuario pode-se fazer o mesmo ate q o registro fosse salvo, comitado ou cancelado a edicao entende??

O seu problema não é usar o hibernate é usar lock pessimista. Isso é péssimo.
O problema é que se acontecer algum problema e o lock não for limpo o registro não poderá ser editado nunca mais.

O seu pensamento deve ser proabilitistico. É altamente inprovável que duas pessoas editem o mesmo registro ao mesmo tempo. Se o cara demorou uma hora o problema é dele. Ele pode repetir a operação. Coisa que não pode ser o lock ficar pendurado.

M

sergio, eu entendo o q vc quer dizer, e concordo em certos pontos. Porem a aplicação que estou trabalhando envolve muitoooooos mil usuarios acessando simultaneamente, o que a probabilidade de 2 acessar o mesmo registro almenta muito. Quando ao registro ficar locado, estamos fazendo controle de mensagens caso o registro estaja locado, de forma que sempre saibamos quem locou. Por exemplo.

Um registro está locado, ai outro tenta locar, ele vai receber uma mensagem de quem locou, tempo de inatividade, etc. Se ver que o tempo de inatividade é grande, ele pode entrar em contato com uqem locou e verificar, ou simplesmente deslocar explicitamente(claro que pra isso ele vai ter q ter esse privilegio, ou entao fazer uma regra pra um X tempo de inatividade.) Em fim, tem varias formas de solucionar isso. O que seria mais ruim é 2 pessoas trabalhando no mesmo registro e acabar perdendo o que fez :). Ou entao ficar fazendo sem necessidade

O

Já que você quer fazer o lock no momento que o usuário fizer a edição (digitação das informações), vc não poderia fazer uma validação na própria aplicação ?

sergiotaborda

mizael86:
sergio, eu entendo o q vc quer dizer, e concordo em certos pontos. Porem a aplicação que estou trabalhando envolve muitoooooos mil usuarios acessando simultaneamente, o que a probabilidade de 2 acessar o mesmo registro almenta muito. Quando ao registro ficar locado, estamos fazendo controle de mensagens caso o registro estaja locado, de forma que sempre saibamos quem locou. Por exemplo.

Um registro está locado, ai outro tenta locar, ele vai receber uma mensagem de quem locou, tempo de inatividade, etc. Se ver que o tempo de inatividade é grande, ele pode entrar em contato com uqem locou e verificar, ou simplesmente deslocar explicitamente(claro que pra isso ele vai ter q ter esse privilegio, ou entao fazer uma regra pra um X tempo de inatividade.) Em fim, tem varias formas de solucionar isso. O que seria mais ruim é 2 pessoas trabalhando no mesmo registro e acabar perdendo o que fez :). Ou entao ficar fazendo sem necessidade

Eu entendo tudo isso. O ponto é que essa forma que fala é burocrática, é assistémica ( o usuário comunica com o outro por fora do sistema) e não funciona.

Se existe alguma razão muito forte para que duas pessoas editem o mesmo cadastro/processo redistribua-os. Por exemplo, pedido só pode ser editado porque o criou ou pelo gerente dessa pessoa. Coisas como esta limitam o numero de pessoas possiveis de concorrer e portanto tornam o lock otimista suficiente. Além de que dão ao negócio uma clareza e simplicidade maior.

Imagine que o usuário lock pessimistiamente um registro. E que a sua ligação cai. Ele nunca ai liberar o lock.
Vc vê isso e liga para o cara “fulano, sai desse registro!” e ele vai dizer “eu não estou lá”. O cara entra no sistema de novo. tenta acessar o registro: não pode porque está lock. O registro fica lock-out.
Vc pode incluir mecanismo que liberar o lock automáticamente ou forçadamente. Os primeiros podem ter erros que liberam o registro mesmo quanto o cara ainda está usando (proque ele demorou demais a dar o save) e os segundos levam o usuário master a forçar a saida sistemática de todo o mundo. Lock pessimista é ruim de implementar, é ruim de manter, leva a processos burucráticos , limita a disponibilidade do sistema… e muitas outras coisas. Em suma, não é bom.

Se mesmo assim vc quer usar. Esteja à vontade, mas não espere que nenhuma ferramenta ou framework dê suporte a isso. ninguem quer essa responsabilidade.

M

Como assim validacao na propria aplicação??

M

Sergio,

Entendo, entao acho que vc me respondeu tudo, nenhum framework vai da suporte a isso ehhehe. Mais olha so, as ideias que vc deu sao boas, porem mesmo limitando acesso, ainda é grande o numero de usuarios possiveis. A nossa aplicação eh de mais de mil acessos simultaneos. Atualmente ela ja existe, porem eh feita em Oracle Forms. E realmente la esse problema de lock-out acontece muito, mais eh um mal necessario. Eu desenvolvi uma ideia aqui interessante para evitar isso.

Primeiro eu tenho um array de objetos HttpSession no escopo de aplicacao para guardar todos session dos usuarios. Cada usuario tem um array de Sessions do hibernate, que ficara cada transacao que tiver com lock pessimista. Dessa forma quando o usuario locka um registro eu guardo essa transacao na sessao tanto do usuario quanto na sessao do oracle. Dessa forma se ocorrer de uma sessao http tiver inexistente e um lock estiver ativo, quando um outro usuario tentar locar esse registro a aplicacao mata a sessao direta do oracle, liberando o registro. Quando o usuario se loga, tbm ja faz uma verificacao de possiveis locks dele q foi deixado para traz, eliminandos. Bom, de tudo q eu criei so tem uma falha que nao tem como ser solucionada.

Se o usuario fecha a aplicacao sem liberar o registro, o registro vai continuar locado, pq a sessao dele ainda vai existir no servidor. O usuario q tentar locar vai ver quem foi q locou. Esse registro so sera liberado quando a sessao do usuario espirar, ou quando esse usuario logar novamente, que ai ja libera automaticamente. Pq nao tem como eu realmente saber quando o usuario ta online ou nao. Mais isso eh um problema pequeno. Vendo q cada sessao vai expirar por 10 minutos de inatividade. Entao um usuario com lock perdido vai ter q esperar no maximo isso. :slight_smile:

O

Você pode marcar em um application (servlet context) attribute, se uma determinada tela está em edição ou não.

application.setAttribute("edicao.tela", true");

Depois lê esse atributo.

A única coisa treta é se o usuário sair da tela sem cancelar ou efetivar a transação.
Vc deve bolar um mecanismo para evitar isso, sei lá usando um session listener.
Quando o session do usuário expirar ele liberar da edição qualquer tela que esjeja em edição para ele.

É para se pensar, é minha sugestão.

maiconramones

Bueno, mizael86.

Pensando em responsabilidade, ao meu ver isso que você está tentando fazer não é de responsabilidade do banco… mas sim da sua aplicação. Então o que eu vejo, criar um HashMap único na sua aplicação aonde você setaria qual o registro da sua aplicação está sendo editado e cada vez que alguém for editar algum registro você confere o HashMap, após o commit você retira o registro do HashMap.

Claro que para este caso teria de se avaliar quantos registros teria essa HashMap em memória e também em questões de perfomance e acesso concorrente.

Abraço

Criado 17 de abril de 2008
Ultima resposta 23 de abr. de 2008
Respostas 21
Participantes 7