事务
MongoDB服务器4.0版本引入了多文档事务。(更新单个文档中的多个字段在所有MongoDB版本中都是原子性的)。事务需要非独立MongoDB拓扑结构和2.6或更高版本的Ruby驱动程序。高级事务API需要Mongoid版本9.0或更高版本,而低级API需要Mongoid版本6.4或更高版本。
使用事务
高级API
可以通过调用transaction
方法来启动一个事务,该方法可以在 Mongoid 文档类的实例、文档类或 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
当调用 transaction
方法时,Mongoid 会执行以下操作
在客户端创建一个会话,该会话由
transaction
方法调用的接收者使用;在会话上启动一个事务;
执行给定的代码块;
如果没有抛出异常,则提交事务;
调用事务内修改的所有对象的
after_commit
回调函数
如果在代码块中抛出异常,则中止事务;
调用事务内修改的所有对象的
after_rollback
回调函数
关闭会话
注意
由于事务与特定客户端相关联,因此只有同一客户端上的操作才会处于事务的作用域内。因此,建议仅在 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
注意
当在 Mongoid
模块上调用 transaction
方法时,事务将使用 :default
客户端创建。
中止事务
在 transaction
方法块内抛出的任何异常都会中止事务。通常情况下,抛出的异常会继续传递,除了 Mongoid::Errors::Rollback
。如果您想显式中止事务而不传递异常,则应抛出此错误。
回调
事务API引入了两个新的回调 - after_commit
和 after_rollback
。
after_commit
回调在对象被创建、保存或销毁时触发
如果在事务内修改了对象,则在事务提交后触发;
如果在事务外修改了对象,则在对象持久化后触发。
注意
在任何情况下,after_commit
回调只有在所有其他回调成功执行后才触发。因此,如果对象未在事务中修改,则对象可能已持久化,但 after_commit
回调可能未触发(例如,在 after_save
回调中抛出异常)。
after_rollback
回调在事务内创建、保存或销毁的对象触发,如果事务被中止。在没有事务的情况下不会触发 after_rollback
。
低级API
为了开始一个事务,应用程序必须有一个会话.
可以通过在会话上调用 start_transaction
方法来启动一个事务,这可以通过在模型类或实例上调用 with_session
方法来获取
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
在启动事务时还可以指定读取关注点、写入关注点和读取优先级
Person.with_session do |session| session.start_transaction( read_concern: {level: :majority}, write_concern: {w: 3}, read: {mode: :primary}) end
事务可以是提交或中止。执行这些操作的方法是 commit_transaction
和 abort_transaction
,这些方法同样应用于会话实例
Person.with_session do |session| session.commit_transaction end Person.with_session do |session| session.abort_transaction end
如果会话以未关闭的事务结束,则事务将中止。
事务提交如果在执行过程中失败,可以重试。以下是用Ruby实现重试的代码示例
begin session.commit_transaction rescue Mongo::Error => e if e.label?(Mongo::Error::UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL) retry else raise end end
请注意,为了在事务中执行操作,操作必须使用与会话启动时相同的客户端。默认情况下,所有操作都在默认客户端上执行
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
要显式使用不同的客户端,请使用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