def commit_transaction(options=nil)
check_if_ended!
check_if_no_transaction!
if within_states?(TRANSACTION_ABORTED_STATE)
raise Mongo::Error::InvalidTransactionOperation.new(
Mongo::Error::InvalidTransactionOperation.cannot_call_after_msg(
:abortTransaction, :commitTransaction))
end
options ||= {}
begin
if within_states?(TRANSACTION_COMMITTED_STATE)
@state = @last_commit_skipped ? STARTING_TRANSACTION_STATE : TRANSACTION_IN_PROGRESS_STATE
@already_committed = true
end
if starting_transaction?
@last_commit_skipped = true
else
@last_commit_skipped = false
write_concern = options[:write_concern] || txn_options[:write_concern]
if write_concern && !write_concern.is_a?(WriteConcern::Base)
write_concern = WriteConcern.get(write_concern)
end
write_with_retry(self, write_concern, true) do |server, txn_num, is_retry|
if is_retry
if write_concern
wco = write_concern.options.merge(w: :majority)
wco[:wtimeout] ||= 10000
write_concern = WriteConcern.get(wco)
else
write_concern = WriteConcern.get(w: :majority, wtimeout: 10000)
end
end
Operation::Command.new(
selector: { commitTransaction: 1 },
db_name: 'admin',
session: self,
txn_num: txn_num,
write_concern: write_concern,
).execute(server)
end
end
rescue Mongo::Error::NoServerAvailable, Mongo::Error::SocketError => e
e.send(:add_label, Mongo::Error::UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL)
raise e
rescue Mongo::Error::OperationFailure => e
err_doc = e.instance_variable_get(:@result).send(:first_document)
if e.write_retryable? || (err_doc['writeConcernError'] &&
!UNLABELED_WRITE_CONCERN_CODES.include?(err_doc['writeConcernError']['code']))
e.send(:add_label, Mongo::Error::UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL)
end
raise e
ensure
@state = TRANSACTION_COMMITTED_STATE
end
end