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