module Mongoid::Clients::Sessions::ClassMethods
Constants
- CALLBACK_ACTIONS
Actions that can be used to trigger transactional callbacks. @api private
Public Instance Methods
Sets up a callback is called after a commit of a transaction. The callback is called only if the document is created, updated, or destroyed in the transaction.
See ActiveSupport::Callbacks::ClassMethods::set_callback
for more information about method parameters and possible options.
# File lib/mongoid/clients/sessions.rb, line 120 def after_commit(*args, &block) set_options_for_callbacks!(args) set_callback(:commit, :after, *args, &block) end
Shortcut for +after_commit :hook, on: :create+.
# File lib/mongoid/clients/sessions.rb, line 132 def after_create_commit(*args, &block) set_options_for_callbacks!(args, on: :create) set_callback(:commit, :after, *args, &block) end
Shortcut for +after_commit :hook, on: :destroy+.
# File lib/mongoid/clients/sessions.rb, line 144 def after_destroy_commit(*args, &block) set_options_for_callbacks!(args, on: :destroy) set_callback(:commit, :after, *args, &block) end
This callback is called after a create, update, or destroy are rolled back.
Please check the documentation of after_commit
for options.
# File lib/mongoid/clients/sessions.rb, line 152 def after_rollback(*args, &block) set_options_for_callbacks!(args) set_callback(:rollback, :after, *args, &block) end
Shortcut for +after_commit :hook, on: [ :create, :update ]+
# File lib/mongoid/clients/sessions.rb, line 126 def after_save_commit(*args, &block) set_options_for_callbacks!(args, on: [ :create, :update ]) set_callback(:commit, :after, *args, &block) end
Shortcut for +after_commit :hook, on: :update+.
# File lib/mongoid/clients/sessions.rb, line 138 def after_update_commit(*args, &block) set_options_for_callbacks!(args, on: :update) set_callback(:commit, :after, *args, &block) end
Executes a block within the context of a transaction.
If the block does not raise an error, the transaction is committed. If an error is raised, the transaction is aborted. The error is passed on except for the ‘Mongoid::Errors::Rollback`. This error is not passed on, so you can raise is if you want to deliberately rollback the transaction.
@param [ Hash ] options The transaction options. Please see the driver
documentation for the available session options.
@param [ Hash ] session_options The session options. A MongoDB
transaction must be started inside a session, therefore a session will be started. Please see the driver documentation for the available session options.
@raise [ Mongoid::Errors::InvalidTransactionNesting
] If the transaction is
opened on a client that already has an open transaction.
@raise [ Mongoid::Errors::TransactionsNotSupported
] If MongoDB deployment
the client is connected to does not support transactions.
@raise [ Mongoid::Errors::TransactionError
] If there is an error raised
by MongoDB deployment or MongoDB driver.
@yield Provided block will be executed inside a transaction.
# File lib/mongoid/clients/sessions.rb, line 90 def transaction(options = {}, session_options: {}) with_session(session_options) do |session| begin session.with_transaction(options) do yield end run_commit_callbacks(session) rescue *transactions_not_supported_exceptions raise Mongoid::Errors::TransactionsNotSupported rescue Mongoid::Errors::Rollback run_abort_callbacks(session) rescue Mongoid::Errors::InvalidSessionNesting # Session should be ended here. raise Mongoid::Errors::InvalidTransactionNesting.new rescue Mongo::Error::InvalidSession, Mongo::Error::InvalidTransactionOperation => e run_abort_callbacks(session) raise Mongoid::Errors::TransactionError.new(e) rescue StandardError => e run_abort_callbacks(session) raise e end end end
Execute a block within the context of a session.
@example Execute some operations in the context of a session.
Band.with_session(causal_consistency: true) do band = Band.create band.records << Record.new band.save band.reload.records end
@param [ Hash ] options The session options. Please see the driver
documentation for the available session options.
@raise [ Errors::InvalidSessionUse ] If an operation is attempted on a model using another
client from which the session was started or if sessions are nested.
@return [ Object
] The result of calling the block.
@yieldparam [ Mongo::Session ] The session being used for the block.
# File lib/mongoid/clients/sessions.rb, line 42 def with_session(options = {}) if Threaded.get_session(client: persistence_context.client) raise Mongoid::Errors::InvalidSessionNesting.new end session = persistence_context.client.start_session(options) Threaded.set_session(session, client: persistence_context.client) yield(session) rescue Mongo::Error::InvalidSession => ex if Mongo::Error::SessionsNotSupported === ex raise Mongoid::Errors::SessionsNotSupported.new else raise ex end rescue Mongo::Error::OperationFailure => ex if (ex.code == 40415 && ex.server_message =~ /startTransaction/) || (ex.code == 20 && ex.server_message =~ /Transaction/) raise Mongoid::Errors::TransactionsNotSupported.new else raise ex end rescue *transactions_not_supported_exceptions raise Mongoid::Errors::TransactionsNotSupported ensure Threaded.clear_modified_documents(session) Threaded.clear_session(client: persistence_context.client) end
Private Instance Methods
@return [ Mongo::Session ] Session for the current client.
# File lib/mongoid/clients/sessions.rb, line 176 def _session Threaded.get_session(client: persistence_context.client) end
Asserts that the given actions are valid for after_commit
and after_rollback
callbacks.
@param [ Array<Symbol> ] actions Actions to be checked. @raise [ ArgumentError ] If any of the actions is not valid.
# File lib/mongoid/clients/sessions.rb, line 235 def assert_valid_transaction_action(actions) if (actions - CALLBACK_ACTIONS).any? raise ArgumentError, ":on conditions for after_commit and after_rollback callbacks have to be one of #{CALLBACK_ACTIONS}" end end
This method should be used to detect whether a persistence operation is executed inside transaction or not.
Currently this method is used to detect when after_commit
callbacks should be triggered. If we introduce implicit transactions and therefore do not need to handle two different ways of triggering callbacks, we may want to remove this method.
@return [ true | false ] Whether there is a session for the current
client, and there is a transaction in progress for this session.
# File lib/mongoid/clients/sessions.rb, line 190 def in_transaction? _session&.in_transaction? || false end
Runs after_rollback
callbacks on modified documents.
@param [ Mongo::Session ] session Session on which
a transaction is started.
# File lib/mongoid/clients/sessions.rb, line 208 def run_abort_callbacks(session) Threaded.clear_modified_documents(session).each do |doc| doc.run_after_callbacks(:rollback) end end
Runs after_commit
callbacks on modified documents.
@param [ Mongo::Session ] session Session on which
a transaction is started.
# File lib/mongoid/clients/sessions.rb, line 198 def run_commit_callbacks(session) Threaded.clear_modified_documents(session).each do |doc| doc.run_after_callbacks(:commit) end end
Transforms custom options for after_commit
and after_rollback
callbacks into options for set_callback
.
# File lib/mongoid/clients/sessions.rb, line 216 def set_options_for_callbacks!(args) options = args.extract_options! args << options if options[:on] fire_on = Array(options[:on]) assert_valid_transaction_action(fire_on) options[:if] = [ -> { transaction_include_any_action?(fire_on) }, *options[:if] ] end end
# File lib/mongoid/clients/sessions.rb, line 241 def transaction_include_any_action?(actions) actions.any? do |action| case action when :create persisted? && previously_new_record? when :update !(previously_new_record? || destroyed?) when :destroy destroyed? end end end
Driver version 2.20 introduced a new exception for reporting that transactions are not supported. Prior to that, the condition was discovered by the rescue clause falling through to a different exception.
This method ensures that Mongoid
continues to work with older driver versions, by only returning the new exception.
Once support is removed for all versions prior to 2.20.0, we can replace this method.
# File lib/mongoid/clients/sessions.rb, line 169 def transactions_not_supported_exceptions return nil unless defined? Mongo::Error::TransactionsNotSupported Mongo::Error::TransactionsNotSupported end