mongo 2.7.2 → 2.8.0.rc0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +1 -3
- data/lib/mongo/address.rb +17 -20
- data/lib/mongo/address/ipv4.rb +6 -3
- data/lib/mongo/address/ipv6.rb +6 -3
- data/lib/mongo/address/unix.rb +5 -2
- data/lib/mongo/auth.rb +15 -2
- data/lib/mongo/auth/cr/conversation.rb +4 -2
- data/lib/mongo/auth/ldap/conversation.rb +4 -2
- data/lib/mongo/auth/scram.rb +3 -7
- data/lib/mongo/auth/scram/conversation.rb +28 -19
- data/lib/mongo/auth/user.rb +45 -10
- data/lib/mongo/auth/x509/conversation.rb +4 -2
- data/lib/mongo/cluster.rb +9 -17
- data/lib/mongo/error.rb +2 -0
- data/lib/mongo/error/missing_password.rb +29 -0
- data/lib/mongo/error/operation_failure.rb +7 -3
- data/lib/mongo/error/parser.rb +2 -1
- data/lib/mongo/error/sdam_error_detection.rb +54 -0
- data/lib/mongo/operation/aggregate/command.rb +1 -16
- data/lib/mongo/operation/aggregate/op_msg.rb +1 -1
- data/lib/mongo/operation/collections_info.rb +2 -3
- data/lib/mongo/operation/delete/command.rb +2 -15
- data/lib/mongo/operation/delete/legacy.rb +1 -16
- data/lib/mongo/operation/explain/command.rb +1 -16
- data/lib/mongo/operation/explain/legacy.rb +1 -16
- data/lib/mongo/operation/find/command.rb +1 -16
- data/lib/mongo/operation/find/legacy.rb +1 -16
- data/lib/mongo/operation/get_more/command.rb +1 -16
- data/lib/mongo/operation/indexes/command.rb +1 -16
- data/lib/mongo/operation/indexes/legacy.rb +4 -16
- data/lib/mongo/operation/list_collections/command.rb +1 -16
- data/lib/mongo/operation/map_reduce/command.rb +1 -16
- data/lib/mongo/operation/parallel_scan/command.rb +1 -16
- data/lib/mongo/operation/result.rb +3 -0
- data/lib/mongo/operation/shared/executable.rb +4 -0
- data/lib/mongo/operation/shared/polymorphic_lookup.rb +1 -1
- data/lib/mongo/operation/shared/polymorphic_result.rb +8 -1
- data/lib/mongo/operation/shared/result/aggregatable.rb +0 -5
- data/lib/mongo/operation/update/command.rb +2 -15
- data/lib/mongo/operation/update/legacy.rb +1 -16
- data/lib/mongo/operation/users_info/command.rb +1 -16
- data/lib/mongo/retryable.rb +22 -10
- data/lib/mongo/server.rb +10 -1
- data/lib/mongo/server/app_metadata.rb +7 -2
- data/lib/mongo/server/connectable.rb +0 -6
- data/lib/mongo/server/connection.rb +86 -135
- data/lib/mongo/server/connection_base.rb +133 -0
- data/lib/mongo/server/connection_pool.rb +11 -24
- data/lib/mongo/server/connection_pool/queue.rb +41 -41
- data/lib/mongo/server/description.rb +1 -1
- data/lib/mongo/server/monitor.rb +4 -4
- data/lib/mongo/server/monitor/connection.rb +26 -7
- data/lib/mongo/server/pending_connection.rb +36 -0
- data/lib/mongo/server_selector/selectable.rb +9 -1
- data/lib/mongo/session.rb +0 -1
- data/lib/mongo/socket.rb +23 -6
- data/lib/mongo/socket/ssl.rb +11 -18
- data/lib/mongo/socket/tcp.rb +13 -14
- data/lib/mongo/socket/unix.rb +9 -27
- data/lib/mongo/uri.rb +1 -1
- data/lib/mongo/version.rb +1 -1
- data/spec/integration/auth_spec.rb +160 -0
- data/spec/integration/retryable_writes_spec.rb +55 -58
- data/spec/integration/sdam_error_handling_spec.rb +115 -0
- data/spec/mongo/address/ipv4_spec.rb +4 -0
- data/spec/mongo/address/ipv6_spec.rb +4 -0
- data/spec/mongo/auth/scram/conversation_spec.rb +6 -5
- data/spec/mongo/auth/scram/negotiation_spec.rb +25 -36
- data/spec/mongo/auth/scram_spec.rb +2 -2
- data/spec/mongo/auth/user_spec.rb +97 -0
- data/spec/mongo/client_construction_spec.rb +1 -1
- data/spec/mongo/error/operation_failure_spec.rb +125 -1
- data/spec/mongo/retryable_spec.rb +17 -8
- data/spec/mongo/server/connection_pool/queue_spec.rb +24 -10
- data/spec/mongo/server/connection_pool_spec.rb +30 -117
- data/spec/mongo/server/connection_spec.rb +147 -25
- data/spec/mongo/server/description_spec.rb +0 -14
- data/spec/mongo/server/monitor/connection_spec.rb +22 -0
- data/spec/mongo/server_selector_spec.rb +1 -0
- data/spec/mongo/server_spec.rb +6 -6
- data/spec/mongo/socket/ssl_spec.rb +48 -116
- data/spec/mongo/socket/tcp_spec.rb +22 -0
- data/spec/mongo/socket/unix_spec.rb +9 -9
- data/spec/mongo/socket_spec.rb +15 -3
- data/spec/spec_tests/server_selection_spec.rb +2 -0
- data/spec/support/client_registry.rb +8 -2
- data/spec/support/common_shortcuts.rb +20 -1
- data/spec/support/constraints.rb +10 -2
- data/spec/support/lite_constraints.rb +8 -0
- data/spec/support/spec_config.rb +9 -1
- metadata +14 -4
- metadata.gz.sig +0 -0
data/lib/mongo/socket/tcp.rb
CHANGED
@@ -40,13 +40,14 @@ module Mongo
|
|
40
40
|
# @return [ TCP ] The connected socket instance.
|
41
41
|
#
|
42
42
|
# @since 2.0.0
|
43
|
-
def connect!
|
44
|
-
Timeout.timeout(connect_timeout, Error::SocketTimeoutError) do
|
43
|
+
def connect!
|
44
|
+
Timeout.timeout(options[:connect_timeout], Error::SocketTimeoutError) do
|
45
45
|
socket.setsockopt(IPPROTO_TCP, TCP_NODELAY, 1)
|
46
46
|
handle_errors { socket.connect(::Socket.pack_sockaddr_in(port, host)) }
|
47
47
|
self
|
48
48
|
end
|
49
49
|
end
|
50
|
+
private :connect!
|
50
51
|
|
51
52
|
# Initializes a new TCP socket.
|
52
53
|
#
|
@@ -58,23 +59,21 @@ module Mongo
|
|
58
59
|
# @param [ Integer ] port The port number.
|
59
60
|
# @param [ Float ] timeout The socket timeout value.
|
60
61
|
# @param [ Integer ] family The socket family.
|
62
|
+
# @param [ Hash ] options The options.
|
63
|
+
#
|
64
|
+
# @option options [ Float ] :connect_timeout Connect timeout.
|
61
65
|
#
|
62
66
|
# @since 2.0.0
|
63
|
-
def initialize(host, port, timeout, family)
|
64
|
-
@host, @port, @timeout = host, port, timeout
|
67
|
+
def initialize(host, port, timeout, family, options = {})
|
68
|
+
@host, @port, @timeout, @options = host, port, timeout, options
|
65
69
|
super(family)
|
70
|
+
connect!
|
66
71
|
end
|
67
72
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
#
|
73
|
-
# @return [ true, false ] If the socket is connectable.
|
74
|
-
#
|
75
|
-
# @since 2.2.5
|
76
|
-
def connectable?
|
77
|
-
true
|
73
|
+
private
|
74
|
+
|
75
|
+
def address
|
76
|
+
"#{host}:#{port} (no TLS)"
|
78
77
|
end
|
79
78
|
end
|
80
79
|
end
|
data/lib/mongo/socket/unix.rb
CHANGED
@@ -26,21 +26,6 @@ module Mongo
|
|
26
26
|
# @return [ Float ] timeout The socket timeout.
|
27
27
|
attr_reader :timeout
|
28
28
|
|
29
|
-
# Establishes a socket connection.
|
30
|
-
#
|
31
|
-
# @example Connect the socket.
|
32
|
-
# sock.connect!
|
33
|
-
#
|
34
|
-
# @note This method mutates the object by setting the socket
|
35
|
-
# internally.
|
36
|
-
#
|
37
|
-
# @return [ Unix ] The connected socket instance.
|
38
|
-
#
|
39
|
-
# @since 2.0.0
|
40
|
-
def connect!(connect_timeout = nil)
|
41
|
-
self
|
42
|
-
end
|
43
|
-
|
44
29
|
# Initializes a new Unix socket.
|
45
30
|
#
|
46
31
|
# @example Create the Unix socket.
|
@@ -48,24 +33,21 @@ module Mongo
|
|
48
33
|
#
|
49
34
|
# @param [ String ] path The path.
|
50
35
|
# @param [ Float ] timeout The socket timeout value.
|
36
|
+
# @param [ Hash ] options The options.
|
37
|
+
#
|
38
|
+
# @option options [ Float ] :connect_timeout Connect timeout (unused).
|
51
39
|
#
|
52
40
|
# @since 2.0.0
|
53
|
-
def initialize(path, timeout)
|
54
|
-
@path, @timeout = path, timeout
|
41
|
+
def initialize(path, timeout, options = {})
|
42
|
+
@path, @timeout, @options = path, timeout, options
|
55
43
|
@socket = ::UNIXSocket.new(path)
|
56
44
|
set_socket_options(@socket)
|
57
45
|
end
|
58
46
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
#
|
64
|
-
# @return [ true, false ] If the socket is connectable.
|
65
|
-
#
|
66
|
-
# @since 2.2.5
|
67
|
-
def connectable?
|
68
|
-
!!@socket
|
47
|
+
private
|
48
|
+
|
49
|
+
def address
|
50
|
+
path
|
69
51
|
end
|
70
52
|
end
|
71
53
|
end
|
data/lib/mongo/uri.rb
CHANGED
@@ -155,7 +155,7 @@ module Mongo
|
|
155
155
|
# @since 2.1.0
|
156
156
|
UNESCAPED_UNIX_SOCKET = "UNIX domain sockets must be urlencoded.".freeze
|
157
157
|
|
158
|
-
# Error details for a non-urlencoded auth
|
158
|
+
# Error details for a non-urlencoded auth databsae name.
|
159
159
|
#
|
160
160
|
# @since 2.1.0
|
161
161
|
UNESCAPED_DATABASE = "Auth database must be urlencoded.".freeze
|
data/lib/mongo/version.rb
CHANGED
@@ -0,0 +1,160 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Auth' do
|
4
|
+
describe 'Unauthorized exception message' do
|
5
|
+
let(:server) do
|
6
|
+
authorized_client.cluster.next_primary
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:connection) do
|
10
|
+
Mongo::Server::Connection.new(server, options)
|
11
|
+
end
|
12
|
+
|
13
|
+
before(:all) do
|
14
|
+
# If auth is configured, the test suite uses the configured user
|
15
|
+
# and does not create its own users. However, the configured user may
|
16
|
+
# not have the auth mechanisms we need. Therefore we create a user
|
17
|
+
# for this test without specifying auth mechanisms, which gets us
|
18
|
+
# server default (scram for 4.0, scram & scram256 for 4.2).
|
19
|
+
|
20
|
+
users = ClientRegistry.instance.global_client('root_authorized').use(:admin).database.users
|
21
|
+
unless users.info('existing_user').empty?
|
22
|
+
users.remove('existing_user')
|
23
|
+
end
|
24
|
+
users.create('existing_user', password: 'password')
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'user mechanism not provided' do
|
28
|
+
|
29
|
+
context 'user does not exist' do
|
30
|
+
let(:options) { SpecConfig.instance.ssl_options.merge(
|
31
|
+
user: 'nonexistent_user') }
|
32
|
+
|
33
|
+
before do
|
34
|
+
expect(connection.app_metadata.send(:document)[:saslSupportedMechs]).to eq('admin.nonexistent_user')
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'scram-sha-1 only server' do
|
38
|
+
min_server_fcv '3.0'
|
39
|
+
max_server_version '3.6'
|
40
|
+
|
41
|
+
it 'indicates scram-sha-1 was used' do
|
42
|
+
expect do
|
43
|
+
connection.connect!
|
44
|
+
end.to raise_error(Mongo::Auth::Unauthorized, 'User nonexistent_user (mechanism: scram) is not authorized to access admin (used mechanism: SCRAM-SHA-1)')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'scram-sha-256 server' do
|
49
|
+
min_server_fcv '4.0'
|
50
|
+
|
51
|
+
# An existing user on 4.0+ will negotiate scram-sha-256.
|
52
|
+
# A non-existing user on 4.0+ will negotiate scram-sha-1.
|
53
|
+
it 'indicates scram-sha-1 was used' do
|
54
|
+
expect do
|
55
|
+
connection.connect!
|
56
|
+
end.to raise_error(Mongo::Auth::Unauthorized, 'User nonexistent_user (mechanism: scram) is not authorized to access admin (used mechanism: SCRAM-SHA-1)')
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'user exists' do
|
62
|
+
let(:options) { SpecConfig.instance.ssl_options.merge(
|
63
|
+
user: 'existing_user', password: 'bogus') }
|
64
|
+
|
65
|
+
before do
|
66
|
+
expect(connection.app_metadata.send(:document)[:saslSupportedMechs]).to eq("admin.existing_user")
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'scram-sha-1 only server' do
|
70
|
+
min_server_fcv '3.0'
|
71
|
+
max_server_version '3.6'
|
72
|
+
|
73
|
+
it 'indicates scram-sha-1 was used' do
|
74
|
+
expect do
|
75
|
+
connection.connect!
|
76
|
+
end.to raise_error(Mongo::Auth::Unauthorized, "User existing_user (mechanism: scram) is not authorized to access admin (used mechanism: SCRAM-SHA-1)")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context 'scram-sha-256 server' do
|
81
|
+
min_server_fcv '4.0'
|
82
|
+
|
83
|
+
# An existing user on 4.0+ will negotiate scram-sha-256.
|
84
|
+
# A non-existing user on 4.0+ will negotiate scram-sha-1.
|
85
|
+
it 'indicates scram-sha-256 was used' do
|
86
|
+
expect do
|
87
|
+
connection.connect!
|
88
|
+
end.to raise_error(Mongo::Auth::Unauthorized, "User existing_user (mechanism: scram256) is not authorized to access admin (used mechanism: SCRAM-SHA-256)")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context 'user mechanism is provided' do
|
95
|
+
min_server_fcv '3.0'
|
96
|
+
|
97
|
+
context 'scram-sha-1 requested' do
|
98
|
+
let(:options) { SpecConfig.instance.ssl_options.merge(
|
99
|
+
user: 'nonexistent_user', auth_mech: :scram) }
|
100
|
+
|
101
|
+
it 'indicates scram-sha-1 was requested and used' do
|
102
|
+
expect do
|
103
|
+
connection.connect!
|
104
|
+
end.to raise_error(Mongo::Auth::Unauthorized, 'User nonexistent_user (mechanism: scram) is not authorized to access admin (used mechanism: SCRAM-SHA-1)')
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context 'scram-sha-256 requested' do
|
109
|
+
min_server_fcv '4.0'
|
110
|
+
|
111
|
+
let(:options) { SpecConfig.instance.ssl_options.merge(
|
112
|
+
user: 'nonexistent_user', auth_mech: :scram256) }
|
113
|
+
|
114
|
+
it 'indicates scram-sha-256 was requested and used' do
|
115
|
+
expect do
|
116
|
+
connection.connect!
|
117
|
+
end.to raise_error(Mongo::Auth::Unauthorized, 'User nonexistent_user (mechanism: scram256) is not authorized to access admin (used mechanism: SCRAM-SHA-256)')
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context 'attempting to connect to a non-tls server with tls' do
|
123
|
+
require_no_ssl
|
124
|
+
|
125
|
+
let(:options) { {ssl: true} }
|
126
|
+
|
127
|
+
it 'reports host, port and tls status' do
|
128
|
+
begin
|
129
|
+
connection.connect!
|
130
|
+
rescue Mongo::Error::SocketError => exc
|
131
|
+
end
|
132
|
+
expect(exc).not_to be nil
|
133
|
+
expect(exc.message).to include('OpenSSL::SSL::SSLError')
|
134
|
+
expect(exc.message).to include(server.address.to_s)
|
135
|
+
expect(exc.message).to include('TLS')
|
136
|
+
expect(exc.message).not_to include('no TLS')
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
context 'attempting to connect to a tls server without tls' do
|
141
|
+
require_ssl
|
142
|
+
|
143
|
+
let(:options) { {} }
|
144
|
+
|
145
|
+
it 'reports host, port and tls status' do
|
146
|
+
begin
|
147
|
+
connection.connect!
|
148
|
+
rescue Mongo::Error::SocketError => exc
|
149
|
+
end
|
150
|
+
expect(exc).not_to be nil
|
151
|
+
expect(exc.message).not_to include('OpenSSL::SSL::SSLError')
|
152
|
+
addresses = Socket.getaddrinfo(server.address.host, nil)
|
153
|
+
expect(addresses.any? do |address|
|
154
|
+
exc.message.include?("#{address[2]}:#{server.address.port}")
|
155
|
+
end).to be true
|
156
|
+
expect(exc.message).to include('no TLS')
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -1,5 +1,15 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
+
# The tests raise OperationFailure in socket reads. This is done for
|
4
|
+
# convenience to make the tests uniform between socket errors and operation
|
5
|
+
# failures; in reality a socket read will never raise OperationFailure as
|
6
|
+
# wire protocol parsing code raises this exception. For the purposes of
|
7
|
+
# testing retryable writes, it is acceptable to raise OperationFailure in
|
8
|
+
# socket reads because both exceptions end up getting handled in the same
|
9
|
+
# place by retryable writes code. The SDAM error handling test specifically
|
10
|
+
# checks server state (i.e. being marked unknown) and scanning behavior
|
11
|
+
# that is performed by the wire protocol code; this test omits scan assertions
|
12
|
+
# as otherwise it quickly becomes unwieldy.
|
3
13
|
describe 'Retryable writes integration tests' do
|
4
14
|
include PrimarySocket
|
5
15
|
|
@@ -7,21 +17,33 @@ describe 'Retryable writes integration tests' do
|
|
7
17
|
authorized_collection.delete_many
|
8
18
|
end
|
9
19
|
|
20
|
+
let(:check_collection) do
|
21
|
+
# Verify data in the collection using another client instance to avoid
|
22
|
+
# having the verification read trigger cluster scans on the writing client
|
23
|
+
subscribed_client[TEST_COLL]
|
24
|
+
end
|
25
|
+
|
26
|
+
let(:primary_connection) do
|
27
|
+
client.database.command(ping: 1)
|
28
|
+
expect(primary_server.pool.send(:queue).pool_size).to eq(1)
|
29
|
+
expect(primary_server.pool.send(:queue).queue_size).to eq(1)
|
30
|
+
primary_server.pool.send(:queue).queue.first
|
31
|
+
end
|
32
|
+
|
10
33
|
shared_examples_for 'an operation that is retried' do
|
11
34
|
|
12
|
-
context 'when the operation fails on the first attempt' do
|
35
|
+
context 'when the operation fails on the first attempt and succeeds on the second attempt' do
|
13
36
|
|
14
37
|
before do
|
15
|
-
|
16
|
-
|
17
|
-
|
38
|
+
wait_for_all_servers(client.cluster)
|
39
|
+
|
40
|
+
allow(primary_socket).to receive(:write).and_raise(error)
|
18
41
|
end
|
19
42
|
|
20
43
|
context 'when the error is retryable' do
|
21
44
|
|
22
45
|
before do
|
23
46
|
expect(Mongo::Logger.logger).to receive(:warn).once.and_call_original
|
24
|
-
expect(client.cluster).to receive(:scan!)
|
25
47
|
end
|
26
48
|
|
27
49
|
context 'when the error is a SocketError' do
|
@@ -54,6 +76,10 @@ describe 'Retryable writes integration tests' do
|
|
54
76
|
Mongo::Error::OperationFailure.new('not master')
|
55
77
|
end
|
56
78
|
|
79
|
+
let(:reply) do
|
80
|
+
make_not_master_reply
|
81
|
+
end
|
82
|
+
|
57
83
|
it 'retries writes' do
|
58
84
|
operation
|
59
85
|
expect(expectation).to eq(successful_retry_value)
|
@@ -66,13 +92,13 @@ describe 'Retryable writes integration tests' do
|
|
66
92
|
context 'when the error is a non-retryable OperationFailure' do
|
67
93
|
|
68
94
|
let(:error) do
|
69
|
-
Mongo::Error::OperationFailure.new('other error')
|
95
|
+
Mongo::Error::OperationFailure.new('other error', code: 123)
|
70
96
|
end
|
71
97
|
|
72
98
|
it 'does not retry writes' do
|
73
99
|
expect {
|
74
100
|
operation
|
75
|
-
}.to raise_error(error)
|
101
|
+
}.to raise_error(Mongo::Error::OperationFailure, /other error/)
|
76
102
|
expect(expectation).to eq(unsuccessful_retry_value)
|
77
103
|
end
|
78
104
|
end
|
@@ -82,8 +108,6 @@ describe 'Retryable writes integration tests' do
|
|
82
108
|
context 'when the operation fails on the first attempt and again on the second attempt' do
|
83
109
|
|
84
110
|
before do
|
85
|
-
# Note that for writes, server.connectable? is called, refreshing the socket
|
86
|
-
allow(primary_server).to receive(:connectable?).and_return(true)
|
87
111
|
allow(primary_socket).to receive(:write).and_raise(error)
|
88
112
|
end
|
89
113
|
|
@@ -138,9 +162,12 @@ describe 'Retryable writes integration tests' do
|
|
138
162
|
end
|
139
163
|
end
|
140
164
|
|
141
|
-
[
|
142
|
-
|
143
|
-
|
165
|
+
[
|
166
|
+
Mongo::Error::SocketError,
|
167
|
+
Mongo::Error::SocketTimeoutError,
|
168
|
+
Mongo::Error::OperationFailure.new('not master'),
|
169
|
+
Mongo::Error::OperationFailure.new('node is recovering'),
|
170
|
+
].each do |retryable_error|
|
144
171
|
|
145
172
|
context "when the first error is a #{retryable_error}" do
|
146
173
|
|
@@ -149,6 +176,7 @@ describe 'Retryable writes integration tests' do
|
|
149
176
|
end
|
150
177
|
|
151
178
|
before do
|
179
|
+
wait_for_all_servers(client.cluster)
|
152
180
|
bad_socket = primary_connection.address.socket(primary_connection.socket_timeout,
|
153
181
|
primary_connection.send(:ssl_options))
|
154
182
|
good_socket = primary_connection.address.socket(primary_connection.socket_timeout,
|
@@ -163,12 +191,6 @@ describe 'Retryable writes integration tests' do
|
|
163
191
|
Mongo::Error::SocketError
|
164
192
|
end
|
165
193
|
|
166
|
-
before do
|
167
|
-
# server selector can call scan! until it finds a server,
|
168
|
-
# hence more than two scan! calls may be issued
|
169
|
-
expect(client.cluster).to receive(:scan!).at_least(:twice).and_call_original
|
170
|
-
end
|
171
|
-
|
172
194
|
it 'does not retry writes and raises the second error' do
|
173
195
|
expect {
|
174
196
|
operation
|
@@ -179,12 +201,6 @@ describe 'Retryable writes integration tests' do
|
|
179
201
|
|
180
202
|
context 'when the second error is a SocketTimeoutError' do
|
181
203
|
|
182
|
-
before do
|
183
|
-
# server selector can call scan! until it finds a server,
|
184
|
-
# hence more than two scan! calls may be issued
|
185
|
-
expect(client.cluster).to receive(:scan!).at_least(:twice).and_call_original
|
186
|
-
end
|
187
|
-
|
188
204
|
let(:second_error) do
|
189
205
|
Mongo::Error::SocketTimeoutError
|
190
206
|
end
|
@@ -199,12 +215,6 @@ describe 'Retryable writes integration tests' do
|
|
199
215
|
|
200
216
|
context 'when the second error is a retryable OperationFailure' do
|
201
217
|
|
202
|
-
before do
|
203
|
-
# server selector can call scan! until it finds a server,
|
204
|
-
# hence more than two scan! calls may be issued
|
205
|
-
expect(client.cluster).to receive(:scan!).at_least(:twice).and_call_original
|
206
|
-
end
|
207
|
-
|
208
218
|
let(:second_error) do
|
209
219
|
Mongo::Error::OperationFailure.new('not master')
|
210
220
|
end
|
@@ -219,10 +229,6 @@ describe 'Retryable writes integration tests' do
|
|
219
229
|
|
220
230
|
context 'when the second error is a non-retryable OperationFailure' do
|
221
231
|
|
222
|
-
before do
|
223
|
-
expect(client.cluster).to receive(:scan!).once
|
224
|
-
end
|
225
|
-
|
226
232
|
let(:second_error) do
|
227
233
|
Mongo::Error::OperationFailure.new('other error')
|
228
234
|
end
|
@@ -260,10 +266,7 @@ describe 'Retryable writes integration tests' do
|
|
260
266
|
end
|
261
267
|
|
262
268
|
before do
|
263
|
-
|
264
|
-
allow(primary_server).to receive(:connectable?).and_return(true)
|
265
|
-
expect(primary_socket).to receive(:write).and_raise(Mongo::Error::SocketError)
|
266
|
-
expect(client.cluster).not_to receive(:scan!)
|
269
|
+
expect(primary_socket).to receive(:write).exactly(:once).and_raise(Mongo::Error::SocketError)
|
267
270
|
end
|
268
271
|
|
269
272
|
it 'does not retry writes' do
|
@@ -285,10 +288,7 @@ describe 'Retryable writes integration tests' do
|
|
285
288
|
end
|
286
289
|
|
287
290
|
before do
|
288
|
-
# Note that for writes, server.connectable? is called, refreshing the socket
|
289
|
-
allow(primary_server).to receive(:connectable?).and_return(true)
|
290
291
|
expect(primary_socket).to receive(:write).and_raise(Mongo::Error::SocketError)
|
291
|
-
expect(client.cluster).not_to receive(:scan!)
|
292
292
|
end
|
293
293
|
|
294
294
|
it 'does not retry writes' do
|
@@ -369,11 +369,7 @@ describe 'Retryable writes integration tests' do
|
|
369
369
|
context 'when the client has retry_writes set to false' do
|
370
370
|
|
371
371
|
let!(:client) do
|
372
|
-
|
373
|
-
end
|
374
|
-
|
375
|
-
after do
|
376
|
-
client.close
|
372
|
+
authorized_client_without_retry_writes
|
377
373
|
end
|
378
374
|
|
379
375
|
context 'when the collection has write concern acknowledged' do
|
@@ -405,6 +401,7 @@ describe 'Retryable writes integration tests' do
|
|
405
401
|
end
|
406
402
|
|
407
403
|
context 'when the client has retry_writes not set' do
|
404
|
+
require_no_retry_writes
|
408
405
|
|
409
406
|
let!(:client) do
|
410
407
|
authorized_client
|
@@ -446,7 +443,7 @@ describe 'Retryable writes integration tests' do
|
|
446
443
|
end
|
447
444
|
|
448
445
|
let(:expectation) do
|
449
|
-
|
446
|
+
check_collection.find(a: 1).count
|
450
447
|
end
|
451
448
|
|
452
449
|
let(:successful_retry_value) do
|
@@ -472,7 +469,7 @@ describe 'Retryable writes integration tests' do
|
|
472
469
|
end
|
473
470
|
|
474
471
|
let(:expectation) do
|
475
|
-
|
472
|
+
check_collection.find(a: 1).count
|
476
473
|
end
|
477
474
|
|
478
475
|
let(:successful_retry_value) do
|
@@ -498,7 +495,7 @@ describe 'Retryable writes integration tests' do
|
|
498
495
|
end
|
499
496
|
|
500
497
|
let(:expectation) do
|
501
|
-
|
498
|
+
check_collection.find(a: 1).count
|
502
499
|
end
|
503
500
|
|
504
501
|
let(:successful_retry_value) do
|
@@ -524,7 +521,7 @@ describe 'Retryable writes integration tests' do
|
|
524
521
|
end
|
525
522
|
|
526
523
|
let(:expectation) do
|
527
|
-
|
524
|
+
check_collection.find(a: 1).count
|
528
525
|
end
|
529
526
|
|
530
527
|
let(:successful_retry_value) do
|
@@ -550,7 +547,7 @@ describe 'Retryable writes integration tests' do
|
|
550
547
|
end
|
551
548
|
|
552
549
|
let(:expectation) do
|
553
|
-
|
550
|
+
check_collection.find(a: 1).count
|
554
551
|
end
|
555
552
|
|
556
553
|
let(:successful_retry_value) do
|
@@ -576,7 +573,7 @@ describe 'Retryable writes integration tests' do
|
|
576
573
|
end
|
577
574
|
|
578
575
|
let(:expectation) do
|
579
|
-
|
576
|
+
check_collection.find(a: 3).count
|
580
577
|
end
|
581
578
|
|
582
579
|
let(:successful_retry_value) do
|
@@ -602,7 +599,7 @@ describe 'Retryable writes integration tests' do
|
|
602
599
|
end
|
603
600
|
|
604
601
|
let(:expectation) do
|
605
|
-
|
602
|
+
check_collection.find(a: 1).count
|
606
603
|
end
|
607
604
|
|
608
605
|
let(:successful_retry_value) do
|
@@ -629,7 +626,7 @@ describe 'Retryable writes integration tests' do
|
|
629
626
|
end
|
630
627
|
|
631
628
|
let(:expectation) do
|
632
|
-
|
629
|
+
check_collection.find(a: 1).count
|
633
630
|
end
|
634
631
|
|
635
632
|
let(:unsuccessful_retry_value) do
|
@@ -652,7 +649,7 @@ describe 'Retryable writes integration tests' do
|
|
652
649
|
end
|
653
650
|
|
654
651
|
let(:expectation) do
|
655
|
-
|
652
|
+
check_collection.find(a: 1).count
|
656
653
|
end
|
657
654
|
|
658
655
|
let(:unsuccessful_retry_value) do
|
@@ -676,7 +673,7 @@ describe 'Retryable writes integration tests' do
|
|
676
673
|
end
|
677
674
|
|
678
675
|
let(:expectation) do
|
679
|
-
|
676
|
+
check_collection.find(a: 1).count
|
680
677
|
end
|
681
678
|
|
682
679
|
let(:successful_retry_value) do
|
@@ -703,7 +700,7 @@ describe 'Retryable writes integration tests' do
|
|
703
700
|
end
|
704
701
|
|
705
702
|
let(:expectation) do
|
706
|
-
|
703
|
+
check_collection.find(a: 1).count
|
707
704
|
end
|
708
705
|
|
709
706
|
let(:unsuccessful_retry_value) do
|
@@ -726,7 +723,7 @@ describe 'Retryable writes integration tests' do
|
|
726
723
|
end
|
727
724
|
|
728
725
|
let(:expectation) do
|
729
|
-
|
726
|
+
check_collection.find(a: 1).count
|
730
727
|
end
|
731
728
|
|
732
729
|
let(:unsuccessful_retry_value) do
|