module Mongoid::Clients::Sessions::ClassMethods

Constants

CALLBACK_ACTIONS

Actions that can be used to trigger transactional callbacks. @api private

Public Instance Methods

after_commit(*args, &block) click to toggle source

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
after_create_commit(*args, &block) click to toggle source

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
after_destroy_commit(*args, &block) click to toggle source

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
after_rollback(*args, &block) click to toggle source

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
after_save_commit(*args, &block) click to toggle source

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
after_update_commit(*args, &block) click to toggle source

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
transaction(options = {}, session_options: {}) { || ... } click to toggle source

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
with_session(options = {}) { |session| ... } click to toggle source

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

_session() click to toggle source

@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
assert_valid_transaction_action(actions) click to toggle source

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
in_transaction?() click to toggle source

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
run_abort_callbacks(session) click to toggle source

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
run_commit_callbacks(session) click to toggle source

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
set_options_for_callbacks!(args) click to toggle source

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
transaction_include_any_action?(actions) click to toggle source
# 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
transactions_not_supported_exceptions() click to toggle source

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