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 188

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 174

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

#abort_transaction(session) (private)

Aborts the active transaction on the session, and calls 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 209

def abort_transaction(session)
  session.abort_transaction
  Threaded.clear_modified_documents(session).each do |doc|
    doc.run_after_callbacks(:rollback)
  end
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 118

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 130

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 142

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 150

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 124

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 136

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 237

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

#commit_transaction(session) (private)

Commits the active transaction on the session, and calls 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 197

def commit_transaction(session)
  session.commit_transaction
  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 218

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 89

def transaction(options = {}, session_options: {})
  with_session(session_options) do |session|
    begin
      session.start_transaction(options)
      yield
      commit_transaction(session)
    rescue *transactions_not_supported_exceptions
      raise Mongoid::Errors::TransactionsNotSupported
    rescue Mongoid::Errors::Rollback
      abort_transaction(session)
    rescue Mongoid::Errors::InvalidSessionNesting
      # Session should be ended here.
      raise Mongoid::Errors::InvalidTransactionNesting.new
    rescue Mongo::Error::InvalidSession, Mongo::Error::InvalidTransactionOperation => e
      abort_transaction(session)
      raise Mongoid::Errors::TransactionError(e)
    rescue StandardError => e
      abort_transaction(session)
      raise e
    end
  end
end

#transaction_include_any_action?(actions) ⇒ Boolean (private)

[ GitHub ]

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

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 167

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_session(client: persistence_context.client)
end