mongo 2.7.2 → 2.8.0.rc0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|