123456789_123456789_123456789_123456789_123456789_

Module: Mongoid::Clients::Sessions::ClassMethods

Relationships & Source Files
Extension / Inclusion / Inheritance Descendants
Extended In:
Defined in: lib/mongoid/clients/sessions.rb

Constant Summary

Instance Attribute Summary

Instance Method Summary

Instance Attribute Details

#in_transaction?true | false (readonly, private)

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.

Returns:

  • (true | false)

    Whether there is a session for the current client, and there is a transaction in progress for this session.

[ GitHub ]

  
# File 'lib/mongoid/clients/sessions.rb', line 190

def in_transaction?
  _session&.in_transaction? || false
end

Instance Method Details

#_sessionMongo::Session (private)

Returns:

  • (Mongo::Session)

    Session for the current client.

[ GitHub ]

  
# File 'lib/mongoid/clients/sessions.rb', line 176

def _session
  Threaded.get_session(client: persistence_context.client)
end

#after_commit(*args, &block)

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.

[ GitHub ]

  
# 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)

Shortcut for after_commit :hook, on: :create.

[ GitHub ]

  
# 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)

Shortcut for after_commit :hook, on: :destroy.

[ GitHub ]

  
# 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)

This callback is called after a create, update, or destroy are rolled back.

Please check the documentation of #after_commit for options.

[ GitHub ]

  
# 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)

Shortcut for after_commit :hook, on: [ :create, :update ]

[ GitHub ]

  
# 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)

Shortcut for after_commit :hook, on: :update.

[ GitHub ]

  
# 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

#assert_valid_transaction_action(actions) (private)

Asserts that the given actions are valid for after_commit and after_rollback callbacks.

Parameters:

Raises:

  • (ArgumentError)

    If any of the actions is not valid.

[ GitHub ]

  
# 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

#run_abort_callbacks(session) (private)

Runs after_rollback callbacks on modified documents.

Parameters:

  • session (Mongo::Session)

    Session on which a transaction is started.

[ GitHub ]

  
# 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) (private)

Runs after_commit callbacks on modified documents.

Parameters:

  • session (Mongo::Session)

    Session on which a transaction is started.

[ GitHub ]

  
# 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) (private)

Transforms custom options for after_commit and after_rollback callbacks into options for set_callback.

[ GitHub ]

  
# 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(options = {}, session_options: {}) { ... }

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.

Parameters:

  • options (Hash) (defaults to: {})

    The transaction options. Please see the driver documentation for the available session options.

  • session_options (Hash)

    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.

Yields:

  • Provided block will be executed inside a transaction.

Raises:

[ GitHub ]

  
# 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

#transaction_include_any_action?(actions) ⇒ Boolean (private)

[ GitHub ]

  
# 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 (private)

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.

[ GitHub ]

  
# File 'lib/mongoid/clients/sessions.rb', line 169

def transactions_not_supported_exceptions
  return nil unless defined? Mongo::Error::TransactionsNotSupported

  Mongo::Error::TransactionsNotSupported
end

#with_session(options = {}) {|The| ... } ⇒ Object

Execute a block within the context of a session.

Examples:

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

Parameters:

  • options (Hash) (defaults to: {})

    The session options. Please see the driver documentation for the available session options.

Yield Parameters:

  • The (Mongo::Session)

    session being used for the block.

Returns:

  • (Object)

    The result of calling the block.

Raises:

  • (Errors::InvalidSessionUse)

    If an operation is attempted on a model using another client from which the session was started or if sessions are nested.

[ GitHub ]

  
# 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