mongo 2.11.3 → 2.11.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f1ec5495bf06a11fa0003454eb892099bc1a86da8ef1694fc25990c18d2f64ba
4
- data.tar.gz: b07b2a3f5b71e89e66d6117253e1c85e4c6c90b8a8bb2f17cee24a8b47b9dad8
3
+ metadata.gz: d3363520c62689e2f649389acd84f68b46dba99a62d82c174c77414f66e055dd
4
+ data.tar.gz: 7b1a54bcc8657047ec15b61cdf39588965fc9328d0ac56aa294f7891ef7fb3e2
5
5
  SHA512:
6
- metadata.gz: 6e88a5e7d0a2bd2917c4ba887d38786229fd9f64e2056b894ccd3289983b52f7aa8b293d77c46df4412d1fb01faed8bb41bfe93fa08e5085a8ec88fe22aaed34
7
- data.tar.gz: fe47a5b889ff2b9449475e040d6e189bc984158e5c6b0cfab055dcce83d7efb090ec129eb5afee5cfed7a4b30ddda6bcc0d60e1b8720ff96f188548f28a75460
6
+ metadata.gz: 546bfab9a6042f6194f87efc8f19fa92ee9828040d1f34a98045e243cce6f056837e4144856a37803f964cf345724ccfdeb6eb2d407f265b4a02e7a14622f0a1
7
+ data.tar.gz: 9eb09ae2c44b3cee7cd9c082af7702b0acd4b56674a6c17b3959c05f654987b3b34f6d87c8d74d9863f312addc01a8de090899a879847d014fa0e7c9baa217d6
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -66,6 +66,8 @@ module Mongo
66
66
  # @param [ String ] seed The provided address.
67
67
  # @param [ Hash ] options The address options.
68
68
  #
69
+ # @option options [ Float ] :connect_timeout Connect timeout.
70
+ #
69
71
  # @since 2.0.0
70
72
  def initialize(seed, options = {})
71
73
  if seed.nil?
@@ -85,6 +87,9 @@ module Mongo
85
87
  # @return [ Integer ] port The port.
86
88
  attr_reader :port
87
89
 
90
+ # @api private
91
+ attr_reader :options
92
+
88
93
  # Check equality of the address to another.
89
94
  #
90
95
  # @example Check address equality.
@@ -138,13 +143,30 @@ module Mongo
138
143
  "#<Mongo::Address:0x#{object_id} address=#{to_s}>"
139
144
  end
140
145
 
141
- # Get a socket for the provided address, given the options.
146
+ # Get a socket for the address stored in this object, given the options.
147
+ #
148
+ # If the address stored in this object looks like a Unix path, this method
149
+ # returns a Unix domain socket for this path.
142
150
  #
143
- # The address the socket connects to is determined by the algorithm described in the
144
- # #intialize_resolver! documentation. Each time this method is called, #initialize_resolver!
145
- # will be called, meaning that a new hostname lookup will occur. This is done so that any
146
- # changes to which addresses the hostname resolves to will be picked up even if a socket has
147
- # been connected to it before.
151
+ # Otherwise, this method attempts to resolve the address stored in
152
+ # this object to IPv4 and IPv6 addresses using +Socket#getaddrinfo+, then
153
+ # connects to the resulting addresses and returns the socket of the first
154
+ # successful connection. The order in which address families (IPv4/IPV6)
155
+ # are tried is the same order in which the addresses are returned by
156
+ # +getaddrinfo+, and is determined by the host system.
157
+ #
158
+ # Name resolution is performed on each +socket+ call. This is done so that
159
+ # any changes to which addresses the host names used as seeds or in
160
+ # server configuration resolve to are immediately noticed by the driver,
161
+ # even if a socket has been connected to the affected host name/address
162
+ # before. However, note that DNS TTL values may still affect when a change
163
+ # to a host address is noticed by the driver.
164
+ #
165
+ # This method propagates any exceptions raised during DNS resolution and
166
+ # subsequent connection attempts. In case of a host name resolving to
167
+ # multiple IP addresses, the error raised by the last attempt is propagated
168
+ # to the caller. This method does not map exceptions to Mongo::Error
169
+ # subclasses, and may raise any subclass of Exception.
148
170
  #
149
171
  # @example Get a socket.
150
172
  # address.socket(5, :ssl => true)
@@ -155,11 +177,34 @@ module Mongo
155
177
  #
156
178
  # @option options [ Float ] :connect_timeout Connect timeout.
157
179
  #
158
- # @return [ Mongo::Socket::SSL, Mongo::Socket::TCP, Mongo::Socket::Unix ] The socket.
180
+ # @return [ Mongo::Socket::SSL | Mongo::Socket::TCP | Mongo::Socket::Unix ]
181
+ # The socket.
182
+ #
183
+ # @raise [ Exception ] If network connection failed.
159
184
  #
160
185
  # @since 2.0.0
161
186
  def socket(socket_timeout, ssl_options = {}, options = {})
162
- create_resolver(ssl_options).socket(socket_timeout, ssl_options, options)
187
+ if seed.downcase =~ Unix::MATCH
188
+ specific_address = Unix.new(seed.downcase)
189
+ return specific_address.socket(socket_timeout, ssl_options, options)
190
+ end
191
+
192
+ options = {
193
+ connect_timeout: Server::CONNECT_TIMEOUT,
194
+ }.update(options)
195
+
196
+ family = (host == LOCALHOST) ? ::Socket::AF_INET : ::Socket::AF_UNSPEC
197
+ error = nil
198
+ ::Socket.getaddrinfo(host, nil, family, ::Socket::SOCK_STREAM).each do |info|
199
+ begin
200
+ specific_address = FAMILY_MAP[info[4]].new(info[3], port, host)
201
+ socket = specific_address.socket(socket_timeout, ssl_options, options)
202
+ return socket
203
+ rescue IOError, SystemCallError, Error::SocketTimeoutError, Error::SocketError => e
204
+ error = e
205
+ end
206
+ end
207
+ raise error
163
208
  end
164
209
 
165
210
  # Get the address as a string.
@@ -182,37 +227,8 @@ module Mongo
182
227
  end
183
228
  end
184
229
 
185
- # @api private
186
- def connect_timeout
187
- @connect_timeout ||= @options[:connect_timeout] || Server::CONNECT_TIMEOUT
188
- end
189
-
190
230
  private
191
231
 
192
- # To determine which address the socket will connect to, the driver will
193
- # attempt to connect to each IP address returned by Socket::getaddrinfo in
194
- # sequence. Once a successful connection is made, a resolver with that
195
- # IP address specified is returned. If no successful connection is
196
- # made, the error made by the last connection attempt is raised.
197
- def create_resolver(ssl_options)
198
- return Unix.new(seed.downcase) if seed.downcase =~ Unix::MATCH
199
-
200
- family = (host == LOCALHOST) ? ::Socket::AF_INET : ::Socket::AF_UNSPEC
201
- error = nil
202
- ::Socket.getaddrinfo(host, nil, family, ::Socket::SOCK_STREAM).each do |info|
203
- begin
204
- specific_address = FAMILY_MAP[info[4]].new(info[3], port, host)
205
- socket = specific_address.socket(
206
- connect_timeout, ssl_options, connect_timeout: connect_timeout)
207
- socket.close
208
- return specific_address
209
- rescue IOError, SystemCallError, Error::SocketTimeoutError, Error::SocketError => e
210
- error = e
211
- end
212
- end
213
- raise error
214
- end
215
-
216
232
  def parse_host_port
217
233
  address = seed.downcase
218
234
  case address
@@ -187,8 +187,7 @@ module Mongo
187
187
 
188
188
  # Separate method to permit easier mocking in the test suite.
189
189
  def do_connect
190
- socket = address.socket(socket_timeout, ssl_options,
191
- connect_timeout: address.connect_timeout)
190
+ socket = address.socket(socket_timeout, ssl_options, address.options)
192
191
 
193
192
  begin
194
193
  handshake!(socket)
@@ -162,8 +162,7 @@ module Mongo
162
162
  # @since 2.0.0
163
163
  def connect!
164
164
  unless @socket
165
- socket = address.socket(socket_timeout, ssl_options,
166
- connect_timeout: address.connect_timeout)
165
+ socket = address.socket(socket_timeout, ssl_options, address.options)
167
166
  handshake!(socket)
168
167
  @socket = socket
169
168
  end
@@ -63,7 +63,16 @@ module Mongo
63
63
  def alive?
64
64
  sock_arr = [ @socket ]
65
65
  if Kernel::select(sock_arr, nil, sock_arr, 0)
66
- eof?
66
+ # The eof? call is supposed to return immediately since select
67
+ # indicated the socket is readable. However, if @socket is an SSL
68
+ # socket, eof? can block anyway - see RUBY-2140.
69
+ begin
70
+ Timeout.timeout(0.1) do
71
+ eof?
72
+ end
73
+ rescue ::Timeout::Error
74
+ true
75
+ end
67
76
  else
68
77
  true
69
78
  end
@@ -17,5 +17,5 @@ module Mongo
17
17
  # The current version of the driver.
18
18
  #
19
19
  # @since 2.0.0
20
- VERSION = '2.11.3'.freeze
20
+ VERSION = '2.11.4'.freeze
21
21
  end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Symbol encoding to BSON' do
4
+ let(:value) { :foo }
5
+
6
+ let(:hash) do
7
+ {'foo' => value}
8
+ end
9
+
10
+ let(:serialized) do
11
+ hash.to_bson.to_s
12
+ end
13
+
14
+ let(:expected) do
15
+ "\x12\x00\x00\x00\x0Efoo\x00\x04\x00\x00\x00foo\x00\x00".force_encoding('binary')
16
+ end
17
+
18
+ it 'encodes symbol to BSON symbol' do
19
+ serialized.should == expected
20
+ end
21
+
22
+ it 'round-trips symbol values' do
23
+ buffer = BSON::ByteBuffer.new(serialized)
24
+ Hash.from_bson(buffer).should == hash
25
+ end
26
+
27
+ it 'round-trips symbol values using the same byte buffer' do
28
+ if BSON::Environment.jruby?
29
+ pending 'https://jira.mongodb.org/browse/RUBY-2128'
30
+ end
31
+
32
+ Hash.from_bson(hash.to_bson).should == hash
33
+ end
34
+ end
@@ -12,17 +12,18 @@ describe 'Connections' do
12
12
  let(:server) { client.cluster.servers.first }
13
13
 
14
14
  describe '#connect!' do
15
- # On JRuby 9.2.7.0, this line:
16
- # expect_any_instance_of(Mongo::Socket).to receive(:write).and_raise(exception)
17
- # ... appears to produce a moment in which Mongo::Socket#write is undefined
18
- # entirely, resulting in this failure:
19
- # RSpec::Expectations::ExpectationNotMetError: expected Mongo::Error::SocketError, got #<NameError: undefined method `write' for class `Mongo::Socket'>
20
- fails_on_jruby
15
+
16
+ let(:connection) do
17
+ Mongo::Server::Connection.new(server, server.options)
18
+ end
21
19
 
22
20
  context 'network error during handshake' do
23
- let(:connection) do
24
- Mongo::Server::Connection.new(server, server.options)
25
- end
21
+ # On JRuby 9.2.7.0, this line:
22
+ # expect_any_instance_of(Mongo::Socket).to receive(:write).and_raise(exception)
23
+ # ... appears to produce a moment in which Mongo::Socket#write is undefined
24
+ # entirely, resulting in this failure:
25
+ # RSpec::Expectations::ExpectationNotMetError: expected Mongo::Error::SocketError, got #<NameError: undefined method `write' for class `Mongo::Socket'>
26
+ fails_on_jruby
26
27
 
27
28
  let(:exception) { Mongo::Error::SocketError }
28
29
 
@@ -120,6 +121,53 @@ describe 'Connections' do
120
121
  expect(client.cluster.topology).to be_a(Mongo::Cluster::Topology::ReplicaSetNoPrimary)
121
122
  end
122
123
  end
124
+
125
+ describe 'number of sockets created' do
126
+
127
+ before do
128
+ server
129
+ end
130
+
131
+ shared_examples_for 'is 1 per connection' do
132
+ it 'is 1 per connection' do
133
+ # Instantiating a connection object should not create any sockets
134
+ RSpec::Mocks.with_temporary_scope do
135
+ expect(socket_cls).not_to receive(:new)
136
+
137
+ connection
138
+ end
139
+
140
+ # When the connection connects, exactly one socket should be created
141
+ # (and subsequently connected)
142
+ RSpec::Mocks.with_temporary_scope do
143
+ expect(socket_cls).to receive(:new).and_call_original
144
+
145
+ connection.connect!
146
+ end
147
+ end
148
+ end
149
+
150
+ let(:socket_cls) { ::Socket }
151
+
152
+ it_behaves_like 'is 1 per connection'
153
+
154
+ context 'connection to Unix domain socket' do
155
+ # Server does not allow Unix socket connections when TLS is enabled
156
+ require_no_tls
157
+
158
+ let(:port) { SpecConfig.instance.any_port }
159
+
160
+ let(:client) do
161
+ new_local_client(["/tmp/mongodb-#{port}.sock"], connect: :direct).tap do |client|
162
+ stop_monitoring(client)
163
+ end
164
+ end
165
+
166
+ let(:socket_cls) { ::UNIXSocket }
167
+
168
+ it_behaves_like 'is 1 per connection'
169
+ end
170
+ end
123
171
  end
124
172
 
125
173
  describe 'wire protocol version range update' do
@@ -24,7 +24,7 @@ describe 'Failing retryable operations' do
24
24
  end
25
25
 
26
26
  after do
27
- ClusterTools.instance.direct_client_for_each_server do |client|
27
+ ClusterTools.instance.direct_client_for_each_data_bearing_server do |client|
28
28
  client.use(:admin).database.command(clear_fail_point_command)
29
29
  end
30
30
  end
@@ -62,7 +62,7 @@ describe 'Failing retryable operations' do
62
62
  server.monitor.stop!
63
63
  end
64
64
 
65
- ClusterTools.instance.direct_client_for_each_server do |client|
65
+ ClusterTools.instance.direct_client_for_each_data_bearing_server do |client|
66
66
  client.use(:admin).database.command(fail_point_command)
67
67
  end
68
68
  end
@@ -249,11 +249,7 @@ describe Mongo::Address do
249
249
  end
250
250
  end
251
251
 
252
- context 'when creating a socket using the resolver' do
253
-
254
- before do
255
- address.send(:create_resolver, SpecConfig.instance.ssl_options)
256
- end
252
+ context 'when creating a socket' do
257
253
 
258
254
  it 'uses the host, not the IP address' do
259
255
  expect(address.socket(0.0).host).to eq(socket_address_or_host)
@@ -285,6 +281,24 @@ describe Mongo::Address do
285
281
  end
286
282
  end
287
283
  end
284
+
285
+ describe ':connect_timeout option' do
286
+ clean_slate
287
+
288
+ let(:address) { Mongo::Address.new('127.0.0.1') }
289
+
290
+ it 'defaults to 10' do
291
+ RSpec::Mocks.with_temporary_scope do
292
+ resolved_address = double('address')
293
+ # This test's expectation
294
+ expect(resolved_address).to receive(:socket).with(0, {}, connect_timeout: 10)
295
+
296
+ expect(Mongo::Address::IPv4).to receive(:new).and_return(resolved_address)
297
+
298
+ address.socket(0)
299
+ end
300
+ end
301
+ end
288
302
  end
289
303
 
290
304
  describe '#to_s' do
@@ -320,12 +334,4 @@ describe Mongo::Address do
320
334
  end
321
335
  end
322
336
  end
323
-
324
- describe '#connect_timeout' do
325
- let(:address) { Mongo::Address.new('127.0.0.1') }
326
-
327
- it 'defaults to 10' do
328
- expect(address.send(:connect_timeout)).to eq(10)
329
- end
330
- end
331
337
  end
@@ -35,6 +35,8 @@ describe Mongo::Auth::SCRAM::Conversation, retry: 3 do
35
35
  end
36
36
 
37
37
  describe '#start' do
38
+ # Test uses global assertions
39
+ clean_slate
38
40
 
39
41
  let(:query) do
40
42
  conversation.start(nil)
@@ -278,9 +278,9 @@ describe Mongo::Collection do
278
278
  require_topology :replica_set
279
279
 
280
280
  let(:client_options) do
281
- {
281
+ SpecConfig.instance.auth_options.merge(
282
282
  read: { mode: :primary_preferred },
283
- }
283
+ )
284
284
  end
285
285
 
286
286
  let(:subscriber) { EventSubscriber.new }
@@ -1080,7 +1080,7 @@ describe Mongo::Server::Connection, retry: 3 do
1080
1080
  end
1081
1081
 
1082
1082
  it 'uses the connect_timeout for the address' do
1083
- expect(connection.address.send(:connect_timeout)).to eq(3)
1083
+ expect(connection.address.options[:connect_timeout]).to eq(3)
1084
1084
  end
1085
1085
 
1086
1086
  it 'uses the socket_timeout as the socket_timeout' do
@@ -1099,7 +1099,7 @@ describe Mongo::Server::Connection, retry: 3 do
1099
1099
  end
1100
1100
 
1101
1101
  it 'uses the connect_timeout for the address' do
1102
- expect(connection.address.send(:connect_timeout)).to eq(3)
1102
+ expect(connection.address.options[:connect_timeout]).to eq(3)
1103
1103
  end
1104
1104
 
1105
1105
  it 'does not use a socket_timeout' do
@@ -1120,8 +1120,8 @@ describe Mongo::Server::Connection, retry: 3 do
1120
1120
  connection.connect!
1121
1121
  end
1122
1122
 
1123
- it 'uses the default connect_timeout for the address' do
1124
- expect(connection.address.send(:connect_timeout)).to eq(10)
1123
+ it 'does not specify connect_timeout for the address' do
1124
+ expect(connection.address.options[:connect_timeout]).to be nil
1125
1125
  end
1126
1126
 
1127
1127
  it 'uses the socket_timeout' do
@@ -1139,8 +1139,8 @@ describe Mongo::Server::Connection, retry: 3 do
1139
1139
  connection.connect!
1140
1140
  end
1141
1141
 
1142
- it 'uses the default connect_timeout for the address' do
1143
- expect(connection.address.send(:connect_timeout)).to eq(10)
1142
+ it 'does not specify connect_timeout for the address' do
1143
+ expect(connection.address.options[:connect_timeout]).to be nil
1144
1144
  end
1145
1145
 
1146
1146
  it 'does not use a socket_timeout' do
@@ -66,7 +66,7 @@ describe Mongo::Server::Monitor::Connection do
66
66
  end
67
67
 
68
68
  it 'uses the connect_timeout for the address' do
69
- expect(connection.address.send(:connect_timeout)).to eq(3)
69
+ expect(connection.address.options[:connect_timeout]).to eq(3)
70
70
  end
71
71
 
72
72
  it 'uses the connect_timeout as the socket_timeout' do
@@ -81,7 +81,7 @@ describe Mongo::Server::Monitor::Connection do
81
81
  end
82
82
 
83
83
  it 'uses the connect_timeout for the address' do
84
- expect(connection.address.send(:connect_timeout)).to eq(3)
84
+ expect(connection.address.options[:connect_timeout]).to eq(3)
85
85
  end
86
86
 
87
87
  it 'uses the connect_timeout as the socket_timeout' do
@@ -98,8 +98,8 @@ describe Mongo::Server::Monitor::Connection do
98
98
  SpecConfig.instance.test_options.merge(connect_timeout: nil, socket_timeout: 5)
99
99
  end
100
100
 
101
- it 'uses the default connect_timeout for the address' do
102
- expect(connection.address.send(:connect_timeout)).to eq(10)
101
+ it 'does not specify connect_timeout for the address' do
102
+ expect(connection.address.options[:connect_timeout]).to be nil
103
103
  end
104
104
 
105
105
  it 'uses the connect_timeout as the socket_timeout' do
@@ -113,8 +113,8 @@ describe Mongo::Server::Monitor::Connection do
113
113
  SpecConfig.instance.test_options.merge(connect_timeout: nil, socket_timeout: nil)
114
114
  end
115
115
 
116
- it 'uses the default connect_timeout for the address' do
117
- expect(connection.address.send(:connect_timeout)).to eq(10)
116
+ it 'does not specify connect_timeout for the address' do
117
+ expect(connection.address.options[:connect_timeout]).to be nil
118
118
  end
119
119
 
120
120
  it 'uses the connect_timeout as the socket_timeout' do