spec/mongo/retryable_spec.rb in mongo-2.8.0 vs spec/mongo/retryable_spec.rb in mongo-2.9.0.rc0

- old
+ new

@@ -1,27 +1,35 @@ require 'spec_helper' class RetryableTestConsumer include Mongo::Retryable + attr_reader :client attr_reader :cluster attr_reader :operation - def initialize(operation, cluster) + def initialize(operation, cluster, client) @operation = operation @cluster = cluster + @client = client end def max_read_retries - cluster.max_read_retries + client.max_read_retries end def read_retry_interval - cluster.read_retry_interval + client.read_retry_interval end def read + read_with_retry(nil, Mongo::ServerSelector.get(mode: :primary)) do + operation.execute + end + end + + def read_legacy read_with_retry do operation.execute end end @@ -79,37 +87,56 @@ double('operation') end let(:server) { double('server') } + let(:max_read_retries) { 1 } + let(:max_write_retries) { 1 } + let(:cluster) do - double('cluster', next_primary: server) + double('cluster', next_primary: server).tap do |cluster| + allow(cluster).to receive(:replica_set?).and_return(true) + allow(cluster).to receive(:addresses).and_return(['x']) + end end + let(:client) do + double('client').tap do |client| + allow(client).to receive(:cluster).and_return(cluster) + allow(client).to receive(:max_read_retries).and_return(max_read_retries) + allow(client).to receive(:max_write_retries).and_return(max_write_retries) + end + end + let(:server_selector) do double('server_selector', select_server: server) end let(:retryable) do - LegacyRetryableTestConsumer.new(operation, cluster) + LegacyRetryableTestConsumer.new(operation, cluster, client) end - describe '#read_with_retry' do + before do + # Retryable reads perform server selection + allow_any_instance_of(Mongo::ServerSelector::Primary).to receive(:select_server).and_return(server) + end + shared_examples_for 'reads with retries' do + context 'when no exception occurs' do before do expect(operation).to receive(:execute).and_return(true) end it 'executes the operation once' do - expect(retryable.read).to be true + expect(read_operation).to be true end end context 'when ending_transaction is true' do - let(:retryable) { RetryableTestConsumer.new(operation, cluster) } + let(:retryable) { RetryableTestConsumer.new(operation, cluster, client) } it 'raises ArgumentError' do expect do retryable.write_with_retry(nil, nil, true) end.to raise_error(ArgumentError, 'Cannot end a transaction without a session') @@ -117,32 +144,32 @@ end context 'when a socket error occurs' do before do + expect(retryable).to receive(:select_server).ordered expect(operation).to receive(:execute).and_raise(Mongo::Error::SocketError).ordered - expect(cluster).to receive(:max_read_retries).and_return(1).ordered - expect(cluster).to receive(:scan!).and_return(true).ordered + expect(retryable).to receive(:select_server).ordered expect(operation).to receive(:execute).and_return(true).ordered end it 'executes the operation twice' do - expect(retryable.read).to be true + expect(read_operation).to be true end end context 'when a socket timeout error occurs' do before do + expect(retryable).to receive(:select_server).ordered expect(operation).to receive(:execute).and_raise(Mongo::Error::SocketTimeoutError).ordered - expect(cluster).to receive(:max_read_retries).and_return(1).ordered - expect(cluster).to receive(:scan!).and_return(true).ordered + expect(retryable).to receive(:select_server).ordered expect(operation).to receive(:execute).and_return(true).ordered end it 'executes the operation twice' do - expect(retryable.read).to be true + expect(read_operation).to be true end end context 'when an operation failure occurs' do @@ -153,11 +180,11 @@ expect(cluster).to receive(:sharded?).and_return(false) end it 'raises an exception' do expect { - retryable.read + read_operation }.to raise_error(Mongo::Error::OperationFailure) end end context 'when the cluster is a mongos' do @@ -173,11 +200,11 @@ expect(cluster).to receive(:sharded?).and_return(true) end it 'raises the exception' do expect { - retryable.read + read_operation }.to raise_error(Mongo::Error::OperationFailure) end end context 'when the operation failure is retryable' do @@ -187,45 +214,71 @@ end context 'when the retry succeeds' do before do + expect(retryable).to receive(:select_server).ordered expect(operation).to receive(:execute).and_raise(error).ordered expect(cluster).to receive(:sharded?).and_return(true) - expect(cluster).to receive(:max_read_retries).and_return(1).ordered - expect(cluster).to receive(:read_retry_interval).and_return(0.1).ordered + expect(client).to receive(:read_retry_interval).and_return(0.1).ordered + expect(retryable).to receive(:select_server).ordered expect(operation).to receive(:execute).and_return(true).ordered end it 'returns the result' do - expect(retryable.read).to be true + expect(read_operation).to be true end end context 'when the retry fails once and then succeeds' do + let(:max_read_retries) { 2 } before do + expect(retryable).to receive(:select_server).ordered expect(operation).to receive(:execute).and_raise(error).ordered + expect(cluster).to receive(:sharded?).and_return(true) - expect(cluster).to receive(:max_read_retries).and_return(2).ordered - expect(cluster).to receive(:read_retry_interval).and_return(0.1).ordered + expect(client).to receive(:read_retry_interval).and_return(0.1).ordered + expect(retryable).to receive(:select_server).ordered expect(operation).to receive(:execute).and_raise(error).ordered + expect(cluster).to receive(:sharded?).and_return(true) - expect(cluster).to receive(:max_read_retries).and_return(2).ordered - expect(cluster).to receive(:read_retry_interval).and_return(0.1).ordered + expect(client).to receive(:read_retry_interval).and_return(0.1).ordered + expect(retryable).to receive(:select_server).ordered expect(operation).to receive(:execute).and_return(true).ordered end it 'returns the result' do - expect(retryable.read).to be true + expect(read_operation).to be true end end end end end end + describe '#read_with_retry' do + let(:read_operation) do + retryable.read + end + + it_behaves_like 'reads with retries' + + context 'zero argument legacy invocation' do + + before do + allow_any_instance_of(Mongo::ServerSelector::PrimaryPreferred).to receive(:select_server).and_return(server) + end + + let(:read_operation) do + retryable.read_legacy + end + + it_behaves_like 'reads with retries' + end + end + describe '#retry_write_allowed?' do let(:retryable) { RetryableHost.new } context 'nil session' do it 'returns false' do @@ -411,10 +464,10 @@ end describe '#write_with_retry - modern' do let(:retryable) do - ModernRetryableTestConsumer.new(operation, cluster) + ModernRetryableTestConsumer.new(operation, cluster, client) end before do # Quick sanity check that the expected code path is being exercised expect(retryable.retry_write_allowed_as_configured?).to be true