module Mongo::Operation::ResponseHandling

Shared behavior of response handling for operations.

@api private

Private Instance Methods

add_error_labels(client, connection, session) { || ... } click to toggle source

Adds error labels to exceptions raised in the yielded to block, which should perform MongoDB operations and raise Mongo::Errors on failure. This method handles network errors (Error::SocketError) and server-side errors (Error::OperationFailure); it does not handle server selection errors (Error::NoServerAvailable), for which labels are added in the server selection code.

# File lib/mongo/operation/shared/response_handling.rb, line 41
def add_error_labels(client, connection, session)
  begin
    yield
  rescue Mongo::Error::SocketError => e
    if session && session.in_transaction? && !session.committing_transaction?
      e.add_label('TransientTransactionError')
    end
    if session && session.committing_transaction?
      e.add_label('UnknownTransactionCommitResult')
    end

    maybe_add_retryable_write_error_label!(e, connection, client, session)

    raise e
  rescue Mongo::Error::SocketTimeoutError => e
    maybe_add_retryable_write_error_label!(e, connection, client, session)
    raise e
  rescue Mongo::Error::OperationFailure => e
    if session && session.committing_transaction?
      if e.write_retryable? || e.wtimeout? || (e.write_concern_error? &&
          !Session::UNLABELED_WRITE_CONCERN_CODES.include?(e.write_concern_error_code)
      ) || e.max_time_ms_expired?
        e.add_label('UnknownTransactionCommitResult')
      end
    end

    maybe_add_retryable_write_error_label!(e, connection, client, session)

    raise e
  end
end
add_server_diagnostics(connection) { || ... } click to toggle source

Yields to the block and, if the block raises an exception, adds a note to the exception with the address of the specified server.

This method is intended to add server address information to exceptions raised during execution of operations on servers.

# File lib/mongo/operation/shared/response_handling.rb, line 95
def add_server_diagnostics(connection)
  yield
rescue Error::SocketError, Error::SocketTimeoutError
  # Diagnostics should have already been added by the connection code,
  # do not add them again.
  raise
rescue Error, Error::AuthError => e
  e.add_note("on #{connection.address.seed}")
  e.generation = connection.generation
  raise e
end
maybe_add_retryable_write_error_label!(error, connection, client, session) click to toggle source

A method that will add the RetryableWriteError label to an error if any of the following conditions are true:

The error meets the criteria for a retryable error (i.e. has one

of the retryable error codes or error messages)

AND the server does not support adding the RetryableWriteError label OR

the error is a network error (i.e. the driver must add the label)

AND the error occured during a commitTransaction or abortTransaction

OR the error occured during a write outside of a transaction on a
client that has retry writes enabled.

If these conditions are met, the original error will be mutated. If they're not met, the error will not be changed.

@param [ Mongo::Error ] error The error to which to add the label. @param [ Mongo::Server::Connection ] connection The connection on which

the operation is performed.

@param [ Mongo::Client | nil ] client The client that is performing

the operation.

@param [ Mongo::Session ] session The operation's session.

@note The client argument is optional because some operations, such as

end_session, do not pass the client as an argument to the execute
method.
# File lib/mongo/operation/shared/response_handling.rb, line 135
def maybe_add_retryable_write_error_label!(error, connection, client, session)
  in_transaction = session && session.in_transaction?
  committing_transaction = in_transaction && session.committing_transaction?
  aborting_transaction = in_transaction && session.aborting_transaction?
  modern_retry_writes = client && client.options[:retry_writes]
  legacy_retry_writes = client && !client.options[:retry_writes] &&
    client.max_write_retries > 0

  # An operation is retryable if it meets one of the following criteria:
  # - It is a commitTransaction or abortTransaction
  # - It does not occur during a transaction and the client has enabled
  #   modern or legacy writes
  #
  # Note: any write operation within a transaction (excepting commit and
  # abort is NOT a retryable operation)
  retryable_operation = committing_transaction || aborting_transaction ||
    (!in_transaction && (modern_retry_writes || legacy_retry_writes))

  # An operation should add the RetryableWriteError label if one of the
  # following conditions is met:
  # - The server does not support adding the RetryableWriteError label
  # - The error is a network error
  should_add_error_label =
    !connection.description.features.retryable_write_error_label_enabled? ||
    error.write_concern_error_label?('RetryableWriteError') ||
    error.is_a?(Mongo::Error::SocketError) ||
    error.is_a?(Mongo::Error::SocketTimeoutError)

  if retryable_operation && should_add_error_label && error.write_retryable?
    error.add_label('RetryableWriteError')
  end
end
unpin_maybe(session) { || ... } click to toggle source

Unpins the session if the session is pinned and the yielded to block raises errors that are required to unpin the session.

@note This method takes the session as an argument because this module

is included in BulkWrite which does not store the session in the
receiver (despite Specifiable doing so).

@param [ Session | nil ] session Session to consider.

# File lib/mongo/operation/shared/response_handling.rb, line 81
def unpin_maybe(session)
  yield
rescue Mongo::Error => e
  if session
    session.unpin_maybe(e)
  end
  raise
end
validate_result(result, client, connection) click to toggle source
# File lib/mongo/operation/shared/response_handling.rb, line 25
def validate_result(result, client, connection)
  unpin_maybe(session) do
    add_error_labels(client, connection, session) do
      add_server_diagnostics(connection) do
        result.validate!
      end
    end
  end
end