Transações
A versão 4.0 do servidor MongoDB introduz transações de vários documentos. (As atualizações de vários campos em um único documento são atômicas em todas as versões do MongoDB). As transações exigem uma topologia não standalone do MongoDB e um driver Ruby versão 2.6 ou superior. Uma API de transação de nível superior exige a versão Mongoide 9.0 ou superior, enquanto uma API de nível inferior exige a versão Mongoide 6.4 ou superior.
Usando transação
API de nível superior
Uma transação pode ser iniciada chamando o método transaction
em uma instância de uma classe de documento Mongoid , em uma classe de documento Mongoid ou no módulo Mongoid
:
Band.transaction do Band.create(title: 'Led Zeppelin') end band = Band.create(title: 'Deep Purple') band.transaction do band.active = false band.save! end Mongoid.transaction do band.destroy end
Quando o método transaction
é chamado, o Mongoid faz o seguinte:
cria uma sessão em um cliente que é usado pelo receptor da chamada de método
transaction
;inicia uma transação na sessão;
executa o bloco fornecido;
confirma a transação se nenhuma exceção gerada no bloco;
chama
after_commit
retornos de chamada para todos os objetos modificados dentro da transação
aborta a transação se uma exceção for gerada no bloco;
chama
after_rollback
retornos de chamada para todos os objetos modificados dentro da transação
fecha a sessão
Observação
Como uma transação está vinculada a um cliente específico, _only_ operações no mesmo cliente estarão no escopo da transação. Portanto, é recomendável que somente objetos que usam o mesmo cliente sejam usados dentro do bloco de método transaction
.
class Author include Mongoid::Document store_in client: :encrypted_client end class User include Mongoid::Document store_in client: :encrypted_client end class Article include Mongoid::Document # This class uses the :default client end # Transaction is started on the :encrypted_client Author.transaction do # This operation uses the same client, so it is in the transaction Author.create! # This operation also uses the same client, so it is in the transaction User.create! # This operation uses a different client, so it is NOT in the transaction Article.create! end
Observação
Quando o método transaction
é chamado no módulo Mongoid
, a transação é criada usando o cliente :default
.
Abortando transação
Qualquer exceção gerada dentro do bloco de método transaction
aborta a transação. Normalmente, a exceção criada é passada, exceto para Mongoid::Errors::Rollback
. Este erro deve ser gerado se você quiser abortar explicitamente a transação sem passar uma exceção.
Chamadas de resposta
A API de transação introduz duas novas chamadas de resposta - after_commit
e after_rollback
.
after_commit
a chamada de resposta de resposta é acionada para um objeto que foi criado, salvo ou destruído:
depois que a transação for confirmada se o objeto foi modificado dentro da transação;
depois que o objeto tiver sido persistente se o objeto tiver sido modificado fora de uma transação.
Observação
Em qualquer caso, a chamada de resposta de resposta after_commit
é acionada somente depois que todas as outras chamadas de resposta foram executadas com sucesso. Portanto, se o objeto for modificado sem uma transação, é possível que o objeto tenha persistido, mas after_commit
chamada de resposta de resposta não foi acionada (por exemplo, uma exceção gerada em after_save
chamada de resposta de resposta ).
after_rollback
a chamada de resposta de resposta é acionada para um objeto que foi criado, salvo ou destruído dentro de uma transação se a transação tiver sido cancelada. after_rollback
nunca é acionado sem uma transação.
API de nível inferior
Para iniciar uma transação, o aplicação deve ter umasessão .
Uma transação pode ser iniciada ligando para o método start_transaction
em uma sessão, que pode ser obtido chamando o método with_session
em uma classe ou instância de modelo:
class Person include Mongoid::Document end Person.with_session do |session| session.start_transaction end person = Person.new person.with_session do |session| session.start_transaction end
Também é possível especificar a referência de leitura, referência de escrita e preferência de leitura ao iniciar uma transação:
Person.with_session do |session| session.start_transaction( read_concern: {level: :majority}, write_concern: {w: 3}, read: {mode: :primary}) end
Uma transação pode ser confirmada ou abortada. Os métodos correspondentes para fazer isso são commit_transaction
e abort_transaction
, novamente na instância da sessão:
Person.with_session do |session| session.commit_transaction end Person.with_session do |session| session.abort_transaction end
Se uma sessão terminar com uma transação aberta, a transação será cancelada.
A confirmação da transação pode ser repetida se falhar. Aqui está o código Ruby para fazer isso:
begin session.commit_transaction rescue Mongo::Error => e if e.label?(Mongo::Error::UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL) retry else raise end end
Observe que, para realizar operações dentro da transação, as operações devem usar o mesmo cliente em que a sessão foi iniciada. Por padrão, todas as operações serão feitas no cliente padrão:
class Person include Mongoid::Document end class Post include Mongoid::Document end Person.with_session do |s| s.start_transaction Person.create! Person.create! Post.create! s.commit_transaction end
Para usar explicitamente um cliente diferente, use o método with
:
Post.with(client: :other) do Person.with(client: :other) do Person.with_session do |s| s.start_transaction Person.create! Person.create! Post.create! s.commit_transaction end end end