Transações e Sessões
Nesta página
Visão geral
Neste guia, você aprenderá a usar o Mongoid para realizar transações. As transações permitem que você execute uma série de operações que alteram os dados somente se toda a transação estiver confirmada. Se qualquer operação na transação não for bem-sucedida, o driver interromperá a transação e descartará todas as alterações de dados antes que elas se tornem visíveis. Esse recurso é chamado de atomicidade.
No MongoDB, as transações são executadas dentro de sessões lógicas . Uma sessão é um agrupamento de operações de leitura ou escrita relacionadas que você deseja executar sequencialmente. As sessões permitem consistência causal para um grupo de operações, o que significa que todos os processos em seu aplicação concordam com a ordem das operações causalmente relacionadas.
As sessões permitem que você execute operações em uma transação compatível com ACID que atenda a uma expectativa de atomicidade, consistência, isolamento e durabilidade. O MongoDB garante que os dados envolvidos em suas operações de transação permaneçam consistentes, mesmo que as operações encontrem erros inesperados.
No Mongoid, você pode realizar transações usando uma das seguintes APIs:
API de transação de alto nível: o Mongoid gerencia o ciclo de vida da transação. Você pode usar essa API no Mongoid v9.0 e versões posteriores.
API de transação de baixo nível: você deve gerenciar o ciclo de vida da transação. Você pode usar essa API no Mongoid v6.4 e versões posteriores.
A seção API de sessão descreve como fazer alterações em seus dados a partir de uma sessão sem executar uma transação.
API de transação de alto nível
Você pode usar a API de transação de alto nível para gerenciar internamente o ciclo de vida de sua transação. Essa API confirma sua transação ou a encerra e incorpora lógica de tratamento de erros.
Você pode iniciar uma transação chamando o método transaction
em uma instância de um modelo, na classe de modelo ou em um módulo Mongoid
.
Quando você chama o método transaction
, o Mongoid executa as seguintes tarefas:
Cria uma sessão no cliente.
Inicia uma transação na sessão.
Executa as alterações de dados especificadas.
Confirma a transação no banco de dados se não ocorrerem erros ou termina a transação se houver um erro.
Encerra a sessão.
Se sua transação estiver confirmada, o Mongoid chamará quaisquer after_commit
chamada de resposta para todos os objetos modificados dentro da transação. Se houver um erro e a transação for revertida, o Mongoid chamará qualquer chamada de resposta after_rollback
para todos os objetos modificados dentro da transação. Para saber mais sobre essas chamadas de resposta e seu comportamento, consulte a seção Chamadas de resposta deste guia.
Exemplo
Este exemplo utiliza os seguintes modelos para representar documentos que descrevem livros e filmes:
class Book include Mongoid::Document field :title, type: String field :author, type: String field :length, type: Integer end class Film include Mongoid::Document field :title, type: String field :year, type: Integer end
O código a seguir demonstra como realizar uma transação em diferentes objetos para alterar dados em várias coleções:
# Starts a transaction from the model class Book.transaction do # Saves new Book and Film instances to MongoDB Book.create(title: 'Covert Joy', author: 'Clarice Lispector') Film.create(title: 'Nostalgia', year: 1983) end # Starts a transaction from an instance of Book book = Book.create(title: 'Sula', author: 'Toni Morrison') book.transaction do # Saves a new field value to the Book instance book.length = 192 book.save! end # Starts a transaction from the Mongoid instance Mongoid.transaction do # Deletes the Book instance in MongoDB book.destroy end
Comportamento do cliente
Somente as operações no mesmo cliente estão no escopo de uma transação, pois cada transação está vinculada a um cliente específico. Certifique-se de usar objetos do mesmo cliente dentro do bloco de método de transação.
O exemplo a seguir define classes de modelo que usam clientes diferentes e demonstra como as operações são executadas com base no cliente de origem:
# Defines a class by using the :default client class Post include Mongoid::Document end # Defines a class by using the :encrypted_client class User include Mongoid::Document store_in client: :encrypted_client end # Starts a transaction on the :encrypted_client User.transaction do # Uses the same client, so the operation is in the transaction User.create! # Uses a different client, so it is *not* in the transaction Post.create! end
Observação
Quando você chama o método transaction
no módulo Mongoid
, o Mongoid cria a transação usando o cliente :default
.
Encerrando transações
Qualquer exceção gerada dentro do bloco do método de transação encerra a transação e reverte as alterações de dados. O Mongoid exibe todas as exceções, exceto a exceção Mongoid::Errors::Rollback
. Você pode gerar essa exceção em seu aplicação para encerrar explicitamente a transação sem retornar a exceção para você. Por exemplo, você pode implementar essa exceção de transação para encerrar uma transação quando uma determinada condição não for atendida, mas sem gerar uma mensagem de exceção.
Chamadas de resposta
Esta API de transação introduz as chamadas de resposta after_commit
e after_rollback
.
O Mongoid aciona a chamada de resposta after_commit
para um objeto que foi criado, salvo ou excluído nos seguintes casos:
Depois que a transação for confirmada, o objeto foi modificado dentro da transação.
Depois que o objeto for persistente se o objeto tiver sido modificado fora do bloco de transação.
A chamada de resposta after_commit
é acionada somente depois que todas as outras chamadas de resposta forem realizadas com sucesso. Portanto, se um objeto for modificado fora de uma transação, é possível que o objeto seja persistente, mas a chamada de resposta after_commit
não seja acionada. Isso pode ocorrer, por exemplo, se o Mongoid tiver gerado uma exceção na chamada de resposta after_save
porque essa chamada de resposta deve ser concluída com sucesso para acionar after_commit
.
A chamada de resposta after_rollback
é acionada para um objeto que foi criado, salvo ou excluído dentro de uma transação, se a transação não tiver sido bem-sucedida e as alterações tiverem sido revertidas. O Mongoid nunca aciona after_rollback
fora de uma transação.
To learn more about callbacks, see the Customize Callbacks for Data Models guide.
API de transação de baixo nível
Ao usar a API de baixo nível, você deve criar uma sessão antes de iniciar uma transação. Você pode criar uma sessão chamando o método with_session
em uma classe de modelo ou em uma instância de um modelo.
Em seguida, você pode iniciar uma transação chamando o método start_transaction
em uma sessão. Ao usar esta API, você deve confirmar ou encerrar a transação manualmente. Você pode utilizar os métodos commit_transaction
e abort_transaction
na instância de sessão para gerenciar o ciclo de vida da transação.
Exemplo
Este exemplo usa a API de transação de baixo nível para executar as seguintes ações:
Cria uma sessão
inicia uma transação
Executa operações de dados
Confirma a transação ou a encerra se houver erros
# Starts a session from the model class Book.with_session do |session| session.start_transaction # Creates a Book Book.create(title: 'Siddhartha', author: 'Hermann Hesse') # Commits the transaction session.commit_transaction rescue StandardError # Ends the transaction if there is an error session.abort_transaction end
Observação
Se uma sessão terminar e incluir uma transação aberta, a transação será automaticamente encerrada.
Repetição da transação
Você pode tentar novamente a confirmação da transação se ela falhar inicialmente. O exemplo a seguir demonstra como tentar novamente a transação quando o Mongoid gerar a exceção UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL
:
begin session.commit_transaction rescue Mongo::Error => e if e.label?(Mongo::Error::UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL) retry else raise end end
Opções
Você pode especificar uma preocupação de leitura, preocupação de gravação ou preferência de leitura ao iniciar uma transação passando opções para o método start_transaction
:
session.start_transaction( read_concern: {level: :majority}, write_concern: {w: 3}, read: {mode: :primary} )
Para saber mais sobre as opções de transação disponíveis, consulte start_transaction na documentação da API do driver Ruby.
Comportamento do cliente
Para realizar operações dentro de uma transação, as operações devem usar o mesmo cliente em que a sessão foi iniciada. Por padrão, todas as operações são executadas usando o cliente padrão.
Para usar explicitamente um cliente diferente, use o método with
:
# Specifies that the operation should use the "other" client instead of # the default client User.with(client: :other) do Post.with(client: :other) do Post.with_session do |session| session.start_transaction Post.create! Post.create! User.create! session.commit_transaction end end end
Session API
Você pode usar sessões no Mongoid de maneira semelhante a que pode realizar uma transação. Você pode chamar o método with_session
em uma classe de modelo ou em uma instância de um modelo e executar algumas operações em um bloco. Todas as operações no bloco serão realizadas no contexto de sessão única.
As seguintes limitações se aplicam ao usar sessões:
Não é possível compartilhar uma sessão entre threads. As sessões não são seguras para threads.
Não é possível aninhar sessões. Por exemplo, você não pode chamar o método
with_session
em uma classe de modelo dentro do bloco passado para o métodowith_session
em outra classe de modelo. O seguinte código demonstra uma sessão aninhada que resulta em um erro:Book.with_session(causal_consistency: true) do # Nesting sessions results in errors Film.with_session(causal_consistency: true) do ... end end Todas as classes e instâncias de modelo usadas no bloco de sessão devem usar o mesmo driver cliente. Por exemplo, se você especificar um cliente diferente para um modelo usado no bloco do modelo ou instância que você chamou de
with_session
, o Mongoid retornará um erro.
Exemplos
Você pode usar o método with_session
em uma classe de modelo e passar as opções de sessão para executar um bloco de operações no contexto de uma sessão.
O código a seguir ativa a opção causal_consistency
para garantir a ordem das operações ao criar uma sessão no modelo Book
e, em seguida, executar operações de dados:
Book.with_session(causal_consistency: true) do Book.create! book = Person.first book.title = "Swann's Way" book.save end
Para saber mais sobre as opções de sessão disponíveis, consulte os detalhes do construtor da classe Session na documentação da API do driver Ruby.
Como alternativa, você pode usar o método with_session
em uma instância de um modelo e passar as opções de sessão para executar um bloco de operações no contexto de uma sessão.
O código a seguir habilita a opção causal_consistency
para garantir a ordem das operações ao criar uma sessão em uma instância de Book
e, em seguida, executar operações de dados:
book = Book.new book.with_session(causal_consistency: true) do book.title = 'Catch-22' book.save book.sellers << Shop.create! end
Informações adicionais
Para saber mais sobre transações, consulte Transações no manual do servidor.
Para saber mais sobre como executar operações CRUD, consulte o guia Executar operações de dados.