mongo 2.16.4 → 2.17.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/README.md +1 -1
- data/lib/mongo/auth/aws/request.rb +0 -1
- data/lib/mongo/client.rb +4 -0
- data/lib/mongo/collection/view/aggregation.rb +62 -17
- data/lib/mongo/collection/view/builder/aggregation.rb +11 -13
- data/lib/mongo/collection/view/builder/map_reduce.rb +5 -8
- data/lib/mongo/collection/view/change_stream.rb +7 -3
- data/lib/mongo/collection/view/iterable.rb +3 -20
- data/lib/mongo/collection/view/map_reduce.rb +3 -14
- data/lib/mongo/collection/view/readable.rb +24 -1
- data/lib/mongo/collection/view/writable.rb +23 -0
- data/lib/mongo/collection/view.rb +0 -1
- data/lib/mongo/collection.rb +21 -1
- data/lib/mongo/database/view.rb +4 -2
- data/lib/mongo/database.rb +6 -6
- data/lib/mongo/error/snapshot_session_invalid_server_version.rb +31 -0
- data/lib/mongo/error/snapshot_session_transaction_prohibited.rb +30 -0
- data/lib/mongo/error.rb +2 -0
- data/lib/mongo/operation/delete/op_msg.rb +2 -1
- data/lib/mongo/operation/find/builder/command.rb +1 -0
- data/lib/mongo/operation/result.rb +6 -0
- data/lib/mongo/operation/shared/executable.rb +4 -0
- data/lib/mongo/operation/shared/sessions_supported.rb +18 -2
- data/lib/mongo/operation/update/op_msg.rb +2 -1
- data/lib/mongo/query_cache.rb +2 -12
- data/lib/mongo/server/description/features.rb +3 -1
- data/lib/mongo/server/monitor/connection.rb +4 -10
- data/lib/mongo/server_selector/base.rb +26 -4
- data/lib/mongo/session.rb +19 -0
- data/lib/mongo/socket/ocsp_cache.rb +2 -3
- data/lib/mongo/socket.rb +1 -5
- data/lib/mongo/utils.rb +0 -6
- data/lib/mongo/version.rb +1 -1
- data/mongo.gemspec +1 -1
- data/spec/integration/query_cache_spec.rb +0 -159
- data/spec/integration/read_preference_spec.rb +16 -12
- data/spec/integration/sdam_events_spec.rb +0 -40
- data/spec/lite_spec_helper.rb +0 -7
- data/spec/mongo/collection/view/aggregation_spec.rb +71 -95
- data/spec/mongo/collection/view/change_stream_spec.rb +1 -1
- data/spec/mongo/collection/view/map_reduce_spec.rb +14 -17
- data/spec/mongo/collection/view/readable_spec.rb +0 -56
- data/spec/mongo/operation/read_preference_op_msg_spec.rb +24 -1
- data/spec/mongo/query_cache_spec.rb +0 -165
- data/spec/mongo/server_selector_spec.rb +136 -15
- data/spec/mongo/socket/ssl_spec.rb +26 -58
- data/spec/mongo/utils_spec.rb +0 -14
- data/spec/runners/auth.rb +1 -1
- data/spec/runners/change_streams/spec.rb +1 -1
- data/spec/runners/cmap.rb +1 -1
- data/spec/runners/command_monitoring.rb +1 -1
- data/spec/runners/connection_string.rb +1 -1
- data/spec/runners/crud/spec.rb +3 -1
- data/spec/runners/crud/verifier.rb +1 -2
- data/spec/runners/gridfs.rb +1 -1
- data/spec/runners/read_write_concern_document.rb +1 -1
- data/spec/runners/sdam.rb +1 -1
- data/spec/runners/server_selection.rb +1 -1
- data/spec/runners/server_selection_rtt.rb +1 -1
- data/spec/runners/unified/assertions.rb +3 -1
- data/spec/runners/unified/crud_operations.rb +77 -23
- data/spec/runners/unified/ddl_operations.rb +29 -1
- data/spec/runners/unified/entity_map.rb +3 -3
- data/spec/runners/unified/support_operations.rb +6 -1
- data/spec/runners/unified/test.rb +15 -3
- data/spec/runners/unified/test_group.rb +1 -1
- data/spec/shared/share/Dockerfile.erb +3 -3
- data/spec/shared/shlib/server.sh +1 -1
- data/spec/spec_tests/data/crud_unified/aggregate-let.yml +138 -0
- data/spec/spec_tests/data/crud_unified/aggregate-write-readPreference.yml +155 -0
- data/spec/spec_tests/data/crud_unified/db-aggregate-write-readPreference.yml +151 -0
- data/spec/spec_tests/data/crud_unified/deleteMany-let.yml +91 -0
- data/spec/spec_tests/data/crud_unified/deleteOne-let.yml +89 -0
- data/spec/spec_tests/data/crud_unified/find-let.yml +71 -0
- data/spec/spec_tests/data/crud_unified/findOneAndDelete-let.yml +88 -0
- data/spec/spec_tests/data/crud_unified/findOneAndReplace-let.yml +94 -0
- data/spec/spec_tests/data/crud_unified/findOneAndUpdate-let.yml +96 -0
- data/spec/spec_tests/data/crud_unified/updateMany-let.yml +103 -0
- data/spec/spec_tests/data/crud_unified/updateOne-let.yml +98 -0
- data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/DefaultNoMaxStaleness.yml +2 -2
- data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/LastUpdateTime.yml +3 -3
- data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/Nearest.yml +3 -3
- data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/Nearest2.yml +3 -3
- data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/PrimaryPreferred.yml +2 -2
- data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/PrimaryPreferred_tags.yml +2 -2
- data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/Secondary.yml +4 -4
- data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/SecondaryPreferred.yml +2 -2
- data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/SecondaryPreferred_tags.yml +4 -4
- data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/ZeroMaxStaleness.yml +2 -2
- data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/DefaultNoMaxStaleness.yml +2 -2
- data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/LastUpdateTime.yml +3 -3
- data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/LongHeartbeat.yml +2 -2
- data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/LongHeartbeat2.yml +2 -2
- data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/MaxStalenessTooSmall.yml +2 -2
- data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/MaxStalenessWithModePrimary.yml +2 -2
- data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/Nearest.yml +3 -3
- data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/Nearest2.yml +3 -3
- data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/Nearest_tags.yml +2 -2
- data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/PrimaryPreferred.yml +2 -2
- data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/SecondaryPreferred.yml +2 -2
- data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/SecondaryPreferred_tags.yml +5 -5
- data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/SecondaryPreferred_tags2.yml +3 -3
- data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/Secondary_tags.yml +5 -5
- data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/Secondary_tags2.yml +3 -3
- data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/ZeroMaxStaleness.yml +2 -2
- data/spec/spec_tests/data/max_staleness/Sharded/SmallMaxStaleness.yml +2 -2
- data/spec/spec_tests/data/max_staleness/Single/SmallMaxStaleness.yml +1 -1
- data/spec/spec_tests/data/max_staleness/Unknown/SmallMaxStaleness.yml +1 -1
- data/spec/spec_tests/data/sessions_unified/snapshot-sessions-not-supported-client-error.yml +69 -0
- data/spec/spec_tests/data/sessions_unified/snapshot-sessions-not-supported-server-error.yml +102 -0
- data/spec/spec_tests/data/sessions_unified/snapshot-sessions-unsupported-ops.yml +258 -0
- data/spec/spec_tests/data/sessions_unified/snapshot-sessions.yml +482 -0
- data/spec/spec_tests/seed_list_discovery_spec.rb +1 -1
- data/spec/spec_tests/sessions_unified_spec.rb +13 -0
- data/spec/support/certificates/atlas-ocsp-ca.crt +47 -40
- data/spec/support/certificates/atlas-ocsp.crt +106 -101
- data/spec/support/utils.rb +0 -31
- data.tar.gz.sig +0 -0
- metadata +1084 -1058
- metadata.gz.sig +0 -0
- data/spec/integration/find_options_spec.rb +0 -227
data/lib/mongo/database.rb
CHANGED
@@ -253,12 +253,12 @@ module Mongo
|
|
253
253
|
|
254
254
|
client.send(:with_session, opts) do |session|
|
255
255
|
read_with_retry(session, preference) do |server|
|
256
|
-
Operation::Command.new(
|
257
|
-
:
|
258
|
-
:
|
259
|
-
:
|
260
|
-
:
|
261
|
-
|
256
|
+
Operation::Command.new(
|
257
|
+
selector: operation.dup,
|
258
|
+
db_name: name,
|
259
|
+
read: preference,
|
260
|
+
session: session,
|
261
|
+
).execute(server, context: Operation::Context.new(client: client, session: session))
|
262
262
|
end
|
263
263
|
end
|
264
264
|
end
|
@@ -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
|
-
|
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
|
@@ -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
|
@@ -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,
|
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)
|
data/lib/mongo/query_cache.rb
CHANGED
@@ -179,8 +179,7 @@ module Mongo
|
|
179
179
|
#
|
180
180
|
# @api private
|
181
181
|
def get(**opts)
|
182
|
-
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 =
|
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 = (
|
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
|
-
|
231
|
-
|
230
|
+
if hello_ok? || server_api
|
231
|
+
doc = HELLO_DOC
|
232
232
|
if server_api
|
233
|
-
|
233
|
+
doc = doc.merge(Utils.transform_server_api(server_api))
|
234
234
|
end
|
235
|
-
|
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 =
|
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
|
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
|
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
|
-
|
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
data/mongo.gemspec
CHANGED
@@ -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)
|