mongo 2.16.3 → 2.17.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/README.md +1 -1
  4. data/lib/mongo/auth/aws/request.rb +0 -1
  5. data/lib/mongo/client.rb +4 -0
  6. data/lib/mongo/collection/view/aggregation.rb +62 -17
  7. data/lib/mongo/collection/view/builder/aggregation.rb +11 -13
  8. data/lib/mongo/collection/view/builder/map_reduce.rb +5 -8
  9. data/lib/mongo/collection/view/change_stream.rb +7 -3
  10. data/lib/mongo/collection/view/iterable.rb +3 -5
  11. data/lib/mongo/collection/view/map_reduce.rb +3 -14
  12. data/lib/mongo/collection/view/readable.rb +24 -1
  13. data/lib/mongo/collection/view/writable.rb +23 -0
  14. data/lib/mongo/collection.rb +21 -1
  15. data/lib/mongo/database/view.rb +4 -2
  16. data/lib/mongo/database.rb +6 -6
  17. data/lib/mongo/error/snapshot_session_invalid_server_version.rb +31 -0
  18. data/lib/mongo/error/snapshot_session_transaction_prohibited.rb +30 -0
  19. data/lib/mongo/error.rb +2 -0
  20. data/lib/mongo/operation/delete/op_msg.rb +2 -1
  21. data/lib/mongo/operation/find/builder/command.rb +1 -0
  22. data/lib/mongo/operation/result.rb +6 -0
  23. data/lib/mongo/operation/shared/executable.rb +4 -0
  24. data/lib/mongo/operation/shared/sessions_supported.rb +18 -2
  25. data/lib/mongo/operation/update/op_msg.rb +2 -1
  26. data/lib/mongo/query_cache.rb +2 -12
  27. data/lib/mongo/server/description/features.rb +3 -1
  28. data/lib/mongo/server/monitor/connection.rb +4 -10
  29. data/lib/mongo/server_selector/base.rb +26 -4
  30. data/lib/mongo/session.rb +19 -0
  31. data/lib/mongo/socket/ocsp_cache.rb +2 -3
  32. data/lib/mongo/socket.rb +1 -5
  33. data/lib/mongo/utils.rb +0 -6
  34. data/lib/mongo/version.rb +1 -1
  35. data/mongo.gemspec +1 -1
  36. data/spec/integration/query_cache_spec.rb +0 -159
  37. data/spec/integration/read_preference_spec.rb +16 -12
  38. data/spec/integration/sdam_events_spec.rb +0 -40
  39. data/spec/lite_spec_helper.rb +0 -7
  40. data/spec/mongo/collection/view/aggregation_spec.rb +71 -95
  41. data/spec/mongo/collection/view/change_stream_spec.rb +1 -1
  42. data/spec/mongo/collection/view/map_reduce_spec.rb +14 -17
  43. data/spec/mongo/collection/view/readable_spec.rb +0 -56
  44. data/spec/mongo/operation/read_preference_op_msg_spec.rb +24 -1
  45. data/spec/mongo/query_cache_spec.rb +0 -165
  46. data/spec/mongo/server_selector_spec.rb +136 -15
  47. data/spec/mongo/socket/ssl_spec.rb +26 -58
  48. data/spec/mongo/utils_spec.rb +0 -14
  49. data/spec/runners/auth.rb +1 -1
  50. data/spec/runners/change_streams/spec.rb +1 -1
  51. data/spec/runners/cmap.rb +1 -1
  52. data/spec/runners/command_monitoring.rb +1 -1
  53. data/spec/runners/connection_string.rb +1 -1
  54. data/spec/runners/crud/spec.rb +3 -1
  55. data/spec/runners/crud/verifier.rb +1 -2
  56. data/spec/runners/gridfs.rb +1 -1
  57. data/spec/runners/read_write_concern_document.rb +1 -1
  58. data/spec/runners/sdam.rb +1 -1
  59. data/spec/runners/server_selection.rb +1 -1
  60. data/spec/runners/server_selection_rtt.rb +1 -1
  61. data/spec/runners/unified/assertions.rb +3 -1
  62. data/spec/runners/unified/crud_operations.rb +77 -23
  63. data/spec/runners/unified/ddl_operations.rb +29 -1
  64. data/spec/runners/unified/entity_map.rb +3 -3
  65. data/spec/runners/unified/support_operations.rb +6 -1
  66. data/spec/runners/unified/test.rb +15 -3
  67. data/spec/runners/unified/test_group.rb +1 -1
  68. data/spec/shared/share/Dockerfile.erb +3 -3
  69. data/spec/shared/shlib/server.sh +1 -1
  70. data/spec/spec_tests/data/crud_unified/aggregate-let.yml +138 -0
  71. data/spec/spec_tests/data/crud_unified/aggregate-write-readPreference.yml +155 -0
  72. data/spec/spec_tests/data/crud_unified/db-aggregate-write-readPreference.yml +151 -0
  73. data/spec/spec_tests/data/crud_unified/deleteMany-let.yml +91 -0
  74. data/spec/spec_tests/data/crud_unified/deleteOne-let.yml +89 -0
  75. data/spec/spec_tests/data/crud_unified/find-let.yml +71 -0
  76. data/spec/spec_tests/data/crud_unified/findOneAndDelete-let.yml +88 -0
  77. data/spec/spec_tests/data/crud_unified/findOneAndReplace-let.yml +94 -0
  78. data/spec/spec_tests/data/crud_unified/findOneAndUpdate-let.yml +96 -0
  79. data/spec/spec_tests/data/crud_unified/updateMany-let.yml +103 -0
  80. data/spec/spec_tests/data/crud_unified/updateOne-let.yml +98 -0
  81. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/DefaultNoMaxStaleness.yml +2 -2
  82. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/LastUpdateTime.yml +3 -3
  83. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/Nearest.yml +3 -3
  84. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/Nearest2.yml +3 -3
  85. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/PrimaryPreferred.yml +2 -2
  86. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/PrimaryPreferred_tags.yml +2 -2
  87. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/Secondary.yml +4 -4
  88. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/SecondaryPreferred.yml +2 -2
  89. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/SecondaryPreferred_tags.yml +4 -4
  90. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/ZeroMaxStaleness.yml +2 -2
  91. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/DefaultNoMaxStaleness.yml +2 -2
  92. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/LastUpdateTime.yml +3 -3
  93. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/LongHeartbeat.yml +2 -2
  94. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/LongHeartbeat2.yml +2 -2
  95. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/MaxStalenessTooSmall.yml +2 -2
  96. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/MaxStalenessWithModePrimary.yml +2 -2
  97. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/Nearest.yml +3 -3
  98. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/Nearest2.yml +3 -3
  99. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/Nearest_tags.yml +2 -2
  100. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/PrimaryPreferred.yml +2 -2
  101. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/SecondaryPreferred.yml +2 -2
  102. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/SecondaryPreferred_tags.yml +5 -5
  103. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/SecondaryPreferred_tags2.yml +3 -3
  104. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/Secondary_tags.yml +5 -5
  105. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/Secondary_tags2.yml +3 -3
  106. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/ZeroMaxStaleness.yml +2 -2
  107. data/spec/spec_tests/data/max_staleness/Sharded/SmallMaxStaleness.yml +2 -2
  108. data/spec/spec_tests/data/max_staleness/Single/SmallMaxStaleness.yml +1 -1
  109. data/spec/spec_tests/data/max_staleness/Unknown/SmallMaxStaleness.yml +1 -1
  110. data/spec/spec_tests/data/sessions_unified/snapshot-sessions-not-supported-client-error.yml +69 -0
  111. data/spec/spec_tests/data/sessions_unified/snapshot-sessions-not-supported-server-error.yml +102 -0
  112. data/spec/spec_tests/data/sessions_unified/snapshot-sessions-unsupported-ops.yml +258 -0
  113. data/spec/spec_tests/data/sessions_unified/snapshot-sessions.yml +482 -0
  114. data/spec/spec_tests/seed_list_discovery_spec.rb +1 -1
  115. data/spec/spec_tests/sessions_unified_spec.rb +13 -0
  116. data/spec/support/certificates/atlas-ocsp-ca.crt +47 -40
  117. data/spec/support/certificates/atlas-ocsp.crt +106 -101
  118. data/spec/support/utils.rb +0 -31
  119. data.tar.gz.sig +3 -1
  120. metadata +1051 -1018
  121. metadata.gz.sig +0 -0
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ # Copyright (C) 2021 MongoDB Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ module Mongo
19
+ class Error
20
+
21
+ # Exception raised if an operation using a snapshot session is
22
+ # directed to a pre-5.0 server.
23
+ class SnapshotSessionInvalidServerVersion < Error
24
+
25
+ # Instantiate the new exception.
26
+ def initialize
27
+ super("Snapshot reads require MongoDB 5.0 or later")
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ # Copyright (C) 2021 MongoDB Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ module Mongo
19
+ class Error
20
+
21
+ # Exception raised if a transaction is attempted on a snapshot session.
22
+ class SnapshotSessionTransactionProhibited < Error
23
+
24
+ # Instantiate the new exception.
25
+ def initialize
26
+ super("Transactions are not supported in snapshot sessions")
27
+ end
28
+ end
29
+ end
30
+ end
data/lib/mongo/error.rb CHANGED
@@ -223,6 +223,8 @@ require 'mongo/error/no_server_available'
223
223
  require 'mongo/error/no_srv_records'
224
224
  require 'mongo/error/session_ended'
225
225
  require 'mongo/error/sessions_not_supported'
226
+ require 'mongo/error/snapshot_session_invalid_server_version'
227
+ require 'mongo/error/snapshot_session_transaction_prohibited'
226
228
  require 'mongo/error/operation_failure'
227
229
  require 'mongo/error/pool_closed_error'
228
230
  require 'mongo/error/raise_original_error'
@@ -37,7 +37,8 @@ module Mongo
37
37
  { delete: coll_name,
38
38
  Protocol::Msg::DATABASE_IDENTIFIER => db_name,
39
39
  ordered: ordered?,
40
- }.tap do |selector|
40
+ let: spec[:let],
41
+ }.compact.tap do |selector|
41
42
  if hint = spec[:hint]
42
43
  validate_hint_on_update(connection, selector)
43
44
  selector[:hint] = hint
@@ -35,6 +35,7 @@ module Mongo
35
35
  comment: 'comment',
36
36
  filter: 'filter',
37
37
  hint: 'hint',
38
+ let: 'let',
38
39
  limit: 'limit',
39
40
  max_scan: 'maxScan',
40
41
  max_time_ms: 'maxTimeMS',
@@ -429,6 +429,12 @@ module Mongo
429
429
  !!(first_document && first_document['writeConcernError'])
430
430
  end
431
431
 
432
+ def snapshot_timestamp
433
+ if doc = reply.documents.first
434
+ doc['cursor']&.[]('atClusterTime') || doc['atClusterTime']
435
+ end
436
+ end
437
+
432
438
  private
433
439
 
434
440
  def aggregate_returned_count
@@ -43,6 +43,10 @@ module Mongo
43
43
  session.pin_to_service(connection.service_id)
44
44
  end
45
45
  end
46
+
47
+ if session.snapshot? && !session.snapshot_timestamp
48
+ session.snapshot_timestamp = result.snapshot_timestamp
49
+ end
46
50
  end
47
51
  process_result(result, connection)
48
52
  end
@@ -199,8 +199,13 @@ module Mongo
199
199
  read_doc
200
200
  else
201
201
  # In replica sets, read preference is passed to the server if one
202
- # is specified by the application, and there is no default.
203
- read&.to_doc
202
+ # is specified by the application, except for primary read preferences.
203
+ read_doc = BSON::Document.new(read&.to_doc || {})
204
+ if [nil, 'primary'].include?(read_doc['mode'])
205
+ nil
206
+ else
207
+ read_doc
208
+ end
204
209
  end
205
210
 
206
211
  if read_doc
@@ -224,6 +229,17 @@ module Mongo
224
229
  then
225
230
  sel[:recoveryToken] = session.recovery_token
226
231
  end
232
+
233
+ if session.snapshot?
234
+ unless connection.description.server_version_gte?('5.0')
235
+ raise Error::SnapshotSessionInvalidServerVersion
236
+ end
237
+
238
+ sel[:readConcern] = {level: 'snapshot'}
239
+ if session.snapshot_timestamp
240
+ sel[:readConcern][:atClusterTime] = session.snapshot_timestamp
241
+ end
242
+ end
227
243
  end
228
244
 
229
245
  def build_message(connection, context)
@@ -37,7 +37,8 @@ module Mongo
37
37
  {
38
38
  update: coll_name,
39
39
  ordered: ordered?,
40
- }
40
+ let: spec[:let]
41
+ }.compact
41
42
  end
42
43
 
43
44
  def message(connection)
@@ -179,8 +179,7 @@ module Mongo
179
179
  #
180
180
  # @api private
181
181
  def get(**opts)
182
- limit = normalized_limit(opts[:limit])
183
-
182
+ limit = opts[:limit]
184
183
  _namespace_key = namespace_key(**opts)
185
184
  _cache_key = cache_key(**opts)
186
185
 
@@ -190,7 +189,7 @@ module Mongo
190
189
  caching_cursor = namespace_hash[_cache_key]
191
190
  return nil unless caching_cursor
192
191
 
193
- caching_cursor_limit = normalized_limit(caching_cursor.view.limit)
192
+ caching_cursor_limit = caching_cursor.view.limit
194
193
 
195
194
  # There are two scenarios in which a caching cursor could fulfill the
196
195
  # query:
@@ -200,7 +199,6 @@ module Mongo
200
199
  #
201
200
  # Otherwise, return nil because the stored cursor will not satisfy
202
201
  # the query.
203
-
204
202
  if limit && (caching_cursor_limit.nil? || caching_cursor_limit >= limit)
205
203
  caching_cursor
206
204
  elsif limit.nil? && caching_cursor_limit.nil?
@@ -210,14 +208,6 @@ module Mongo
210
208
  end
211
209
  end
212
210
 
213
- def normalized_limit(limit)
214
- return nil unless limit
215
- # For the purposes of caching, a limit of 0 means no limit, as mongo treats it as such.
216
- return nil if limit == 0
217
- # For the purposes of caching, a negative limit is the same as as a positive limit.
218
- limit.abs
219
- end
220
-
221
211
  private
222
212
 
223
213
  def cache_key(**opts)
@@ -35,9 +35,11 @@ module Mongo
35
35
  # - 8 => 4.2
36
36
  # - 9 => 4.4
37
37
  # - 13 => 5.0
38
+ # - 14 => 5.1
38
39
  #
39
40
  # @since 2.0.0
40
41
  MAPPINGS = {
42
+ merge_out_on_secondary: 13,
41
43
  retryable_write_error_label: 9,
42
44
  commit_quorum: 9,
43
45
  # Server versions older than 4.2 do not reliably validate options
@@ -78,7 +80,7 @@ module Mongo
78
80
  # The wire protocol versions that this version of the driver supports.
79
81
  #
80
82
  # @since 2.0.0
81
- DRIVER_WIRE_VERSIONS = (2..13).freeze
83
+ DRIVER_WIRE_VERSIONS = (6..14).freeze
82
84
 
83
85
  # Create the methods for each mapping to tell if they are supported.
84
86
  #
@@ -227,21 +227,15 @@ module Mongo
227
227
  # @api private
228
228
  def check_document
229
229
  server_api = @app_metadata.server_api || options[:server_api]
230
- doc = if hello_ok? || server_api
231
- _doc = HELLO_DOC
230
+ if hello_ok? || server_api
231
+ doc = HELLO_DOC
232
232
  if server_api
233
- _doc = _doc.merge(Utils.transform_server_api(server_api))
233
+ doc = doc.merge(Utils.transform_server_api(server_api))
234
234
  end
235
- _doc
235
+ doc
236
236
  else
237
237
  LEGACY_HELLO_DOC
238
238
  end
239
- # compressors must be set to maintain correct compression status
240
- # in the server description. See RUBY-2427
241
- if compressors = options[:compressors]
242
- doc = doc.merge(compression: compressors)
243
- end
244
- doc
245
239
  end
246
240
 
247
241
  private
@@ -162,6 +162,8 @@ module Mongo
162
162
  # Deprecated and ignored.
163
163
  # @param [ Session | nil ] session Optional session to take into account
164
164
  # for mongos pinning. Added in version 2.10.0.
165
+ # @param [ true | false ] write_aggregation Whether we need a server that
166
+ # supports writing aggregations (e.g. with $merge/$out) on secondaries.
165
167
  #
166
168
  # @return [ Mongo::Server ] A server matching the server preference.
167
169
  #
@@ -172,7 +174,7 @@ module Mongo
172
174
  # lint mode is enabled.
173
175
  #
174
176
  # @since 2.0.0
175
- def select_server(cluster, ping = nil, session = nil)
177
+ def select_server(cluster, ping = nil, session = nil, write_aggregation: false)
176
178
  if cluster.topology.is_a?(Cluster::Topology::LoadBalanced)
177
179
  return cluster.servers.first
178
180
  end
@@ -243,7 +245,7 @@ module Mongo
243
245
  =end
244
246
 
245
247
  loop do
246
- server = try_select_server(cluster)
248
+ server = try_select_server(cluster, write_aggregation: write_aggregation)
247
249
 
248
250
  if server
249
251
  unless cluster.topology.compatible?
@@ -294,11 +296,31 @@ module Mongo
294
296
  # Tries to find a suitable server, returns the server if one is available
295
297
  # or nil if there isn't a suitable server.
296
298
  #
299
+ # @param [ Mongo::Cluster ] cluster The cluster from which to select
300
+ # an eligible server.
301
+ # @param [ true | false ] write_aggregation Whether we need a server that
302
+ # supports writing aggregations (e.g. with $merge/$out) on secondaries.
303
+ #
297
304
  # @return [ Server | nil ] A suitable server, if one exists.
298
305
  #
299
306
  # @api private
300
- def try_select_server(cluster)
301
- servers = suitable_servers(cluster)
307
+ def try_select_server(cluster, write_aggregation: false)
308
+ servers = if write_aggregation && cluster.replica_set?
309
+ # 1. Check if ALL servers in cluster support secondary writes.
310
+ is_write_supported = cluster.servers.reduce(true) do |res, server|
311
+ res && server.features.merge_out_on_secondary_enabled?
312
+ end
313
+
314
+ if is_write_supported
315
+ # 2. If all servers support secondary writes, we respect read preference.
316
+ suitable_servers(cluster)
317
+ else
318
+ # 3. Otherwise we fallback to primary for replica set.
319
+ [cluster.servers.detect(&:primary?)]
320
+ end
321
+ else
322
+ suitable_servers(cluster)
323
+ end
302
324
 
303
325
  # This list of servers may be ordered in a specific way
304
326
  # by the selector (e.g. for secondary preferred, the first
data/lib/mongo/session.rb CHANGED
@@ -56,10 +56,16 @@ module Mongo
56
56
  # - *:mode* -- the read preference as a string or symbol; valid values are
57
57
  # *:primary*, *:primary_preferred*, *:secondary*, *:secondary_preferred*
58
58
  # and *:nearest*.
59
+ # @option options [ true | false ] :snapshot Set up the session for
60
+ # snapshot reads.
59
61
  #
60
62
  # @since 2.5.0
61
63
  # @api private
62
64
  def initialize(server_session, client, options = {})
65
+ if options[:causal_consistency] && options[:snapshot]
66
+ raise ArgumentError, ':causal_consistency and :snapshot options cannot be both set on a session'
67
+ end
68
+
63
69
  @server_session = server_session
64
70
  options = options.dup
65
71
 
@@ -83,6 +89,12 @@ module Mongo
83
89
  @client.cluster
84
90
  end
85
91
 
92
+ # @return [ true | false ] Whether the session is configured for snapshot
93
+ # reads.
94
+ def snapshot?
95
+ !!options[:snapshot]
96
+ end
97
+
86
98
  # @return [ BSON::Timestamp ] The latest seen operation time for this session.
87
99
  #
88
100
  # @since 2.5.0
@@ -506,6 +518,10 @@ module Mongo
506
518
  =end
507
519
  end
508
520
 
521
+ if snapshot?
522
+ raise Mongo::Error::SnapshotSessionTransactionProhibited
523
+ end
524
+
509
525
  check_if_ended!
510
526
 
511
527
  if within_states?(STARTING_TRANSACTION_STATE, TRANSACTION_IN_PROGRESS_STATE)
@@ -1024,6 +1040,9 @@ module Mongo
1024
1040
  @server_session.txn_num
1025
1041
  end
1026
1042
 
1043
+ # @api private
1044
+ attr_accessor :snapshot_timestamp
1045
+
1027
1046
  private
1028
1047
 
1029
1048
  # Get the read concern the session will use when starting a transaction.
@@ -21,8 +21,7 @@ module Mongo
21
21
  # This module caches OCSP responses for their indicated validity time.
22
22
  #
23
23
  # The key is the CertificateId used for the OCSP request.
24
- # The value is the SingleResponse on Ruby 2.4+, or the OpenStruct
25
- # emulation of it on Ruby 2.3.
24
+ # The value is the SingleResponse.
26
25
  #
27
26
  # @api private
28
27
  module OcspCache
@@ -40,7 +39,7 @@ module Mongo
40
39
  # expire by the time caller uses them. The caller should not perform
41
40
  # update time checks on the returned response.
42
41
  #
43
- # @return [ OpenSSL::OCSP::SingleResponse | OpenStruct ] The previously
42
+ # @return [ OpenSSL::OCSP::SingleResponse ] The previously
44
43
  # retrieved response.
45
44
  module_function def get(cert_id)
46
45
  resp = responses.detect do |resp|
data/lib/mongo/socket.rb CHANGED
@@ -362,11 +362,7 @@ module Mongo
362
362
  end
363
363
 
364
364
  def allocate_string(capacity)
365
- if RUBY_VERSION >= '2.4.0'
366
- String.new('', :capacity => capacity, :encoding => 'BINARY')
367
- else
368
- ('x'*capacity).force_encoding('BINARY')
369
- end
365
+ String.new('', :capacity => capacity, :encoding => 'BINARY')
370
366
  end
371
367
 
372
368
  def read_buffer_size
data/lib/mongo/utils.rb CHANGED
@@ -101,11 +101,5 @@ module Mongo
101
101
  module_function def monotonic_time
102
102
  Process.clock_gettime(Process::CLOCK_MONOTONIC)
103
103
  end
104
-
105
- # Hash#compact implementation for Ruby 2.3/2.4
106
- # Implementation based on activesupport 5.2.3
107
- module_function def slice_hash(hash, *keys)
108
- keys.each_with_object({}) { |k, res| res[k] = hash[k] if hash.key?(k) }
109
- end
110
104
  end
111
105
  end
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.16.3'.freeze
23
+ VERSION = '2.17.0'.freeze
24
24
  end
data/mongo.gemspec CHANGED
@@ -36,7 +36,7 @@ Gem::Specification.new do |s|
36
36
  s.require_paths = ['lib']
37
37
  s.bindir = 'bin'
38
38
 
39
- s.required_ruby_version = ">= 2.4"
39
+ s.required_ruby_version = ">= 2.5"
40
40
 
41
41
  s.add_dependency 'bson', '>=4.8.2', '<5.0.0'
42
42
  end
@@ -345,69 +345,18 @@ describe 'QueryCache' do
345
345
 
346
346
  it 'uses the cache' do
347
347
  results_limit_5 = authorized_collection.find.limit(5).to_a
348
- results_limit_negative_5 = authorized_collection.find.limit(-5).to_a
349
348
  results_limit_3 = authorized_collection.find.limit(3).to_a
350
- results_limit_negative_3 = authorized_collection.find.limit(-3).to_a
351
349
  results_no_limit = authorized_collection.find.to_a
352
- results_limit_0 = authorized_collection.find.limit(0).to_a
353
-
354
-
355
- expect(results_limit_5.length).to eq(5)
356
- expect(results_limit_5.map { |r| r["test"] }).to eq([0, 1, 2, 3, 4])
357
-
358
- expect(results_limit_negative_5.length).to eq(5)
359
- expect(results_limit_negative_5.map { |r| r["test"] }).to eq([0, 1, 2, 3, 4])
360
-
361
- expect(results_limit_3.length).to eq(3)
362
- expect(results_limit_3.map { |r| r["test"] }).to eq([0, 1, 2])
363
-
364
- expect(results_limit_negative_3.length).to eq(3)
365
- expect(results_limit_negative_3.map { |r| r["test"] }).to eq([0, 1, 2])
366
-
367
- expect(results_no_limit.length).to eq(10)
368
- expect(results_no_limit.map { |r| r["test"] }).to eq([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
369
-
370
- expect(results_limit_0.length).to eq(10)
371
- expect(results_limit_0.map { |r| r["test"] }).to eq([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
372
-
373
- expect(events.length).to eq(1)
374
- end
375
- end
376
-
377
- context 'when the first query has a 0 limit' do
378
- before do
379
- authorized_collection.find.limit(0).to_a
380
- end
381
-
382
- it 'uses the cache' do
383
- results_limit_5 = authorized_collection.find.limit(5).to_a
384
- results_limit_negative_5 = authorized_collection.find.limit(-5).to_a
385
- results_limit_3 = authorized_collection.find.limit(3).to_a
386
- results_limit_negative_3 = authorized_collection.find.limit(-3).to_a
387
- results_no_limit = authorized_collection.find.to_a
388
- results_limit_0 = authorized_collection.find.limit(0).to_a
389
350
 
390
351
  expect(results_limit_5.length).to eq(5)
391
352
  expect(results_limit_5.map { |r| r["test"] }).to eq([0, 1, 2, 3, 4])
392
353
 
393
- expect(results_limit_negative_5.length).to eq(5)
394
- expect(results_limit_negative_5.map { |r| r["test"] }).to eq([0, 1, 2, 3, 4])
395
-
396
-
397
354
  expect(results_limit_3.length).to eq(3)
398
355
  expect(results_limit_3.map { |r| r["test"] }).to eq([0, 1, 2])
399
356
 
400
- expect(results_limit_negative_3.length).to eq(3)
401
- expect(results_limit_negative_3.map { |r| r["test"] }).to eq([0, 1, 2])
402
-
403
-
404
357
  expect(results_no_limit.length).to eq(10)
405
358
  expect(results_no_limit.map { |r| r["test"] }).to eq([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
406
359
 
407
-
408
- expect(results_limit_0.length).to eq(10)
409
- expect(results_limit_0.map { |r| r["test"] }).to eq([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
410
-
411
360
  expect(events.length).to eq(1)
412
361
  end
413
362
  end
@@ -442,21 +391,6 @@ describe 'QueryCache' do
442
391
  end
443
392
  end
444
393
 
445
- context 'and two queries are performed with a larger negative limit' do
446
- it 'uses the query cache for the third query' do
447
- results1 = authorized_collection.find.limit(-3).to_a
448
- results2 = authorized_collection.find.limit(-3).to_a
449
-
450
- expect(results1.length).to eq(3)
451
- expect(results1.map { |r| r["test"] }).to eq([0, 1, 2])
452
-
453
- expect(results2.length).to eq(3)
454
- expect(results2.map { |r| r["test"] }).to eq([0, 1, 2])
455
-
456
- expect(events.length).to eq(2)
457
- end
458
- end
459
-
460
394
  context 'and the second query has a smaller limit' do
461
395
  let(:results) { authorized_collection.find.limit(1).to_a }
462
396
 
@@ -467,99 +401,6 @@ describe 'QueryCache' do
467
401
  end
468
402
  end
469
403
 
470
- context 'and the second query has a smaller negative limit' do
471
- let(:results) { authorized_collection.find.limit(-1).to_a }
472
-
473
- it 'uses the cached query' do
474
- expect(results.count).to eq(1)
475
- expect(results.first["test"]).to eq(0)
476
- expect(events.length).to eq(1)
477
- end
478
- end
479
-
480
- context 'and the second query has no limit' do
481
- it 'queries again' do
482
- expect(authorized_collection.find.to_a.count).to eq(10)
483
- expect(events.length).to eq(2)
484
- end
485
- end
486
- end
487
-
488
- context 'when the first query has a negative limit' do
489
- before do
490
- authorized_collection.find.limit(-2).to_a
491
- end
492
-
493
- context 'and the second query has a larger limit' do
494
- let(:results) { authorized_collection.find.limit(3).to_a }
495
-
496
- it 'queries again' do
497
- expect(results.length).to eq(3)
498
- expect(results.map { |result| result["test"] }).to eq([0, 1, 2])
499
- expect(events.length).to eq(2)
500
- end
501
- end
502
-
503
- context 'and the second query has a larger negative limit' do
504
- let(:results) { authorized_collection.find.limit(-3).to_a }
505
-
506
- it 'queries again' do
507
- expect(results.length).to eq(3)
508
- expect(results.map { |result| result["test"] }).to eq([0, 1, 2])
509
- expect(events.length).to eq(2)
510
- end
511
- end
512
-
513
- context 'and two queries are performed with a larger limit' do
514
- it 'uses the query cache for the third query' do
515
- results1 = authorized_collection.find.limit(3).to_a
516
- results2 = authorized_collection.find.limit(3).to_a
517
-
518
- expect(results1.length).to eq(3)
519
- expect(results1.map { |r| r["test"] }).to eq([0, 1, 2])
520
-
521
- expect(results2.length).to eq(3)
522
- expect(results2.map { |r| r["test"] }).to eq([0, 1, 2])
523
-
524
- expect(events.length).to eq(2)
525
- end
526
- end
527
-
528
- context 'and two queries are performed with a larger negative limit' do
529
- it 'uses the query cache for the third query' do
530
- results1 = authorized_collection.find.limit(-3).to_a
531
- results2 = authorized_collection.find.limit(-3).to_a
532
-
533
- expect(results1.length).to eq(3)
534
- expect(results1.map { |r| r["test"] }).to eq([0, 1, 2])
535
-
536
- expect(results2.length).to eq(3)
537
- expect(results2.map { |r| r["test"] }).to eq([0, 1, 2])
538
-
539
- expect(events.length).to eq(2)
540
- end
541
- end
542
-
543
- context 'and the second query has a smaller limit' do
544
- let(:results) { authorized_collection.find.limit(1).to_a }
545
-
546
- it 'uses the cached query' do
547
- expect(results.count).to eq(1)
548
- expect(results.first["test"]).to eq(0)
549
- expect(events.length).to eq(1)
550
- end
551
- end
552
-
553
- context 'and the second query has a smaller negative limit' do
554
- let(:results) { authorized_collection.find.limit(-1).to_a }
555
-
556
- it 'uses the cached query' do
557
- expect(results.count).to eq(1)
558
- expect(results.first["test"]).to eq(0)
559
- expect(events.length).to eq(1)
560
- end
561
- end
562
-
563
404
  context 'and the second query has no limit' do
564
405
  it 'queries again' do
565
406
  expect(authorized_collection.find.to_a.count).to eq(10)
@@ -45,16 +45,6 @@ describe 'Read preference' do
45
45
  {}
46
46
  end
47
47
 
48
- shared_examples_for 'sends expected read preference when reading' do
49
- it 'sends expected read preference when reading' do
50
- read_operation
51
-
52
- event = subscriber.single_command_started_event('find')
53
- actual_preference = event.command['$readPreference']
54
- expect(actual_preference).to eq(expected_read_preference)
55
- end
56
- end
57
-
58
48
  shared_examples_for 'does not send read preference when reading' do
59
49
  it 'does not send read preference when reading' do
60
50
  read_operation
@@ -95,7 +85,17 @@ describe 'Read preference' do
95
85
  context 'server supporting OP_MSG' do
96
86
  min_server_fcv '3.6'
97
87
 
98
- it_behaves_like 'sends expected read preference when reading'
88
+ it 'sends expected read preference when reading' do
89
+ read_operation
90
+
91
+ event = subscriber.single_command_started_event('find')
92
+ actual_preference = event.command['$readPreference']
93
+ if expected_read_preference&.[]("mode") == "primary"
94
+ expect(actual_preference).to be_nil
95
+ else
96
+ expect(actual_preference).to eq(expected_read_preference)
97
+ end
98
+ end
99
99
  end
100
100
  end
101
101
 
@@ -307,7 +307,11 @@ describe 'Read preference' do
307
307
 
308
308
  event = subscriber.single_command_started_event('find')
309
309
  actual_preference = event.command['$readPreference']
310
- expect(actual_preference).to eq(expected_read_preference)
310
+ if expected_read_preference&.[]("mode") == "primary"
311
+ expect(actual_preference).to be_nil
312
+ else
313
+ expect(actual_preference).to eq(expected_read_preference)
314
+ end
311
315
  end
312
316
  end
313
317
  end