事务
MongoDB 服务器 4.0版本引入了多文档事务。 (在所有版本的MongoDB中,对单个文档中多个字段的更新都是原子性的)。 事务需要非独立运行的MongoDB拓扑结构和Ruby驾驶员版本2.6或更高版本。 较高级别的ACID 事务API需要 Mongoid 版本9.0或更高版本,而较低级别的API需要 Mongoid 版本6 。 4或更高版本。
使用事务
更高级别的 API
可以通过对 Mongoid文档类的实例、Mongoid文档类、或Mongoid
模块调用 transaction
方法来启动ACID 事务:
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
方法调用的接收者使用;在会话上启动ACID 事务;
执行给定区块;
如果区块中没有引发异常,则提交ACID 事务;
为事务中修改的所有对象调用
after_commit
回调
如果区块中出现异常,则中止ACID 事务;
为事务中修改的所有对象调用
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
除外。 如果要显式中止事务而不传递异常,则应引发此错误。
回调
Transaction API引入了两个新的回调 — after_commit
和after_rollback
。
after_commit
为创建、保存或销毁的对象触发回调:
如果在ACID 事务内修改了对象,则在ACID 事务提交后;
如果在事务外修改了对象,则在持久化对象后。
注意
在任何情况下,只有在所有其他回调成功执行后才会触发after_commit
回调。 因此,如果在没有ACID 事务的情况下修改了对象,则可能是对象已持久保存,但未触发after_commit
回调(示例,在after_save
回调中引发异常)。
after_rollback
如果ACID 事务中止,则会为在ACID 事务中创建、保存或销毁的对象触发回调。 如果没有ACID 事务,则永远不会触发after_rollback
。
较低级别的 API
要启动ACID 事务,应用程序必须具有会话。
可以通过在会话上调用start_transaction
方法来启动ACID 事务,可以通过在模型类或实例上调用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
还可以在启动事务(transaction)时指定读关注(read concern)、写关注(write concern)和读取偏好(read preference):
Person.with_session do |session| session.start_transaction( read_concern: {level: :majority}, write_concern: {w: 3}, read: {mode: :primary}) end
可以提交或中止ACID 事务。 执行此操作的相应方法是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