Transaes e concorrncia Jobson Ronan jrjscin ufpe br
Transações e concorrência Jobson Ronan {jrjs@cin. ufpe. br}
O que é uma transação? n n Uma transação é uma unidade de trabalho que não pode ser dividida. É uma operação atômica. Há dois níveis de granularidade em aplicações corporativas ¡ ¡ n Transações de banco de dados Transações longas (de aplicação): envolvem várias transações de banco de dados Uma transação ou termina com sucesso (commit) ou desfaz todo o processo (rollback) A maior parte da complexidade de se lidar com transações é ocultada pelo sistema (Hibernate, servidor de aplicações, banco de dados) O trabalho consiste, geralmente, em demarcar o início e fim das transações
Transações em servidores n Demarcar transações em uma aplicação JDBC é fácil. Basta configurar a Conexão conn com da seguinte forma ¡ n conn. set. Auto. Commit(false); Os statements executados serão acumulados e só serão tornados definitivos no banco após um conn. commit() n n Ou serão desfeitos caso ocorra um conn. rollback() Em servidores de aplicação, ou quando é preciso realizar transações entre vários bancos, é preciso usar o protocolo Two-phase commit, que gerencia o processo ¡ Para isto existe a API JTA e a classe User. Transaction que
Tratamento de transações em Hibernate Session session = sessions. open. Session(); Transaction tx = null; try { tx = session. begin. Transaction(); conclude. Auction(); tx. commit(); Executado } catch (Exception e) { if (tx != null) { try { tx. rollback(); } catch (Hibernate. Exception he) { //log he and rethrow e } } throw e; } finally { try { session. close(); } catch (Hibernate. Exception he) { throw he; } } dentro da transação
Transações no Hibernate n n O Hibernate encapsula o sistema de transações do banco (JDBC) ou servidor (ambiente gerenciado) usado A transação começa na Session com uma chamada para session. begin. Transaction() ¡ ¡ n Commit e rollback. Em uma transação Transaction tx: ¡ ¡ n Em um ambiente não gerenciado, isto inicia uma transação JDBC na conexão. Em um ambiente gerenciado, inicia uma transação JTA ou unese à transação existente. tx. commit() sincroniza o estado da sessão com o banco de dados. tx. rollback() ou desfaz imediatamente a transação ou marca a transação para rollback. É importante fechar a sessão em um bloco finally para garantir que a conexão JDBC será liberada e retornada
Flushing (descarregando) n O objeto Session implementa "transparent write behind“ ¡ ¡ n Mudanças ao modelo de domínio não são imediatamente persistidas no banco (para reduzir acesso ao banco) Gravação/sincronização transparente dos dados no final da transação. Flushing é a sincronização da camada de objetos com a camada de dados. Ocorre quando ¡ ¡ Uma transação é cometida Às vezes, antes que uma query é executada
Isolamento n Bancos de dados e sistemas transacionais tentam garantir isolamento entre transações ¡ ¡ n Bancos de dados fornecem vários graus de flexibilização de isolamento ¡ n Isolamento completo é uma utopia. É muito caro em termos de escalabilidade da aplicação. Variam de isolamento completo a isolamento praticamente inexistente (neste caso, cabe à aplicação lidar com os conflitos) Para a maior parte das aplicações, isolamento incompleto de uma transação é aceitável
Problemas de isolamento n n O padrão ANSI SQL define os níveis de isolamento de transações em termos de fenômenos que podem ou não serem permitidos. Os fenômenos são: Update perdido (lost update) ¡ ¡ n Duas transações ambas atualizam um registro e a segunda transação aborta, fazendo com que as duas mudanças sejam perdidas. As transações concorrentes não têm isolamento algum. Leitura suja (dirty read) ¡ Uma transação lê mudanças feitas por transação que ainda não cometeu os dados.
Problemas de isolamento n Leitura não-repetível (unrepeatable read) ¡ ¡ n Uma transação lê um registro duas vezes e obtém um estado diferente em cada leitura. Outra transação pode ter gravado dados e cometido mudanças entre as duas leituras Leitura fantasma (phantom read) ¡ ¡ Uma transação executa uma consulta duas vezes, e o segundo resultado inclui registros que não estavam na primeira consulta. Novos registros foram inseridos por outra transação entre as consultas.
Níveis de isolamento JDBC n n JTA usa esses mesmos níveis de isolamento Read uncommitted ¡ ¡ ¡ n Permite dirty reads mas não updates perdidos. Uma transação não pode gravar em um registro se outra transação não cometida já gravou dados nele. Este nível de isolamento pode ser implementado com locks de gravação exclusiva. Read committed ¡ ¡ ¡ Permite unrepeatable reads mas não dirty reads. Transação de gravação não cometida impede que outras transações acessem registro. Transações de leitura não bloqueiam o sistema.
Níveis de isolamento JDBC n Repeatable read ¡ ¡ ¡ n Não permite unrepeatable reads nem dirty reads. Podem ocorrer phantom reads. Transações de leitura bloqueiam transações de gravação (mas não outras transações de leitura) e transações de gravação bloqueiam todas as outras. Serializable ¡ ¡ Fornece o isolamento mais rigoroso. Emula execução em série de transações (em vez de concorrentemente).
Qual nível de isolamento? n A escolha do nível de isolamento depende do cenário onde a aplicação executa. ¡ n Qual um nível razoável de isolamento para aplicações típicas? ¡ ¡ ¡ n Não existe uma regra que sirva para todas as situações. Isolamento excessivo geralmente não é aceitável, devido ao alto custo quanto à escalabilidade (crítica nas aplicações típicas do Hibernate), portanto o isolamento serializable não deve ser usado O isolamento read uncommitted é perigoso, e não deve ser usado se houver opções melhores no banco Suporte a versioning (travas otimistas) e uso do cache de segundo nível (por classe) do Hibernate já alcançam a maior parte dos benefícios de um isolamento do tipo repeatable read, usando read committed. Portanto, read committed é uma boa opção com o
Como mudar o nível de isolamento default? n É preciso definir uma propriedade no hibernate. properties ou hibernate. cfg. xml. Use: hibernate. connection. isolation=numero onde número é 1, 2, 4 ou 8. Exemplo: hibernate. connection. isolation = 4 n O número refere-se a um dos quatro níveis: ¡ ¡ n 1—Read uncommitted isolation 2—Read committed isolation 4—Repeatable read isolation 8—Serializable isolation Só é possível fazer esse controle em ambientes não gerenciados ¡ Servidores de aplicação têm configuração própria.
Estratégias de isolamento locais n Nível de isolamento global afeta todas as conexões ¡ n n Read committed é um bom isolamento default para aplicações Hibernate Mas pode ser desejável utilizar travas mais rigorosas para transações específicas Existem duas estratégias ¡ ¡ Travas pessimistas (evita colisões entre transações bloqueando totalmente o acesso de outras transações) Travas otimistas (onde o sistema flexibiliza o isolamento mas lida com eventuais colisões)
Travas pessimistas n Uma trava pessimista é adquirida quando dados são lidos e mantidos isolados de outras transações até que a sua transação complete. ¡ n Classe Lock. Mode ¡ n Em modo read-committed, o banco de dados nunca adquire travas pessimistas a não ser que sejam requisitadas explicitamente Permite a solicitação de uma trava pessimista em um objeto Considere a seguinte transação Transaction tx = session. begin. Transaction(); Category cat = (Category) session. get(Category. class, cat. Id); cat. set. Name("New Name"); tx. commit(); n Uma trava pessimista pode ser obtida da seguinte forma: Transaction tx = session. begin. Transaction(); Category cat = (Category) session. get(Category. class, cat. Id, Lock. Mode. UPGRADE); cat. set. Name("New Name"); tx. commit();
Controle de Lock. Mode n Os modos suportados para Lock. Mode são: ¡ ¡ ¡ NONE - Só vai ao banco se o objeto não estiver no cache. Default em load() e get() READ - Ignora cache e faz verificação de versão para assegurar-se que o objeto na memória é o mesmo que está no banco. UPDGRADE - Ignora cache, faz verificação de versão (se aplicável) e obtém trava pessimista (se suportada). UPDGRADE_NOWAIT - Mesmo que UPGRADE, mas desabilita a espera por liberação de travas, e provoca exceção de locking se a trava não puder ser obtida. WRITE - Obtida automaticamente quando Hibernate grava em um registro na transação atual
Controle de Lock. Mode n Sincronização de objeto desligado se registro não foi alterado por outra transação. Item item =. . . ; Bid bid = new Bid(); item. add. Bid(bid); . . . Transaction tx = session. begin. Transaction(); session. lock(item, Lock. Mode. READ); tx. commit(); Caching é considerada uma solução melhor que travas pessimistas. Evite usar Lock. Mode explícito a não ser que realmente seja necessário.
Transações longas (de aplicação) n Processos de negócio ¡ ¡ ¡ n Podem ser consideradas uma única unidade de trabalho do ponto de vista de um usuário. Transação de baixa granularidade. Uma noção mais abrangente da unidade de trabalho. Exemplo de cenário típico 1) 2) 3) Dados são recuperados e mostrados na tela em uma primeira transação do banco O usuário tem uma oportunidade de visualizar e modificar os dados, fora de uma transação As modificações são feitas persistentes em uma segunda transação de banco de dados
Como lidar com as colisões? n Três estratégias ¡ ¡ ¡ n A primeira opção é problemática para várias aplicações ¡ ¡ n Último commit ganha - os dois updates funcionam, mas o segundo sobrescreve as alterações do primeiro. Nenhuma mensagem de erro é mostrada. Primeiro commit ganha - a primeira modificação é feita persistente, e o usuário que envia a segunda recebe uma mensagem de erro. Optimistic locking. Mesclar updates conflitantes - A primeira modificação é persistida, e a segunda pode ser aplicada seletivamente pelo usuário. É importante que o usuário pelo menos saiba do erro Acontece por default. Hibernate ajuda a implementar as outras duas estratégias usando controle de versões e travas otimistas.
Uso de managed versioning (controle de versão) n Depende de que um número seja incrementado sempre que um objeto é modificado. public class Comment {. . . private int version; . . . void set. Version(int version) {this. version = version; } int get. Version() {return version; } } n No arquivo de mapeamento, <version> vem logo depois de <id> <class name="Comment" table="COMMENTS"> <id. . . <version name="version" column="VERSION"/>. . . </class> n O número de versão é só um contador. Não tem outra utilidade.
Timestamp n Alternativa ao <version>. Exemplo: public class Comment {. . . private Date last. Updated; void set. Last. Updated(Date last. Updated) { this. last. Updated = last. Updated; } public Date get. Last. Updated() {return last. Updated; } } n Mapeamento <class name="Comment" table="COMMENTS"> <id. . . /> <timestamp name="last. Updated" column="LAST_UPDATED"/>. . . </class> n Em tese, um timestamp é menos seguro pois duas transações concorrentes poderiam tentar load e update no mesmo milisegundo.
Travas otimistas n O Hibernate controla a inicialização e gerenciamento de <version> e <timestamp> automaticamente. ¡ ¡ n Esses recursos permitem o eficiente gerenciamento de colisões que implementam a estratégia de trava otimista. Stale. Object. State. Exception é lançado em caso de inconsistência Otimistas versus Pessimistas ¡ ¡ Enfoque pessimista assume que serão constantes os conflitos e o ideal é bloquear completamente o acesso. Não ultrapassa os limites de uma sessão Enfoque otimista assume que conflitos serão raros e quando eles acontecerem, é possível lidar com eles. Garante maior escalabilidade e suporta
Granularidade de uma Sessão Session-per-request Uma sessão tem a mesma granularidade de uma transação Session-per-request-withdetached-objects Objetos são modificados entre duas sessões Uma transação por sessão Objetos desligados Session-per-applicationtransaction Sessão longa Objetos mantêm-se persistentes
Cache n O cache é uma cópia local dos dados. ¡ n O cache evita acesso ao banco sempre que ¡ ¡ n Fica entre sua aplicação e o banco de dados. A aplicação faz uma pesquisa por chave primária ou A camada de persistência resolve uma associação usando estratégia lazy Podem ser classificados quanto ao escopo: ¡ ¡ ¡ Escopo de transação - cada unidade de trabalho tem seu próprio cache; vale enquanto a transação está rodando. Escopo de processo - o cache é compartilhado entre transações (há implicações quanto ao isolamento) Escopo de cluster - compartilhado entre processos na mesma máquina ou entre múltiplas máquinas de um cluster.
Cache no Hibernate n Dois níveis ¡ ¡ n Primeiro nível tem escopo de transação. Segundo nível é opcional e tem nível de processo ou cluster. O primeiro nível é a Session. ¡ ¡ ¡ Uma session ou tem a duração de uma transação de banco de dados ou de uma transação de aplicação longa. Não pode ser desligada. Garante identidade do objeto dentro da transação. O segundo nível é cache de estado (valores; não instâncias) É opcional Pode ser configurado por classe ou por associação.
Primeiro e segundo cache n Cache de primeiro nível ¡ ¡ Automático (Session) Usado sempre que se passa um objeto para save(), update(), save. Or. Update() ou quando ele é requisitado com load(), find(), list(), iterate(), ou filter() ¡ n Garante quando uma aplicação requisita o mesmo objeto persistente duas vezes numa sessão, ela recebe de volta a mesma instância. Cache de segundo nível ¡ ¡ Instâncias persistentes são desmontadas (é como serialização, mas o algoritmo é mais rápido). Requer conhecimento sobre os dados para uso eficiente (não é automático – as classes são mapeadas ao cache uma por uma) Se dados são mais freqüentemente atualizados que lidos, não habilite o cache de segundo nível Requer configuração fina em gerente de cache para melhor
Resumo: tags de mapeamento n <version> ¡ n Usado em implementação de transações longas, para sinalizar que uma tabela/objeto está sendo alterada <cache> ¡ Usado para definir política de cache de segundo nível
Propriedades: transação e cache n n n hibernate. cache. provider_class=nome. da. Classe ¡ Usa um cache provider próprio em substituição ao nativo usado pelo Hibernate (implementação de org. hibernate. cache. Cache. Provider) hibernate. transaction. factory_class=nome. da. Classe ¡ Para definir um gerente de transações próprio, ou org. hibernate. transaction. <nome> para usar uma implementação disponível, onde <nome> pode ser ¡ ¡ ¡ ¡ JBoss. Transaction. Manager. Lookup Weblogic. Transaction. Manager. Lookup Web. Sphere. Transaction. Manager. Lookup Orion. Transaction. Manager. Lookup Resin. Transaction. Manager. Lookup JOTMTransaction. Manager. Lookup JOn. ASTransaction. Manager. Lookup. . . Propriedades para hibernate. properti es ou hibernate. cfg. xml
Boas Práticas n n Usar sempre o padrão facade. . . mas como englobar várias operações a a vários DAOs em uma transação?
Classe utilitária simples public class Hibernate. Util { private static final Session. Factory session. Factory; static { try { Configuration cfg = new Configuration(); session. Factory = cfg. configure(). build. Session. Factory(); } catch (Throwable ex) { ex. print. Stack. Trace(System. out); throw new Exception. Initializer. Error(ex); } } //. . . }
Classe utilitária simples //. . private static Session session; public static Session get. Session() { try { if (session == null || !session. is. Open()) { Session. Factory factory = get. Session. Factory(); session = factory. open. Session(); } return session; } catch (Exception e) { throw new Runtime. Exception(e); } } //. . .
Classe utilitária simples n Suporte transacional //. . private static Transaction transaction; public static void begin. Transaction() { transaction = get. Session(). begin. Transaction(); } public static void commit() { if (transaction != null) transaction. commit(); } public static void rollback() { if (transaction != null) transaction. rollback(); } //. . .
Usando n Todo DAO, quando precisar de uma Sessão, irá obte-la através do get. Session() ¡ n A fachada pode gerencar a transação com os metodos begin, commit e rollback ¡ n Para que vários DAOs obtenham a mesma sessão, basta não fecha-la Os DAOs não gerencia, mais as transações . . . Mas se a houver acesso concorrente
Classe utilitária com suporte a concorrencia //. . private static final Thread. Local<Session> local. Session = new Thread. Local<Session>(); public static Session get. Session() { try { Session session = local. Session. get(); if (session == null || !session. is. Open()) { Session. Factory factory = get. Session. Factory(); session = factory. open. Session(); local. Session. set(session); } return session; } catch (Exception e) { throw new Runtime. Exception(e); } } //. . .
Classe utilitária com suporte a concorrencia //. . private static final Thread. Local<Transaction> local. Tx = new Thread. Local<Transaction>(); public static void begin. Transaction() { local. Tx. set(get. Session(). begin. Transaction()); } public static void commit() { if (local. Tx. get() != null) local. Tx. get(). commit(); } public static void rollback() { if (local. Tx. get() != null) local. Tx. get(). rollback(); } //. . .
Exercício n Criar o modelo de Objetos analisando o schema do banco legado script. sql n n Criar criar DAOs e fachada para a aplicação Criar methodos de negício para ¡ ¡ ¡ Realizar uma reserva Agendar uma reserva Cancelar uma reserva
Transações e concorrência Jobson Ronan {jrjs@cin. ufpe. br}
- Slides: 37