mongo 2.19.0 → 2.19.2

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: '073817a77e166171b32b6531b111a273bd327d754e65e0b10c8e21d750afe861'
4
- data.tar.gz: a133671749199aa848a5ba068fc586922bb4e1494e33eec976240e488e7fa66a
3
+ metadata.gz: 455d5b9685fd2cefa18a1c37e81ddb97a177ac8e40b9298945e4f126ac9ce093
4
+ data.tar.gz: fab0630edde803ab5125536159e8fbd12d3f0bac233e87d40ea1e414287145ac
5
5
  SHA512:
6
- metadata.gz: d7aeab7c0494cab55b75f3c1b7ab1ec8c5e301cf6a29d8eb927c3176106d9885d67898d3c0b9a8d494f4546b3db3bbd6bd316398d85aa30edeebbb0335461955
7
- data.tar.gz: cd5e5d4fd5de2b2503b8ff15e8408ad41e08e2a37f3547152e33742b385908956dae63bffdffda88f05456a5e43bc57c2f6086d5391074ea7e1724ea2a7fad1b
6
+ metadata.gz: f3db945520bf825c31fcd7a21295b03257b1ee01d3d2eb6903f69ae6dfdbb0834516c8214227196702f88e087754780a876941eb5a3ac8f0861bff40a3e55a07
7
+ data.tar.gz: 73ea3f0a4fc86ba9922c1054f925db03eab8ac3da47bd9b9daae5006af1f32299c3d1a45104623126db5c89e41bf3ddbfe82dd641ea77dce6d8936a5297a1880
checksums.yaml.gz.sig CHANGED
Binary file
data/lib/mongo/client.rb CHANGED
@@ -71,6 +71,7 @@ module Mongo
71
71
  :local_threshold,
72
72
  :logger,
73
73
  :log_prefix,
74
+ :max_connecting,
74
75
  :max_idle_time,
75
76
  :max_pool_size,
76
77
  :max_read_retries,
@@ -266,6 +267,12 @@ module Mongo
266
267
  # @option options [ String ] :log_prefix A custom log prefix to use when
267
268
  # logging. This option is experimental and subject to change in a future
268
269
  # version of the driver.
270
+ # @option options [ Integer ] :max_connecting The maximum number of
271
+ # connections that can be connecting simultaneously. The default is 2.
272
+ # This option should be increased if there are many threads that share
273
+ # the same client and the application is experiencing timeouts
274
+ # while waiting for connections to be established.
275
+ # selecting a server for an operation. The default is 2.
269
276
  # @option options [ Integer ] :max_idle_time The maximum seconds a socket can remain idle
270
277
  # since it has been checked in to the pool.
271
278
  # @option options [ Integer ] :max_pool_size The maximum size of the
@@ -1318,6 +1325,7 @@ module Mongo
1318
1325
  key = k.to_sym
1319
1326
  if VALID_OPTIONS.include?(key)
1320
1327
  validate_max_min_pool_size!(key, opts)
1328
+ validate_max_connecting!(key, opts)
1321
1329
  validate_read!(key, opts)
1322
1330
  if key == :compressors
1323
1331
  compressors = valid_compressors(v)
@@ -1580,6 +1588,23 @@ module Mongo
1580
1588
  true
1581
1589
  end
1582
1590
 
1591
+ # Validates whether the max_connecting option is valid.
1592
+ #
1593
+ # @param [ Symbol ] option The option to validate.
1594
+ # @param [ Hash ] opts The client options.
1595
+ #
1596
+ # @return [ true ] If the option is valid.
1597
+ # @raise [ Error::InvalidMaxConnecting ] If the option is invalid.
1598
+ def validate_max_connecting!(option, opts)
1599
+ if option == :max_connecting && opts.key?(:max_connecting)
1600
+ max_connecting = opts[:max_connecting] || Server::ConnectionPool::DEFAULT_MAX_CONNECTING
1601
+ if max_connecting <= 0
1602
+ raise Error::InvalidMaxConnecting.new(opts[:max_connecting])
1603
+ end
1604
+ end
1605
+ true
1606
+ end
1607
+
1583
1608
  def validate_read!(option, opts)
1584
1609
  if option == :read && opts.has_key?(:read)
1585
1610
  read = opts[:read]
@@ -185,6 +185,8 @@ module Mongo
185
185
  collection.client.log_warn("The :oplog_replay option is deprecated and ignored by MongoDB 4.4 and later")
186
186
  end
187
187
 
188
+ maybe_set_tailable_options(spec)
189
+
188
190
  if explained?
189
191
  spec[:explain] = options[:explain]
190
192
  Operation::Explain.new(spec)
@@ -200,6 +202,19 @@ module Mongo
200
202
  def use_query_cache?
201
203
  QueryCache.enabled? && !collection.system_collection?
202
204
  end
205
+
206
+ # Add tailable cusror options to the command specifiction if needed.
207
+ #
208
+ # @param [ Hash ] spec The command specification.
209
+ def maybe_set_tailable_options(spec)
210
+ case cursor_type
211
+ when :tailable
212
+ spec[:tailable] = true
213
+ when :tailable_await
214
+ spec[:tailable] = true
215
+ spec[:await_data] = true
216
+ end
217
+ end
203
218
  end
204
219
  end
205
220
  end
@@ -127,6 +127,7 @@ module Mongo
127
127
  # return in each response from MongoDB.
128
128
  # @option options [ Hash ] :collation The collation to use.
129
129
  # @option options [ String ] :comment Associate a comment with the query.
130
+ # @option options [ :tailable, :tailable_await ] :cursor_type The type of cursor to use.
130
131
  # @option options [ Hash ] :explain Execute an explain with the provided
131
132
  # explain options (known options are :verbose and :verbosity) rather
132
133
  # than a find.
@@ -314,7 +314,9 @@ module Mongo
314
314
  #
315
315
  # @since 2.0.0
316
316
  def capped?
317
- database.read_command(:collstats => name).documents[0][CAPPED]
317
+ database.list_collections(filter: { name: name })
318
+ .first
319
+ &.dig('options', CAPPED) || false
318
320
  end
319
321
 
320
322
  # Force the collection to be created in the database.
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2014-present MongoDB Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ module Mongo
18
+ class Error
19
+ # Exception that is raised when trying to create a client with an invalid
20
+ # max_connecting option.
21
+ class InvalidMaxConnecting < Error
22
+ # Instantiate the new exception.
23
+ def initialize(max_connecting)
24
+ super("Invalid max_connecting: #{max_connecting}. Please ensure that it is greater than zero. ")
25
+ end
26
+ end
27
+ end
28
+ end
data/lib/mongo/error.rb CHANGED
@@ -159,6 +159,7 @@ require 'mongo/error/invalid_database_name'
159
159
  require 'mongo/error/invalid_document'
160
160
  require 'mongo/error/invalid_file'
161
161
  require 'mongo/error/invalid_file_revision'
162
+ require 'mongo/error/invalid_max_connecting'
162
163
  require 'mongo/error/invalid_min_pool_size'
163
164
  require 'mongo/error/invalid_read_option'
164
165
  require 'mongo/error/invalid_application_name'
@@ -30,7 +30,6 @@ module Mongo
30
30
 
31
31
  READ_COMMANDS = [
32
32
  :aggregate,
33
- :collStats,
34
33
  :count,
35
34
  :dbStats,
36
35
  :distinct,
@@ -29,12 +29,16 @@ module Mongo
29
29
  # The default max size for the connection pool.
30
30
  #
31
31
  # @since 2.9.0
32
- DEFAULT_MAX_SIZE = 20.freeze
32
+ DEFAULT_MAX_SIZE = 20
33
33
 
34
34
  # The default min size for the connection pool.
35
35
  #
36
36
  # @since 2.9.0
37
- DEFAULT_MIN_SIZE = 0.freeze
37
+ DEFAULT_MIN_SIZE = 0
38
+
39
+ # The default maximum number of connections that can be connecting at
40
+ # any given time.
41
+ DEFAULT_MAX_CONNECTING = 2
38
42
 
39
43
  # The default timeout, in seconds, to wait for a connection.
40
44
  #
@@ -61,6 +65,11 @@ module Mongo
61
65
  #
62
66
  # @option options [ Integer ] :max_size The maximum pool size. Setting
63
67
  # this option to zero creates an unlimited connection pool.
68
+ # @option options [ Integer ] :max_connecting The maximum number of
69
+ # connections that can be connecting simultaneously. The default is 2.
70
+ # This option should be increased if there are many threads that share
71
+ # same connection pool and the application is experiencing timeouts
72
+ # while waiting for connections to be established.
64
73
  # @option options [ Integer ] :max_pool_size Deprecated.
65
74
  # The maximum pool size. If max_size is also given, max_size and
66
75
  # max_pool_size must be identical.
@@ -89,6 +98,7 @@ module Mongo
89
98
  # any connections created by the pool.
90
99
  #
91
100
  # @since 2.0.0, API changed in 2.9.0
101
+
92
102
  def initialize(server, options = {})
93
103
  unless server.is_a?(Server)
94
104
  raise ArgumentError, 'First argument must be a Server instance'
@@ -146,7 +156,7 @@ module Mongo
146
156
  # Condition variable to enforce the first check in check_out: max_pool_size.
147
157
  # This condition variable should be signaled when the number of
148
158
  # unavailable connections decreases (pending + pending_connections +
149
- # available_connections).
159
+ # checked_out_connections).
150
160
  @size_cv = Mongo::ConditionVariable.new(@lock)
151
161
  # This represents the number of threads that have made it past the size_cv
152
162
  # gate but have not acquired a connection to add to the pending_connections
@@ -157,7 +167,7 @@ module Mongo
157
167
  # Thei condition variable should be signaled when the number of pending
158
168
  # connections decreases.
159
169
  @max_connecting_cv = Mongo::ConditionVariable.new(@lock)
160
- @max_connecting = options.fetch(:max_connecting, 2)
170
+ @max_connecting = options.fetch(:max_connecting, DEFAULT_MAX_CONNECTING)
161
171
 
162
172
  ObjectSpace.define_finalizer(self, self.class.finalize(@available_connections, @pending_connections, @populator))
163
173
 
@@ -1074,7 +1084,8 @@ module Mongo
1074
1084
  "from pool for #{@server.address}#{connection_global_id_msg} after #{wait_timeout} sec. " +
1075
1085
  "Connections in pool: #{@available_connections.length} available, " +
1076
1086
  "#{@checked_out_connections.length} checked out, " +
1077
- "#{@pending_connections.length + @connection_requests} pending " +
1087
+ "#{@pending_connections.length} pending, " +
1088
+ "#{@connection_requests} connections requests " +
1078
1089
  "(max size: #{max_size})"
1079
1090
  raise Error::ConnectionCheckOutTimeout.new(msg, address: @server.address)
1080
1091
  end
@@ -1170,7 +1181,6 @@ module Mongo
1170
1181
  # one. If no connection exists and the pool is at max size, wait until
1171
1182
  # a connection is checked back into the pool.
1172
1183
  #
1173
- # @param [ Float ] deadline The deadline to get the connection.
1174
1184
  # @param [ Integer ] pid The current process id.
1175
1185
  # @param [ Integer ] connection_global_id The global id for the
1176
1186
  # connection to check out.
@@ -1180,17 +1190,22 @@ module Mongo
1180
1190
  # @raise [ Error::PoolClosedError ] If the pool has been closed.
1181
1191
  # @raise [ Timeout::Error ] If the connection pool is at maximum size
1182
1192
  # and remains so for longer than the wait timeout.
1183
- def get_connection(deadline, pid, connection_global_id)
1193
+ def get_connection(pid, connection_global_id)
1184
1194
  if connection = next_available_connection(connection_global_id)
1185
1195
  unless valid_available_connection?(connection, pid, connection_global_id)
1186
1196
  return nil
1187
1197
  end
1188
1198
 
1199
+ # We've got a connection, so we decrement the number of connection
1200
+ # requests.
1201
+ # We do not need to signal condition variable here, because
1202
+ # because the execution will continue, and we signal later.
1203
+ @connection_requests -= 1
1204
+
1189
1205
  # If the connection is connected, it's not considered a
1190
1206
  # "pending connection". The pending_connections list represents
1191
1207
  # the set of connections that are awaiting connection.
1192
1208
  unless connection.connected?
1193
- @connection_requests -= 1
1194
1209
  @pending_connections << connection
1195
1210
  end
1196
1211
  return connection
@@ -1207,6 +1222,9 @@ module Mongo
1207
1222
  Monitoring::Event::Cmap::ConnectionCheckOutFailed::CONNECTION_ERROR
1208
1223
  ),
1209
1224
  )
1225
+ # We're going to raise, so we need to decrement the number of
1226
+ # connection requests.
1227
+ decrement_connection_requests_and_signal
1210
1228
  raise Error::MissingConnection.new
1211
1229
  end
1212
1230
  end
@@ -1237,7 +1255,6 @@ module Mongo
1237
1255
  connection = nil
1238
1256
 
1239
1257
  @lock.synchronize do
1240
-
1241
1258
  # The first gate to checking out a connection. Make sure the number of
1242
1259
  # unavailable connections is less than the max pool size.
1243
1260
  until max_size == 0 || unavailable_connections < max_size
@@ -1247,60 +1264,97 @@ module Mongo
1247
1264
  raise_if_not_ready!
1248
1265
  end
1249
1266
  @connection_requests += 1
1267
+ connection = wait_for_connection(connection_global_id, deadline)
1268
+ end
1250
1269
 
1251
- while connection.nil?
1252
- # The second gate to checking out a connection. Make sure 1) there
1253
- # exists an available connection and 2) we are under max_connecting.
1254
- until @available_connections.any? || @pending_connections.length < @max_connecting
1255
- wait = deadline - Utils.monotonic_time
1256
- raise_check_out_timeout!(connection_global_id) if wait <= 0
1257
- @max_connecting_cv.wait(wait)
1258
- raise_if_not_ready!
1259
- end
1270
+ connect_or_raise(connection) unless connection.connected?
1260
1271
 
1261
- connection = get_connection(deadline, Process.pid, connection_global_id)
1262
- wait = deadline - Utils.monotonic_time
1263
- raise_check_out_timeout!(connection_global_id) if connection.nil? && wait <= 0
1272
+ @lock.synchronize do
1273
+ @checked_out_connections << connection
1274
+ if @pending_connections.include?(connection)
1275
+ @pending_connections.delete(connection)
1264
1276
  end
1277
+ @max_connecting_cv.signal
1278
+ # no need to signal size_cv here since the number of unavailable
1279
+ # connections is unchanged.
1265
1280
  end
1266
1281
 
1267
- begin
1268
- connect_connection(connection)
1269
- rescue Exception
1270
- # Handshake or authentication failed
1271
- @lock.synchronize do
1272
- if @pending_connections.include?(connection)
1273
- @pending_connections.delete(connection)
1274
- else
1275
- @connection_requests -= 1
1282
+ connection
1283
+ end
1284
+
1285
+ # Waits for a connection to become available, or raises is no connection
1286
+ # becomes available before the timeout.
1287
+ # @param [ Integer ] connection_global_id The global id for the
1288
+ # connection to check out.
1289
+ # @param [ Float ] deadline The time at which to stop waiting.
1290
+ #
1291
+ # @return [ Mongo::Server::Connection ] The checked out connection.
1292
+ def wait_for_connection(connection_global_id, deadline)
1293
+ connection = nil
1294
+ while connection.nil?
1295
+ # The second gate to checking out a connection. Make sure 1) there
1296
+ # exists an available connection and 2) we are under max_connecting.
1297
+ until @available_connections.any? || @pending_connections.length < @max_connecting
1298
+ wait = deadline - Utils.monotonic_time
1299
+ if wait <= 0
1300
+ # We are going to raise a timeout error, so the connection
1301
+ # request is not going to be fulfilled. Decrement the counter
1302
+ # here.
1303
+ decrement_connection_requests_and_signal
1304
+ raise_check_out_timeout!(connection_global_id)
1276
1305
  end
1277
- @max_connecting_cv.signal
1278
- @size_cv.signal
1306
+ @max_connecting_cv.wait(wait)
1307
+ # We do not need to decrement the connection_requests counter
1308
+ # or signal here because the pool is not ready yet.
1309
+ raise_if_not_ready!
1279
1310
  end
1280
- @populate_semaphore.signal
1281
1311
 
1282
- publish_cmap_event(
1283
- Monitoring::Event::Cmap::ConnectionCheckOutFailed.new(
1284
- @server.address,
1285
- Monitoring::Event::Cmap::ConnectionCheckOutFailed::CONNECTION_ERROR
1286
- ),
1287
- )
1288
- raise
1312
+ connection = get_connection(Process.pid, connection_global_id)
1313
+ wait = deadline - Utils.monotonic_time
1314
+ if connection.nil? && wait <= 0
1315
+ # connection is nil here, it means that get_connection method
1316
+ # did not create a new connection; therefore, it did not decrease
1317
+ # the connection_requests counter. We need to do it here.
1318
+ decrement_connection_requests_and_signal
1319
+ raise_check_out_timeout!(connection_global_id)
1320
+ end
1289
1321
  end
1290
1322
 
1323
+ connection
1324
+ end
1325
+
1326
+ # Connects a connection and raises an exception if the connection
1327
+ # cannot be connected.
1328
+ # This method also publish corresponding event and ensures that counters
1329
+ # and condition variables are updated.
1330
+ def connect_or_raise(connection)
1331
+ connect_connection(connection)
1332
+ rescue Exception
1333
+ # Handshake or authentication failed
1291
1334
  @lock.synchronize do
1292
- @checked_out_connections << connection
1293
1335
  if @pending_connections.include?(connection)
1294
1336
  @pending_connections.delete(connection)
1295
- else
1296
- @connection_requests -= 1
1297
1337
  end
1298
1338
  @max_connecting_cv.signal
1299
- # no need to signal size_cv here since the number of unavailable
1300
- # connections is unchanged.
1339
+ @size_cv.signal
1301
1340
  end
1341
+ @populate_semaphore.signal
1342
+ publish_cmap_event(
1343
+ Monitoring::Event::Cmap::ConnectionCheckOutFailed.new(
1344
+ @server.address,
1345
+ Monitoring::Event::Cmap::ConnectionCheckOutFailed::CONNECTION_ERROR
1346
+ ),
1347
+ )
1348
+ raise
1349
+ end
1302
1350
 
1303
- connection
1351
+
1352
+ # Decrement connection requests counter and signal the condition
1353
+ # variables that the number of unavailable connections has decreased.
1354
+ def decrement_connection_requests_and_signal
1355
+ @connection_requests -= 1
1356
+ @max_connecting_cv.signal
1357
+ @size_cv.signal
1304
1358
  end
1305
1359
  end
1306
1360
  end
@@ -285,6 +285,7 @@ module Mongo
285
285
  uri_option 'maxStalenessSeconds', :max_staleness, group: :read, type: :max_staleness
286
286
 
287
287
  # Pool options
288
+ uri_option 'maxConnecting', :max_connecting, type: :integer
288
289
  uri_option 'minPoolSize', :min_pool_size, type: :integer
289
290
  uri_option 'maxPoolSize', :max_pool_size, type: :integer
290
291
  uri_option 'waitQueueTimeoutMS', :wait_queue_timeout, type: :ms
data/lib/mongo/version.rb CHANGED
@@ -20,5 +20,5 @@ module Mongo
20
20
  # The current version of the driver.
21
21
  #
22
22
  # @since 2.0.0
23
- VERSION = '2.19.0'.freeze
23
+ VERSION = '2.19.2'.freeze
24
24
  end
@@ -162,7 +162,7 @@ describe 'aggregation examples in Ruby' do
162
162
 
163
163
  # Start runCommand Example 2
164
164
 
165
- client.database.command(collStats: 'restaurants')
165
+ client.database.command(dbStats: 1)
166
166
 
167
167
  # End runCommand Example 2
168
168
  end
@@ -0,0 +1,227 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe 'Find operation options' do
6
+ require_mri
7
+ require_no_auth
8
+ min_server_fcv '4.4'
9
+
10
+ let(:subscriber) { Mrss::EventSubscriber.new }
11
+
12
+ let(:seeds) do
13
+ [ SpecConfig.instance.addresses.first ]
14
+ end
15
+
16
+ let(:client_options) do
17
+ {}
18
+ end
19
+
20
+ let(:collection_options) do
21
+ {}
22
+ end
23
+
24
+ let(:client) do
25
+ ClientRegistry.instance.new_local_client(
26
+ seeds,
27
+ SpecConfig.instance.test_options
28
+ .merge(database: SpecConfig.instance.test_db)
29
+ .merge(client_options)
30
+ ).tap do |client|
31
+ client.subscribe(Mongo::Monitoring::COMMAND, subscriber)
32
+ end
33
+ end
34
+
35
+ let(:collection) do
36
+ client['find_options', collection_options]
37
+ end
38
+
39
+ let(:find_command) do
40
+ subscriber.started_events.find { |cmd| cmd.command_name == 'find' }
41
+ end
42
+
43
+ let(:should_create_collection) { true }
44
+
45
+ before do
46
+ client['find_options'].drop
47
+ collection.create if should_create_collection
48
+ collection.insert_many([ { a: 1 }, { a: 2 }, { a: 3 } ])
49
+ end
50
+
51
+ describe 'collation' do
52
+ let(:client_options) do
53
+ {}
54
+ end
55
+
56
+ let(:collation) do
57
+ { 'locale' => 'en_US' }
58
+ end
59
+
60
+ context 'when defined on the collection' do
61
+ let(:collection_options) do
62
+ { collation: collation }
63
+ end
64
+
65
+ it 'uses the collation defined on the collection' do
66
+ collection.find.to_a
67
+ expect(find_command.command['collation']).to be_nil
68
+ end
69
+ end
70
+
71
+ context 'when defined on the operation' do
72
+ let(:collection_options) do
73
+ {}
74
+ end
75
+
76
+ it 'uses the collation defined on the collection' do
77
+ collection.find({}, collation: collation).to_a
78
+ expect(find_command.command['collation']).to eq(collation)
79
+ end
80
+ end
81
+
82
+ context 'when defined on both collection and operation' do
83
+ let(:collection_options) do
84
+ { 'locale' => 'de_AT' }
85
+ end
86
+
87
+ let(:should_create_collection) { false }
88
+
89
+ it 'uses the collation defined on the collection' do
90
+ collection.find({}, collation: collation).to_a
91
+ expect(find_command.command['collation']).to eq(collation)
92
+ end
93
+ end
94
+ end
95
+
96
+ describe 'read concern' do
97
+ context 'when defined on the client' do
98
+ let(:client_options) do
99
+ { read_concern: { level: :local } }
100
+ end
101
+
102
+ let(:collection_options) do
103
+ {}
104
+ end
105
+
106
+ it 'uses the read concern defined on the client' do
107
+ collection.find.to_a
108
+ expect(find_command.command['readConcern']).to eq('level' => 'local')
109
+ end
110
+
111
+ context 'when defined on the collection' do
112
+ let(:collection_options) do
113
+ { read_concern: { level: :majority } }
114
+ end
115
+
116
+ it 'uses the read concern defined on the collection' do
117
+ collection.find.to_a
118
+ expect(find_command.command['readConcern']).to eq('level' => 'majority')
119
+ end
120
+
121
+ context 'when defined on the operation' do
122
+ let(:operation_read_concern) do
123
+ { level: :available }
124
+ end
125
+
126
+ it 'uses the read concern defined on the operation' do
127
+ collection.find({}, read_concern: operation_read_concern).to_a
128
+ expect(find_command.command['readConcern']).to eq('level' => 'available')
129
+ end
130
+ end
131
+ end
132
+
133
+ context 'when defined on the operation' do
134
+ let(:collection_options) do
135
+ {}
136
+ end
137
+
138
+ let(:operation_read_concern) do
139
+ { level: :available }
140
+ end
141
+
142
+ it 'uses the read concern defined on the operation' do
143
+ collection.find({}, read_concern: operation_read_concern).to_a
144
+ expect(find_command.command['readConcern']).to eq('level' => 'available')
145
+ end
146
+ end
147
+ end
148
+
149
+ context 'when defined on the collection' do
150
+ let(:client_options) do
151
+ {}
152
+ end
153
+
154
+ let(:collection_options) do
155
+ { read_concern: { level: :majority } }
156
+ end
157
+
158
+ it 'uses the read concern defined on the collection' do
159
+ collection.find.to_a
160
+ expect(find_command.command['readConcern']).to eq('level' => 'majority')
161
+ end
162
+
163
+ context 'when defined on the operation' do
164
+ let(:operation_read_concern) do
165
+ { level: :available }
166
+ end
167
+
168
+ it 'uses the read concern defined on the operation' do
169
+ collection.find({}, read_concern: operation_read_concern).to_a
170
+ expect(find_command.command['readConcern']).to eq('level' => 'available')
171
+ end
172
+ end
173
+ end
174
+ end
175
+
176
+ describe 'read preference' do
177
+ require_topology :replica_set
178
+
179
+ context 'when defined on the client' do
180
+ let(:client_options) do
181
+ { read: { mode: :secondary } }
182
+ end
183
+
184
+ let(:collection_options) do
185
+ {}
186
+ end
187
+
188
+ it 'uses the read preference defined on the client' do
189
+ collection.find.to_a
190
+ expect(find_command.command['$readPreference']).to eq('mode' => 'secondary')
191
+ end
192
+
193
+ context 'when defined on the collection' do
194
+ let(:collection_options) do
195
+ { read: { mode: :secondary_preferred } }
196
+ end
197
+
198
+ it 'uses the read concern defined on the collection' do
199
+ collection.find.to_a
200
+ expect(find_command.command['$readPreference']).to eq('mode' => 'secondaryPreferred')
201
+ end
202
+ end
203
+ end
204
+ end
205
+
206
+ describe 'cursor type' do
207
+ let(:collection_options) do
208
+ { capped: true, size: 1000 }
209
+ end
210
+
211
+ context 'when cursor type is :tailable' do
212
+ it 'sets the cursor type to tailable' do
213
+ collection.find({}, cursor_type: :tailable).first
214
+ expect(find_command.command['tailable']).to be true
215
+ expect(find_command.command['awaitData']).to be_falsey
216
+ end
217
+ end
218
+
219
+ context 'when cursor type is :tailable_await' do
220
+ it 'sets the cursor type to tailable' do
221
+ collection.find({}, cursor_type: :tailable_await).first
222
+ expect(find_command.command['tailable']).to be true
223
+ expect(find_command.command['awaitData']).to be true
224
+ end
225
+ end
226
+ end
227
+ end
@@ -16,7 +16,11 @@ GRIDFS_TESTS = Dir.glob("#{CURRENT_PATH}/spec_tests/data/gridfs/*.yml").sort
16
16
  TRANSACTIONS_TESTS = Dir.glob("#{CURRENT_PATH}/spec_tests/data/transactions/*.yml").sort
17
17
  TRANSACTIONS_API_TESTS = Dir.glob("#{CURRENT_PATH}/spec_tests/data/transactions_api/*.yml").sort
18
18
  CHANGE_STREAMS_TESTS = Dir.glob("#{CURRENT_PATH}/spec_tests/data/change_streams/*.yml").sort
19
- CMAP_TESTS = Dir.glob("#{CURRENT_PATH}/spec_tests/data/cmap/*.yml").sort
19
+ CMAP_TESTS = Dir.glob("#{CURRENT_PATH}/spec_tests/data/cmap/*.yml").sort.select do |f|
20
+ # Skip tests that are flaky on JRuby.
21
+ # https://jira.mongodb.org/browse/RUBY-3292
22
+ !defined?(JRUBY_VERSION) || !f.include?('pool-checkout-minPoolSize-connection-maxConnecting.yml')
23
+ end
20
24
  AUTH_TESTS = Dir.glob("#{CURRENT_PATH}/spec_tests/data/auth/*.yml").sort
21
25
  CLIENT_SIDE_ENCRYPTION_TESTS = Dir.glob("#{CURRENT_PATH}/spec_tests/data/client_side_encryption/*.yml").sort
22
26
 
@@ -797,6 +797,32 @@ describe Mongo::Client do
797
797
  end
798
798
  end
799
799
 
800
+ context 'when max_connecting is provided' do
801
+ let(:client) do
802
+ new_local_client_nmio(SINGLE_CLIENT, options)
803
+ end
804
+
805
+ context 'when max_connecting is a positive integer' do
806
+ let(:options) do
807
+ { max_connecting: 5 }
808
+ end
809
+
810
+ it 'sets the max connecting' do
811
+ expect(client.options[:max_connecting]).to eq(options[:max_connecting])
812
+ end
813
+ end
814
+
815
+ context 'when max_connecting is a negative integer' do
816
+ let(:options) do
817
+ { max_connecting: -5 }
818
+ end
819
+
820
+ it 'raises an exception' do
821
+ expect { client }.to raise_error(Mongo::Error::InvalidMaxConnecting)
822
+ end
823
+ end
824
+ end
825
+
800
826
  context 'when min_pool_size is provided' do
801
827
  let(:client) { new_local_client_nmio(SINGLE_CLIENT, options) }
802
828
 
@@ -998,6 +1024,28 @@ describe Mongo::Client do
998
1024
  expect(client.options).to eq(expected_options)
999
1025
  end
1000
1026
 
1027
+ context 'when max_connecting is provided' do
1028
+ context 'when max_connecting is a positive integer' do
1029
+ let(:uri) do
1030
+ 'mongodb://127.0.0.1:27017/?maxConnecting=10'
1031
+ end
1032
+
1033
+ it 'sets the max connecting' do
1034
+ expect(client.options[:max_connecting]).to eq(10)
1035
+ end
1036
+ end
1037
+
1038
+ context 'when max_connecting is a negative integer' do
1039
+ let(:uri) do
1040
+ 'mongodb://127.0.0.1:27017/?maxConnecting=0'
1041
+ end
1042
+
1043
+ it 'raises an exception' do
1044
+ expect { client }.to raise_error(Mongo::Error::InvalidMaxConnecting)
1045
+ end
1046
+ end
1047
+ end
1048
+
1001
1049
  context 'when min_pool_size is provided' do
1002
1050
  context 'when max_pool_size is provided' do
1003
1051
  context 'when the min_pool_size is greater than the max_pool_size' do
@@ -137,7 +137,11 @@ describe Mongo::Collection do
137
137
  end
138
138
 
139
139
  let(:collstats) do
140
- database.read_command(:collstats => :specs).documents.first
140
+ collection.aggregate([ {'$collStats' => { 'storageStats' => {} }} ]).first
141
+ end
142
+
143
+ let(:storage_stats) do
144
+ collstats.fetch('storageStats', {})
141
145
  end
142
146
 
143
147
  let(:options) do
@@ -157,9 +161,9 @@ describe Mongo::Collection do
157
161
  end
158
162
 
159
163
  it "applies the options" do
160
- expect(collstats["capped"]).to be true
161
- expect(collstats["max"]).to eq(512)
162
- expect(collstats["maxSize"]).to eq(4096)
164
+ expect(storage_stats["capped"]).to be true
165
+ expect(storage_stats["max"]).to eq(512)
166
+ expect(storage_stats["maxSize"]).to eq(4096)
163
167
  end
164
168
  end
165
169
  end
@@ -651,7 +651,11 @@ describe Mongo::Collection do
651
651
  end
652
652
 
653
653
  let(:collstats) do
654
- database.read_command(:collstats => :specs).documents.first
654
+ collection.aggregate([ {'$collStats' => { 'storageStats' => {} }} ]).first
655
+ end
656
+
657
+ let(:storage_stats) do
658
+ collstats.fetch('storageStats', {})
655
659
  end
656
660
 
657
661
  before do
@@ -664,9 +668,9 @@ describe Mongo::Collection do
664
668
  end
665
669
 
666
670
  it "applies the options" do
667
- expect(collstats["capped"]).to be true
668
- expect(collstats["max"]).to eq(512)
669
- expect(collstats["maxSize"]).to eq(4096)
671
+ expect(storage_stats["capped"]).to be true
672
+ expect(storage_stats["max"]).to eq(512)
673
+ expect(storage_stats["maxSize"]).to eq(4096)
670
674
  end
671
675
  end
672
676
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  # rubocop:todo all
3
3
 
4
- require 'lite_spec_helper'
4
+ require 'spec_helper'
5
5
  require 'support/shared/protocol'
6
6
 
7
7
  describe Mongo::Protocol::Msg do
@@ -237,44 +237,6 @@ describe Mongo::Protocol::Msg do
237
237
  end
238
238
  end
239
239
 
240
- =begin no longer supported
241
- context 'when a 0 payload type is specified' do
242
-
243
- let(:section) do
244
- { type: 0, payload: { ismaster: 1 } }
245
- end
246
-
247
- let(:section_payload_type) { bytes.to_s[36] }
248
- let(:section_bytes) { bytes.to_s[37..-1] }
249
-
250
- it 'sets the payload type' do
251
- expect(section_payload_type).to eq(0.chr)
252
- end
253
-
254
- it 'serializes the section' do
255
- expect(section_bytes).to be_bson(section[:payload])
256
- end
257
- end
258
-
259
- context 'when a no payload type is specified' do
260
-
261
- let(:section) do
262
- { payload: { ismaster: 1 } }
263
- end
264
-
265
- let(:section_payload_type) { bytes.to_s[36] }
266
- let(:section_bytes) { bytes.to_s[37..-1] }
267
-
268
- it 'sets the payload type as 0' do
269
- expect(section_payload_type).to eq(0.chr)
270
- end
271
-
272
- it 'serializes the section' do
273
- expect(section_bytes).to be_bson(section[:payload])
274
- end
275
- end
276
- =end
277
-
278
240
  context 'when a payload of type 1 is specified' do
279
241
 
280
242
  let(:section) do
@@ -366,32 +328,9 @@ describe Mongo::Protocol::Msg do
366
328
  end
367
329
  end
368
330
  end
369
-
370
- =begin no longer supported
371
- context 'when the sections are mixed types and payload type 1 comes before type 0' do
372
-
373
- let(:section1) do
374
- Mongo::Protocol::Msg::Section1.new('documents', [ { a: 1 } ])
375
- end
376
-
377
- let(:section2) do
378
- { type: 0, payload: { 'b' => 2 } }
379
- end
380
-
381
- let(:sections) do
382
- [ section1, section2 ]
383
- end
384
-
385
- it 'serializes all sections' do
386
- expect(deserialized.documents).to eq([ BSON::Document.new(main_document), { 'a' => 1 }, { 'b' => 2 }])
387
- end
388
- end
389
- =end
390
331
  end
391
332
 
392
333
  context 'when the validating_keys option is true with payload 1' do
393
- max_bson_version '4.99.99'
394
-
395
334
  let(:sequences) do
396
335
  [ section ]
397
336
  end
@@ -1018,6 +1018,18 @@ describe Mongo::URI::SRVProtocol do
1018
1018
  include_examples "roundtrips string"
1019
1019
  end
1020
1020
 
1021
+ context 'when providing maxConnecting' do
1022
+
1023
+ let(:max_connecting) { 10 }
1024
+ let(:options) { "maxConnecting=#{max_connecting}" }
1025
+
1026
+ it 'sets the max connecting option' do
1027
+ expect(uri.uri_options[:max_connecting]).to eq(max_connecting)
1028
+ end
1029
+
1030
+ include_examples "roundtrips string"
1031
+ end
1032
+
1021
1033
  context 'when providing maxPoolSize' do
1022
1034
 
1023
1035
  let(:max_pool_size) { 10 }
@@ -320,22 +320,22 @@ module Unified
320
320
  collection = root_authorized_client.with(write_concern: {w: :majority}).
321
321
  use(spec.use!('databaseName'))[spec.use!('collectionName')]
322
322
  collection.drop
323
+ create_options = spec.use('createOptions') || {}
323
324
  docs = spec.use!('documents')
324
- if docs.any?
325
- collection.insert_many(docs)
326
- else
327
- begin
328
- collection.create
329
- rescue Mongo::Error => e
330
- if Mongo::Error::OperationFailure === e && (
325
+ begin
326
+ collection.create(create_options)
327
+ rescue Mongo::Error => e
328
+ if Mongo::Error::OperationFailure === e && (
331
329
  e.code == 48 || e.message =~ /collection already exists/
332
- )
333
- # Already exists
334
- else
335
- raise
336
- end
330
+ )
331
+ # Already exists
332
+ else
333
+ raise
337
334
  end
338
335
  end
336
+ if docs.any?
337
+ collection.insert_many(docs)
338
+ end
339
339
  unless spec.empty?
340
340
  raise NotImplementedError, "Unhandled spec keys: #{spec}"
341
341
  end
@@ -26,7 +26,7 @@ tests:
26
26
  command:
27
27
  ping: 1
28
28
  command_name: ping
29
- - description: "current op is not bypassed"
29
+ - description: "kill op is not bypassed"
30
30
  clientOptions:
31
31
  autoEncryptOpts:
32
32
  kmsProviders:
@@ -34,9 +34,10 @@ tests:
34
34
  operations:
35
35
  - name: runCommand
36
36
  object: database
37
- command_name: currentOp
37
+ command_name: killOp
38
38
  arguments:
39
39
  command:
40
- currentOp: 1
40
+ killOp: 1
41
+ op: 1234
41
42
  result:
42
- errorContains: "command not supported for auto encryption: currentOp"
43
+ errorContains: "command not supported for auto encryption: killOp"
@@ -1,7 +1,6 @@
1
1
  version: 1
2
2
  style: integration
3
3
  description: threads blocked by maxConnecting check out minPoolSize connections
4
- # Remove the topology runOn requirement when cmap specs are adjusted for lbs
5
4
  runOn:
6
5
  -
7
6
  # required for blockConnection in fail point
@@ -14,7 +13,7 @@ failPoint:
14
13
  failCommands: ["isMaster","hello"]
15
14
  closeConnection: false
16
15
  blockConnection: true
17
- blockTimeMS: 500
16
+ blockTimeMS: 1000
18
17
  poolOptions:
19
18
  # allows both thread1 and the background thread to start opening connections concurrently
20
19
  minPoolSize: 2
@@ -35,9 +34,8 @@ operations:
35
34
  # - it is long enough to make sure that the background thread opens a connection before thread1 releases its permit;
36
35
  # - it is short enough to allow thread2 to become blocked acquiring a permit to open a connection, and then grab the connection
37
36
  # opened by the background thread, before the background thread releases its permit.
38
- # This wait is not necessary in ruby since the background thread runs immediately.
39
37
  - name: wait
40
- ms: 0
38
+ ms: 200
41
39
  - name: checkOut
42
40
  thread: thread1
43
41
  - name: waitForEvent
@@ -0,0 +1,37 @@
1
+ description: collectionData-createOptions
2
+ schemaVersion: "1.9"
3
+ runOnRequirements:
4
+ - minServerVersion: "3.6"
5
+ # Capped collections cannot be created on serverless instances.
6
+ serverless: forbid
7
+ createEntities:
8
+ - client:
9
+ id: &client0 client0
10
+ - database:
11
+ id: &database0 database0
12
+ client: *client0
13
+ databaseName: &database0Name database0
14
+ - collection:
15
+ id: &collection0 collection0
16
+ database: *database0
17
+ collectionName: &collection0Name coll0
18
+ initialData:
19
+ - collectionName: *collection0Name
20
+ databaseName: *database0Name
21
+ createOptions:
22
+ capped: true
23
+ # With MMAPv1, the size field cannot be less than 4096.
24
+ size: &cappedSize 4096
25
+ documents:
26
+ - { _id: 1, x: 11 }
27
+ tests:
28
+ - description: collection is created with the correct options
29
+ operations:
30
+ - object: *collection0
31
+ name: aggregate
32
+ arguments:
33
+ pipeline:
34
+ - $collStats: { storageStats: {} }
35
+ - $project: { capped: '$storageStats.capped', maxSize: '$storageStats.maxSize'}
36
+ expectResult:
37
+ - { capped: true, maxSize: *cappedSize }
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongo
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.19.0
4
+ version: 2.19.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - The MongoDB Ruby Team
@@ -35,7 +35,7 @@ cert_chain:
35
35
  wkeAWhd5b+5JS0zgDL4SvGB8/W2IY+y0zELkojBMgJPyrpAWHL/WSsSBMuhyI2Pv
36
36
  xxaBVLklnJJ/qCCOZ3lG2MyVc/Nb0Mmq8ygWNsfwHmKKYuuWcviit0D0Tek=
37
37
  -----END CERTIFICATE-----
38
- date: 2023-06-22 00:00:00.000000000 Z
38
+ date: 2023-10-30 00:00:00.000000000 Z
39
39
  dependencies:
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: bson
@@ -231,6 +231,7 @@ files:
231
231
  - lib/mongo/error/invalid_document.rb
232
232
  - lib/mongo/error/invalid_file.rb
233
233
  - lib/mongo/error/invalid_file_revision.rb
234
+ - lib/mongo/error/invalid_max_connecting.rb
234
235
  - lib/mongo/error/invalid_min_pool_size.rb
235
236
  - lib/mongo/error/invalid_nonce.rb
236
237
  - lib/mongo/error/invalid_read_concern.rb
@@ -590,6 +591,7 @@ files:
590
591
  - spec/integration/cursor_reaping_spec.rb
591
592
  - spec/integration/docs_examples_spec.rb
592
593
  - spec/integration/error_detection_spec.rb
594
+ - spec/integration/find_options_spec.rb
593
595
  - spec/integration/fork_reconnect_spec.rb
594
596
  - spec/integration/get_more_spec.rb
595
597
  - spec/integration/grid_fs_bucket_spec.rb
@@ -1774,6 +1776,7 @@ files:
1774
1776
  - spec/spec_tests/data/unified/valid-fail/operation-failure.yml
1775
1777
  - spec/spec_tests/data/unified/valid-fail/operation-unsupported.yml
1776
1778
  - spec/spec_tests/data/unified/valid-pass/assertNumberConnectionsCheckedOut.yml
1779
+ - spec/spec_tests/data/unified/valid-pass/collectionData-createOptions.yml
1777
1780
  - spec/spec_tests/data/unified/valid-pass/entity-client-cmap-events.yml
1778
1781
  - spec/spec_tests/data/unified/valid-pass/entity-client-storeEventsAsEntities.yml
1779
1782
  - spec/spec_tests/data/unified/valid-pass/expectedError-errorResponse.yml
@@ -2457,6 +2460,7 @@ test_files:
2457
2460
  - spec/integration/client_side_encryption/auto_encryption_spec.rb
2458
2461
  - spec/integration/client_side_encryption/on_demand_aws_credentials_spec.rb
2459
2462
  - spec/integration/server_selection_spec.rb
2463
+ - spec/integration/find_options_spec.rb
2460
2464
  - spec/integration/ocsp_connectivity_spec.rb
2461
2465
  - spec/integration/error_detection_spec.rb
2462
2466
  - spec/integration/aws_auth_request_spec.rb
@@ -2989,6 +2993,7 @@ test_files:
2989
2993
  - spec/spec_tests/data/unified/valid-pass/poc-sessions.yml
2990
2994
  - spec/spec_tests/data/unified/valid-pass/poc-transactions.yml
2991
2995
  - spec/spec_tests/data/unified/valid-pass/expectedEventsForClient-eventType.yml
2996
+ - spec/spec_tests/data/unified/valid-pass/collectionData-createOptions.yml
2992
2997
  - spec/spec_tests/data/unified/valid-pass/poc-retryable-writes.yml
2993
2998
  - spec/spec_tests/data/unified/valid-pass/observeSensitiveCommands.yml
2994
2999
  - spec/spec_tests/data/unified/valid-pass/poc-retryable-reads.yml
metadata.gz.sig CHANGED
Binary file