lib/mongo/retryable.rb in mongo-2.5.3 vs lib/mongo/retryable.rb in mongo-2.6.0
- old
+ new
@@ -1,6 +1,6 @@
-# Copyright (C) 2015 MongoDB, Inc.
+# Copyright (C) 2015-2018 MongoDB, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
@@ -28,29 +28,30 @@
# ...
# end
#
# @note This only retries read operations on socket errors.
#
+ # @param [ Mongo::Session ] session The session that the operation is being run on.
# @param [ Proc ] block The block to execute.
#
# @yieldparam [ Server ] server The server to which the write should be sent.
#
# @return [ Result ] The result of the operation.
#
# @since 2.1.0
- def read_with_retry
+ def read_with_retry(session = nil)
attempt = 0
begin
attempt += 1
yield
rescue Error::SocketError, Error::SocketTimeoutError => e
- raise(e) if attempt > cluster.max_read_retries
+ raise(e) if attempt > cluster.max_read_retries || (session && session.in_transaction?)
log_retry(e)
cluster.scan!
retry
rescue Error::OperationFailure => e
- if cluster.sharded? && e.retryable?
+ if cluster.sharded? && e.retryable? && !(session && session.in_transaction?)
raise(e) if attempt > cluster.max_read_retries
log_retry(e)
sleep(cluster.read_retry_interval)
retry
else
@@ -91,41 +92,45 @@
# end
#
# @note This only retries operations on not master failures, since it is
# the only case we can be sure a partial write did not already occur.
#
+ # @param [ true | false ] ending_transaction True if the write operation is abortTransaction or
+ # commitTransaction, false otherwise.
# @param [ Proc ] block The block to execute.
#
# @return [ Result ] The result of the operation.
#
# @since 2.1.0
- def write_with_retry(session, write_concern, &block)
- unless retry_write_allowed?(session, write_concern)
- return legacy_write_with_retry(&block)
+ def write_with_retry(session, write_concern, ending_transaction = false, &block)
+ unless retry_write_allowed?(session, write_concern) || ending_transaction
+ return legacy_write_with_retry(nil, session, &block)
end
server = cluster.next_primary
- unless server.retry_writes?
- return legacy_write_with_retry(server, &block)
+
+ unless server.retry_writes? || ending_transaction
+ return legacy_write_with_retry(server, session, &block)
end
begin
- txn_num = session.next_txn_num
+ txn_num = session.in_transaction? ? session.txn_num : session.next_txn_num
yield(server, txn_num)
rescue Error::SocketError, Error::SocketTimeoutError => e
+ raise e if session.in_transaction? && !ending_transaction
retry_write(e, txn_num, &block)
rescue Error::OperationFailure => e
- raise e unless e.write_retryable?
+ raise e if (session.in_transaction? && !ending_transaction) || !e.write_retryable?
retry_write(e, txn_num, &block)
end
end
private
def retry_write_allowed?(session, write_concern)
session && session.retry_writes? &&
- (write_concern.nil? || write_concern.acknowledged?)
+ (write_concern.nil? || write_concern.acknowledged?) or false
end
def retry_write(original_error, txn_num, &block)
cluster.scan!
server = cluster.next_primary
@@ -141,18 +146,21 @@
raise e
rescue
raise original_error
end
- def legacy_write_with_retry(server = nil)
+ def legacy_write_with_retry(server = nil, session = nil)
+ # This is the pre-session retry logic, and is not subject to
+ # current retryable write specifications.
+ # In particular it does not retry on SocketError and SocketTimeoutError.
attempt = 0
begin
attempt += 1
yield(server || cluster.next_primary)
rescue Error::OperationFailure => e
server = nil
raise(e) if attempt > Cluster::MAX_WRITE_RETRIES
- if e.write_retryable?
+ if e.write_retryable? && !(session && session.in_transaction?)
log_retry(e)
cluster.scan!
retry
else
raise(e)