mongo 2.13.3 → 2.14.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/mongo/address/ipv4.rb +1 -1
- data/lib/mongo/address/ipv6.rb +1 -1
- data/lib/mongo/address.rb +1 -1
- data/lib/mongo/bulk_write.rb +17 -0
- data/lib/mongo/caching_cursor.rb +74 -0
- data/lib/mongo/client.rb +47 -8
- data/lib/mongo/cluster/topology/single.rb +1 -1
- data/lib/mongo/cluster.rb +3 -3
- data/lib/mongo/collection/view/aggregation.rb +25 -4
- data/lib/mongo/collection/view/builder/find_command.rb +38 -18
- data/lib/mongo/collection/view/explainable.rb +27 -8
- data/lib/mongo/collection/view/iterable.rb +72 -12
- data/lib/mongo/collection/view/readable.rb +12 -2
- data/lib/mongo/collection/view/writable.rb +15 -1
- data/lib/mongo/collection/view.rb +24 -20
- data/lib/mongo/collection.rb +26 -2
- data/lib/mongo/crypt/encryption_io.rb +6 -6
- data/lib/mongo/cursor.rb +1 -0
- data/lib/mongo/database/view.rb +1 -1
- data/lib/mongo/database.rb +8 -14
- data/lib/mongo/error/invalid_read_concern.rb +28 -0
- data/lib/mongo/error/server_certificate_revoked.rb +22 -0
- data/lib/mongo/error/unsupported_option.rb +14 -12
- data/lib/mongo/error.rb +2 -0
- data/lib/mongo/grid/fs_bucket.rb +37 -37
- data/lib/mongo/lint.rb +2 -1
- data/lib/mongo/logger.rb +3 -3
- data/lib/mongo/operation/aggregate/result.rb +9 -8
- data/lib/mongo/operation/collections_info/command.rb +0 -5
- data/lib/mongo/operation/collections_info/result.rb +3 -16
- data/lib/mongo/operation/delete/bulk_result.rb +2 -0
- data/lib/mongo/operation/delete/result.rb +3 -0
- data/lib/mongo/operation/explain/command.rb +4 -0
- data/lib/mongo/operation/explain/legacy.rb +4 -0
- data/lib/mongo/operation/explain/op_msg.rb +6 -0
- data/lib/mongo/operation/explain/result.rb +3 -0
- data/lib/mongo/operation/find/legacy/result.rb +2 -0
- data/lib/mongo/operation/find/result.rb +3 -0
- data/lib/mongo/operation/get_more/result.rb +3 -0
- data/lib/mongo/operation/indexes/result.rb +5 -0
- data/lib/mongo/operation/insert/bulk_result.rb +5 -0
- data/lib/mongo/operation/insert/result.rb +5 -0
- data/lib/mongo/operation/list_collections/result.rb +5 -0
- data/lib/mongo/operation/map_reduce/result.rb +10 -0
- data/lib/mongo/operation/parallel_scan/command.rb +2 -1
- data/lib/mongo/operation/parallel_scan/result.rb +4 -0
- data/lib/mongo/operation/result.rb +35 -6
- data/lib/mongo/operation/shared/bypass_document_validation.rb +1 -0
- data/lib/mongo/operation/shared/causal_consistency_supported.rb +1 -0
- data/lib/mongo/operation/shared/collections_info_or_list_collections.rb +2 -0
- data/lib/mongo/operation/shared/executable.rb +1 -0
- data/lib/mongo/operation/shared/idable.rb +2 -1
- data/lib/mongo/operation/shared/limited.rb +1 -0
- data/lib/mongo/operation/shared/object_id_generator.rb +1 -0
- data/lib/mongo/operation/shared/read_preference_supported.rb +36 -38
- data/lib/mongo/operation/shared/result/aggregatable.rb +1 -0
- data/lib/mongo/operation/shared/sessions_supported.rb +3 -3
- data/lib/mongo/operation/shared/specifiable.rb +1 -0
- data/lib/mongo/operation/shared/write.rb +1 -0
- data/lib/mongo/operation/shared/write_concern_supported.rb +1 -0
- data/lib/mongo/operation/update/legacy/result.rb +7 -0
- data/lib/mongo/operation/update/result.rb +8 -0
- data/lib/mongo/operation/users_info/result.rb +3 -0
- data/lib/mongo/operation.rb +2 -0
- data/lib/mongo/protocol/msg.rb +2 -2
- data/lib/mongo/protocol/query.rb +11 -11
- data/lib/mongo/query_cache.rb +242 -0
- data/lib/mongo/retryable.rb +8 -1
- data/lib/mongo/server/connection_common.rb +2 -2
- data/lib/mongo/server/connection_pool.rb +3 -0
- data/lib/mongo/server/monitor/connection.rb +3 -3
- data/lib/mongo/server/monitor.rb +1 -1
- data/lib/mongo/server/pending_connection.rb +2 -2
- data/lib/mongo/server/push_monitor.rb +1 -1
- data/lib/mongo/server.rb +5 -1
- data/lib/mongo/server_selector/base.rb +5 -1
- data/lib/mongo/server_selector/secondary_preferred.rb +7 -2
- data/lib/mongo/session.rb +3 -0
- data/lib/mongo/socket/ocsp_cache.rb +97 -0
- data/lib/mongo/socket/ocsp_verifier.rb +368 -0
- data/lib/mongo/socket/ssl.rb +45 -24
- data/lib/mongo/socket.rb +6 -4
- data/lib/mongo/srv/monitor.rb +7 -13
- data/lib/mongo/srv/resolver.rb +14 -10
- data/lib/mongo/timeout.rb +2 -0
- data/lib/mongo/uri/options_mapper.rb +582 -0
- data/lib/mongo/uri/srv_protocol.rb +3 -2
- data/lib/mongo/uri.rb +21 -390
- data/lib/mongo/utils.rb +12 -1
- data/lib/mongo/version.rb +1 -1
- data/lib/mongo.rb +9 -0
- data/spec/NOTES.aws-auth.md +12 -7
- data/spec/README.md +56 -1
- data/spec/integration/bson_symbol_spec.rb +2 -4
- data/spec/integration/bulk_write_spec.rb +48 -0
- data/spec/integration/client_authentication_options_spec.rb +55 -28
- data/spec/integration/connection_pool_populator_spec.rb +3 -1
- data/spec/integration/cursor_reaping_spec.rb +53 -17
- data/spec/integration/ocsp_connectivity_spec.rb +26 -0
- data/spec/integration/ocsp_verifier_cache_spec.rb +188 -0
- data/spec/integration/ocsp_verifier_spec.rb +334 -0
- data/spec/integration/query_cache_spec.rb +1045 -0
- data/spec/integration/query_cache_transactions_spec.rb +179 -0
- data/spec/integration/retryable_writes/retryable_writes_40_and_newer_spec.rb +1 -0
- data/spec/integration/retryable_writes/shared/performs_legacy_retries.rb +2 -0
- data/spec/integration/sdam_error_handling_spec.rb +69 -18
- data/spec/integration/sdam_events_spec.rb +7 -8
- data/spec/integration/server_selection_spec.rb +36 -0
- data/spec/integration/srv_monitoring_spec.rb +38 -3
- data/spec/integration/srv_spec.rb +56 -0
- data/spec/lite_spec_helper.rb +4 -2
- data/spec/mongo/address_spec.rb +1 -1
- data/spec/mongo/caching_cursor_spec.rb +70 -0
- data/spec/mongo/client_construction_spec.rb +54 -1
- data/spec/mongo/client_encryption_spec.rb +10 -16
- data/spec/mongo/client_spec.rb +40 -0
- data/spec/mongo/cluster/topology/single_spec.rb +14 -5
- data/spec/mongo/cluster_spec.rb +3 -0
- data/spec/mongo/collection/view/explainable_spec.rb +87 -4
- data/spec/mongo/collection/view/map_reduce_spec.rb +2 -0
- data/spec/mongo/collection_spec.rb +60 -0
- data/spec/mongo/crypt/auto_decryption_context_spec.rb +1 -1
- data/spec/mongo/crypt/auto_encryption_context_spec.rb +1 -1
- data/spec/mongo/crypt/data_key_context_spec.rb +1 -1
- data/spec/mongo/crypt/explicit_decryption_context_spec.rb +1 -1
- data/spec/mongo/crypt/explicit_encryption_context_spec.rb +1 -1
- data/spec/mongo/database_spec.rb +44 -64
- data/spec/mongo/error/no_server_available_spec.rb +1 -1
- data/spec/mongo/index/view_spec.rb +2 -4
- data/spec/mongo/logger_spec.rb +13 -11
- data/spec/mongo/operation/read_preference_legacy_spec.rb +19 -9
- data/spec/mongo/operation/read_preference_op_msg_spec.rb +3 -3
- data/spec/mongo/query_cache_spec.rb +279 -0
- data/spec/mongo/server/app_metadata_shared.rb +7 -33
- data/spec/mongo/server/connection_pool_spec.rb +7 -3
- data/spec/mongo/server/connection_spec.rb +14 -7
- data/spec/mongo/server_selector/secondary_preferred_spec.rb +6 -6
- data/spec/mongo/socket/ssl_spec.rb +1 -1
- data/spec/mongo/socket_spec.rb +1 -1
- data/spec/mongo/uri/srv_protocol_spec.rb +64 -33
- data/spec/mongo/uri_option_parsing_spec.rb +11 -11
- data/spec/mongo/uri_spec.rb +68 -41
- data/spec/mongo/utils_spec.rb +39 -0
- data/spec/runners/auth.rb +3 -0
- data/spec/runners/connection_string.rb +35 -124
- data/spec/runners/transactions/operation.rb +2 -13
- data/spec/spec_tests/cmap_spec.rb +7 -3
- data/spec/spec_tests/data/change_streams/change-streams-errors.yml +0 -1
- data/spec/spec_tests/data/change_streams/change-streams.yml +0 -1
- data/spec/spec_tests/data/cmap/pool-checkout-connection.yml +6 -2
- data/spec/spec_tests/data/cmap/pool-create-min-size.yml +3 -0
- data/spec/spec_tests/data/connection_string/valid-warnings.yml +24 -0
- data/spec/spec_tests/data/sdam_monitoring/discovered_standalone.yml +1 -3
- data/spec/spec_tests/data/sdam_monitoring/standalone.yml +2 -2
- data/spec/spec_tests/data/sdam_monitoring/standalone_repeated.yml +2 -2
- data/spec/spec_tests/data/sdam_monitoring/standalone_suppress_equal_description_changes.yml +2 -2
- data/spec/spec_tests/data/sdam_monitoring/standalone_to_rs_with_me_mismatch.yml +2 -2
- data/spec/spec_tests/data/uri_options/auth-options.yml +25 -0
- data/spec/spec_tests/data/uri_options/compression-options.yml +6 -3
- data/spec/spec_tests/data/uri_options/read-preference-options.yml +24 -0
- data/spec/spec_tests/data/uri_options/ruby-connection-options.yml +1 -0
- data/spec/spec_tests/data/uri_options/tls-options.yml +160 -4
- data/spec/spec_tests/dns_seedlist_discovery_spec.rb +9 -1
- data/spec/spec_tests/uri_options_spec.rb +31 -33
- data/spec/support/certificates/atlas-ocsp-ca.crt +28 -0
- data/spec/support/certificates/atlas-ocsp.crt +41 -0
- data/spec/support/client_registry.rb +4 -8
- data/spec/support/client_registry_macros.rb +4 -4
- data/spec/support/common_shortcuts.rb +45 -0
- data/spec/support/constraints.rb +23 -0
- data/spec/support/lite_constraints.rb +24 -0
- data/spec/support/matchers.rb +16 -0
- data/spec/support/ocsp +1 -0
- data/spec/support/session_registry.rb +52 -0
- data/spec/support/spec_config.rb +22 -12
- data/spec/support/spec_setup.rb +38 -48
- data/spec/support/utils.rb +19 -1
- data.tar.gz.sig +1 -3
- metadata +938 -933
- metadata.gz.sig +0 -0
- data/spec/integration/secondary_reads_spec.rb +0 -102
- data/spec/shared/LICENSE +0 -20
- data/spec/shared/bin/get-mongodb-download-url +0 -17
- data/spec/shared/lib/mrss/child_process_helper.rb +0 -80
- data/spec/shared/lib/mrss/cluster_config.rb +0 -221
- data/spec/shared/lib/mrss/constraints.rb +0 -346
- data/spec/shared/lib/mrss/docker_runner.rb +0 -265
- data/spec/shared/lib/mrss/lite_constraints.rb +0 -191
- data/spec/shared/lib/mrss/server_version_registry.rb +0 -115
- data/spec/shared/lib/mrss/spec_organizer.rb +0 -152
- data/spec/shared/lib/mrss/utils.rb +0 -15
- data/spec/shared/share/Dockerfile.erb +0 -231
- data/spec/shared/shlib/distro.sh +0 -73
- data/spec/shared/shlib/server.sh +0 -290
- data/spec/shared/shlib/set_env.sh +0 -128
@@ -36,44 +36,47 @@ module Mongo
|
|
36
36
|
#
|
37
37
|
# @since 2.0.0
|
38
38
|
def options(connection)
|
39
|
-
|
40
|
-
if add_slave_ok_flag?(connection)
|
41
|
-
flags = options[:flags]&.dup || []
|
42
|
-
flags << :slave_ok
|
43
|
-
options = options.merge(flags: flags)
|
44
|
-
end
|
45
|
-
options
|
39
|
+
add_slave_ok_flag_maybe(super, connection)
|
46
40
|
end
|
47
41
|
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
42
|
+
# Adds :slave_ok flag to options based on the read preference specified
|
43
|
+
# in the operation or implied by the topology that the connection's
|
44
|
+
# server is a part of.
|
51
45
|
#
|
46
|
+
# @param [ Hash ] options The options calculated so far.
|
52
47
|
# @param [ Server::Connection ] connection The connection that the
|
53
48
|
# operation will be executed on.
|
54
49
|
#
|
55
|
-
# @return [
|
56
|
-
def
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
50
|
+
# @return [ Hash ] The new options.
|
51
|
+
def add_slave_ok_flag_maybe(options, connection)
|
52
|
+
add_flag =
|
53
|
+
# https://github.com/mongodb/specifications/blob/master/source/server-selection/server-selection.rst#topology-type-single
|
54
|
+
if connection.description.standalone?
|
55
|
+
# Read preference is never sent to standalones.
|
56
|
+
false
|
57
|
+
elsif connection.server.cluster.single?
|
58
|
+
# In Single topology the driver forces primaryPreferred read
|
59
|
+
# preference mode (via the slave_ok flag, in case of old servers)
|
60
|
+
# so that the query is satisfied.
|
61
|
+
true
|
62
|
+
else
|
63
|
+
# In replica sets and sharded clusters, read preference is passed
|
64
|
+
# to the server if one is specified by the application, and there
|
65
|
+
# is no default.
|
66
|
+
read && read.slave_ok?
|
67
|
+
end
|
68
|
+
|
69
|
+
if add_flag
|
70
|
+
options= options.dup
|
71
|
+
(options[:flags] ||= []) << :slave_ok
|
71
72
|
end
|
73
|
+
|
74
|
+
options
|
72
75
|
end
|
73
76
|
|
74
77
|
def command(connection)
|
75
78
|
sel = super
|
76
|
-
|
79
|
+
update_selector_for_read_pref(sel, connection)
|
77
80
|
end
|
78
81
|
|
79
82
|
# Adds $readPreference field to the command document.
|
@@ -92,19 +95,14 @@ module Mongo
|
|
92
95
|
# operation will be executed on.
|
93
96
|
#
|
94
97
|
# @return [ Hash ] New command document to send to the server.
|
95
|
-
def
|
98
|
+
def update_selector_for_read_pref(sel, connection)
|
96
99
|
if read && connection.description.mongos? && read_pref = read.to_mongos
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
Mongo::Lint.validate_camel_case_read_preference(read_pref)
|
103
|
-
sel = sel[:$query] ? sel : {:$query => sel}
|
104
|
-
sel = sel.merge(:$readPreference => read_pref)
|
105
|
-
end
|
100
|
+
Mongo::Lint.validate_camel_case_read_preference(read_pref)
|
101
|
+
sel = sel[:$query] ? sel : {:$query => sel}
|
102
|
+
sel = sel.merge(:$readPreference => read_pref)
|
103
|
+
else
|
104
|
+
sel
|
106
105
|
end
|
107
|
-
sel
|
108
106
|
end
|
109
107
|
end
|
110
108
|
end
|
@@ -18,6 +18,7 @@ module Mongo
|
|
18
18
|
# Shared behavior of operations that support a session.
|
19
19
|
#
|
20
20
|
# @since 2.5.2
|
21
|
+
# @api private
|
21
22
|
module SessionsSupported
|
22
23
|
|
23
24
|
private
|
@@ -170,10 +171,9 @@ module Mongo
|
|
170
171
|
elsif connection.description.mongos?
|
171
172
|
# When server is a mongos:
|
172
173
|
# - $readPreference is never sent when mode is 'primary'
|
174
|
+
# - When mode is 'secondaryPreferred' $readPreference is only sent
|
175
|
+
# when a non-mode field (i.e. tag_sets) is present
|
173
176
|
# - Otherwise $readPreference is sent
|
174
|
-
# When mode is 'secondaryPreferred' $readPreference is currently
|
175
|
-
# required to only be sent when a non-mode field (i.e. tag_sets)
|
176
|
-
# is present, but this causes wrong behavior (DRIVERS-1642).
|
177
177
|
if read
|
178
178
|
doc = read.to_mongos
|
179
179
|
if doc
|
@@ -21,17 +21,20 @@ module Mongo
|
|
21
21
|
# version <= 2.4.
|
22
22
|
#
|
23
23
|
# @since 2.0.0
|
24
|
+
# @api semiprivate
|
24
25
|
class Result < Operation::Result
|
25
26
|
include Operation::Result::UseLegacyErrorParser
|
26
27
|
|
27
28
|
# Whether an existing document was updated.
|
28
29
|
#
|
29
30
|
# @since 2.0.0
|
31
|
+
# @api private
|
30
32
|
UPDATED_EXISTING = 'updatedExisting'.freeze
|
31
33
|
|
32
34
|
# The upserted docs field in the result.
|
33
35
|
#
|
34
36
|
# @since 2.0.0
|
37
|
+
# @api private
|
35
38
|
UPSERTED = 'upserted'.freeze
|
36
39
|
|
37
40
|
# Get the number of documents matched.
|
@@ -42,6 +45,7 @@ module Mongo
|
|
42
45
|
# @return [ Integer ] The matched count.
|
43
46
|
#
|
44
47
|
# @since 2.0.0
|
48
|
+
# @api public
|
45
49
|
def matched_count
|
46
50
|
return 0 unless acknowledged?
|
47
51
|
if upsert?
|
@@ -59,6 +63,7 @@ module Mongo
|
|
59
63
|
# @return [ nil ] Always omitted for legacy versions.
|
60
64
|
#
|
61
65
|
# @since 2.0.0
|
66
|
+
# @api public
|
62
67
|
def modified_count; end
|
63
68
|
|
64
69
|
# The identifier of the inserted document if an upsert
|
@@ -70,6 +75,7 @@ module Mongo
|
|
70
75
|
# @return [ Object ] The upserted id.
|
71
76
|
#
|
72
77
|
# @since 2.0.0
|
78
|
+
# @api public
|
73
79
|
def upserted_id
|
74
80
|
first[UPSERTED] if upsert?
|
75
81
|
end
|
@@ -82,6 +88,7 @@ module Mongo
|
|
82
88
|
# @return [ Integer ] The number upserted.
|
83
89
|
#
|
84
90
|
# @since 2.4.2
|
91
|
+
# @api public
|
85
92
|
def upserted_count
|
86
93
|
upsert? ? n : 0
|
87
94
|
end
|
@@ -19,16 +19,19 @@ module Mongo
|
|
19
19
|
# Defines custom behavior of results for an update.
|
20
20
|
#
|
21
21
|
# @since 2.0.0
|
22
|
+
# @api semiprivate
|
22
23
|
class Result < Operation::Result
|
23
24
|
|
24
25
|
# The number of modified docs field in the result.
|
25
26
|
#
|
26
27
|
# @since 2.0.0
|
28
|
+
# @api private
|
27
29
|
MODIFIED = 'nModified'.freeze
|
28
30
|
|
29
31
|
# The upserted docs field in the result.
|
30
32
|
#
|
31
33
|
# @since 2.0.0
|
34
|
+
# @api private
|
32
35
|
UPSERTED = 'upserted'.freeze
|
33
36
|
|
34
37
|
# Get the number of documents matched.
|
@@ -39,6 +42,7 @@ module Mongo
|
|
39
42
|
# @return [ Integer ] The matched count.
|
40
43
|
#
|
41
44
|
# @since 2.0.0
|
45
|
+
# @api public
|
42
46
|
def matched_count
|
43
47
|
return 0 unless acknowledged?
|
44
48
|
if upsert?
|
@@ -56,6 +60,7 @@ module Mongo
|
|
56
60
|
# @return [ Integer ] The modified count.
|
57
61
|
#
|
58
62
|
# @since 2.0.0
|
63
|
+
# @api public
|
59
64
|
def modified_count
|
60
65
|
return 0 unless acknowledged?
|
61
66
|
first[MODIFIED]
|
@@ -70,6 +75,7 @@ module Mongo
|
|
70
75
|
# @return [ Object ] The upserted id.
|
71
76
|
#
|
72
77
|
# @since 2.0.0
|
78
|
+
# @api public
|
73
79
|
def upserted_id
|
74
80
|
return nil unless upsert?
|
75
81
|
upsert?.first['_id']
|
@@ -83,10 +89,12 @@ module Mongo
|
|
83
89
|
# @return [ Integer ] The number upserted.
|
84
90
|
#
|
85
91
|
# @since 2.4.2
|
92
|
+
# @api public
|
86
93
|
def upserted_count
|
87
94
|
upsert? ? n : 0
|
88
95
|
end
|
89
96
|
|
97
|
+
# @api public
|
90
98
|
def bulk_result
|
91
99
|
BulkResult.new(@replies, connection_description)
|
92
100
|
end
|
@@ -20,13 +20,16 @@ module Mongo
|
|
20
20
|
# usersInfo command.
|
21
21
|
#
|
22
22
|
# @since 2.1.0
|
23
|
+
# @api semiprivate
|
23
24
|
class Result < Operation::Result
|
24
25
|
|
25
26
|
# The field name for the users document in a usersInfo result.
|
26
27
|
#
|
27
28
|
# @since 2.1.0
|
29
|
+
# @api private
|
28
30
|
USERS = 'users'.freeze
|
29
31
|
|
32
|
+
# @api public
|
30
33
|
def documents
|
31
34
|
reply.documents.first[USERS]
|
32
35
|
end
|
data/lib/mongo/operation.rb
CHANGED
data/lib/mongo/protocol/msg.rb
CHANGED
@@ -43,8 +43,8 @@ module Mongo
|
|
43
43
|
# Msg.new([:more_to_come], {}, { ismaster: 1 },
|
44
44
|
# { type: 1, payload: { identifier: 'documents', sequence: [..] } })
|
45
45
|
#
|
46
|
-
# @param [ Array<Symbol> ] flags The flag bits.
|
47
|
-
#
|
46
|
+
# @param [ Array<Symbol> ] flags The flag bits. Current supported values
|
47
|
+
# are :more_to_come and :checksum_present.
|
48
48
|
# @param [ Hash ] options The options.
|
49
49
|
# @param [ BSON::Document, Hash ] main_document The document that will
|
50
50
|
# become the payload type 0 section. Can contain global args as they
|
data/lib/mongo/protocol/query.rb
CHANGED
@@ -46,18 +46,18 @@ module Mongo
|
|
46
46
|
# @example Find all user ids.
|
47
47
|
# Query.new('xgen', 'users', {}, :fields => {:id => 1})
|
48
48
|
#
|
49
|
-
# @param [
|
50
|
-
# @param [
|
51
|
-
# @param [
|
52
|
-
# @param [
|
49
|
+
# @param database [String, Symbol] The database to query.
|
50
|
+
# @param collection [String, Symbol] The collection to query.
|
51
|
+
# @param selector [Hash] The query selector.
|
52
|
+
# @param options [Hash] The additional query options.
|
53
53
|
#
|
54
|
-
# @option options [
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
54
|
+
# @option options :project [Hash] The projection.
|
55
|
+
# @option options :skip [Integer] The number of documents to skip.
|
56
|
+
# @option options :limit [Integer] The number of documents to return.
|
57
|
+
# @option options :flags [Array] The flags for the query message.
|
58
|
+
#
|
59
|
+
# Supported flags: +:tailable_cursor+, +:slave_ok+, +:oplog_replay+,
|
60
|
+
# +:no_cursor_timeout+, +:await_data+, +:exhaust+, +:partial+
|
61
61
|
def initialize(database, collection, selector, options = {})
|
62
62
|
@database = database
|
63
63
|
@namespace = "#{database}.#{collection}"
|
@@ -0,0 +1,242 @@
|
|
1
|
+
# Copyright (C) 2020 MongoDB, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module Mongo
|
16
|
+
module QueryCache
|
17
|
+
class << self
|
18
|
+
|
19
|
+
# Set whether the cache is enabled.
|
20
|
+
#
|
21
|
+
# @example Set if the cache is enabled.
|
22
|
+
# QueryCache.enabled = true
|
23
|
+
#
|
24
|
+
# @param [ true, false ] value The enabled value.
|
25
|
+
def enabled=(value)
|
26
|
+
Thread.current["[mongo]:query_cache:enabled"] = value
|
27
|
+
end
|
28
|
+
|
29
|
+
# Is the query cache enabled on the current thread?
|
30
|
+
#
|
31
|
+
# @example Is the query cache enabled?
|
32
|
+
# QueryCache.enabled?
|
33
|
+
#
|
34
|
+
# @return [ true, false ] If the cache is enabled.
|
35
|
+
def enabled?
|
36
|
+
!!Thread.current["[mongo]:query_cache:enabled"]
|
37
|
+
end
|
38
|
+
|
39
|
+
# Execute the block while using the query cache.
|
40
|
+
#
|
41
|
+
# @example Execute with the cache.
|
42
|
+
# QueryCache.cache { collection.find }
|
43
|
+
#
|
44
|
+
# @return [ Object ] The result of the block.
|
45
|
+
def cache
|
46
|
+
enabled = enabled?
|
47
|
+
self.enabled = true
|
48
|
+
begin
|
49
|
+
yield
|
50
|
+
ensure
|
51
|
+
self.enabled = enabled
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Execute the block with the query cache disabled.
|
56
|
+
#
|
57
|
+
# @example Execute without the cache.
|
58
|
+
# QueryCache.uncached { collection.find }
|
59
|
+
#
|
60
|
+
# @return [ Object ] The result of the block.
|
61
|
+
def uncached
|
62
|
+
enabled = enabled?
|
63
|
+
self.enabled = false
|
64
|
+
begin
|
65
|
+
yield
|
66
|
+
ensure
|
67
|
+
self.enabled = enabled
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Get the cached queries.
|
72
|
+
#
|
73
|
+
# @example Get the cached queries from the current thread.
|
74
|
+
# QueryCache.cache_table
|
75
|
+
#
|
76
|
+
# @return [ Hash ] The hash of cached queries.
|
77
|
+
private def cache_table
|
78
|
+
Thread.current["[mongo]:query_cache"] ||= {}
|
79
|
+
end
|
80
|
+
|
81
|
+
# Clear the query cache.
|
82
|
+
#
|
83
|
+
# @example Clear the cache.
|
84
|
+
# QueryCache.clear
|
85
|
+
#
|
86
|
+
# @return [ nil ] Always nil.
|
87
|
+
def clear
|
88
|
+
Thread.current["[mongo]:query_cache"] = nil
|
89
|
+
end
|
90
|
+
|
91
|
+
# Clear the section of the query cache storing cursors with results
|
92
|
+
# from this namespace.
|
93
|
+
#
|
94
|
+
# @param [ String ] namespace The namespace to be cleared, in the format
|
95
|
+
# "database.collection".
|
96
|
+
#
|
97
|
+
# @return [ nil ] Always nil.
|
98
|
+
#
|
99
|
+
# @api private
|
100
|
+
def clear_namespace(namespace)
|
101
|
+
cache_table.delete(namespace)
|
102
|
+
# The nil key is where cursors are stored that could potentially read from
|
103
|
+
# multiple collections. This key should be cleared on every write operation
|
104
|
+
# to prevent returning stale data.
|
105
|
+
cache_table.delete(nil)
|
106
|
+
nil
|
107
|
+
end
|
108
|
+
|
109
|
+
# Store a CachingCursor instance in the query cache associated with the
|
110
|
+
# specified query options.
|
111
|
+
#
|
112
|
+
# @param [ Mongo::CachingCursor ] cursor The CachingCursor instance to store.
|
113
|
+
#
|
114
|
+
# @option opts [ String | nil ] namespace The namespace of the query,
|
115
|
+
# in the format "database_name.collection_name".
|
116
|
+
# @option opts [ Array, Hash ] selector The selector passed to the query.
|
117
|
+
# For most queries, this will be a Hash, but for aggregations, this
|
118
|
+
# will be an Array representing the aggregation pipeline. May not be nil.
|
119
|
+
# @option opts [ Integer | nil ] skip The skip value of the query.
|
120
|
+
# @option opts [ Hash | nil ] sort The order of the query results
|
121
|
+
# (e.g. { name: -1 }).
|
122
|
+
# @option opts [ Integer | nil ] limit The limit value of the query.
|
123
|
+
# @option opts [ Hash | nil ] projection The projection of the query
|
124
|
+
# results (e.g. { name: 1 }).
|
125
|
+
# @option opts [ Hash | nil ] collation The collation of the query
|
126
|
+
# (e.g. { "locale" => "fr_CA" }).
|
127
|
+
# @option opts [ Hash | nil ] read_concern The read concern of the query
|
128
|
+
# (e.g. { level: :majority }).
|
129
|
+
# @option opts [ Hash | nil ] read_preference The read preference of
|
130
|
+
# the query (e.g. { mode: :secondary }).
|
131
|
+
# @option opts [ Boolean | nil ] multi_collection Whether the query
|
132
|
+
# results could potentially come from multiple collections. When true,
|
133
|
+
# these results will be stored under the nil namespace key and cleared
|
134
|
+
# on every write command.
|
135
|
+
#
|
136
|
+
# @return [ true ] Always true.
|
137
|
+
#
|
138
|
+
# @api private
|
139
|
+
def set(cursor, **opts)
|
140
|
+
_cache_key = cache_key(**opts)
|
141
|
+
_namespace_key = namespace_key(**opts)
|
142
|
+
|
143
|
+
cache_table[_namespace_key] ||= {}
|
144
|
+
cache_table[_namespace_key][_cache_key] = cursor
|
145
|
+
|
146
|
+
true
|
147
|
+
end
|
148
|
+
|
149
|
+
# For the given query options, retrieve a cached cursor that can be used
|
150
|
+
# to obtain the correct query results, if one exists in the cache.
|
151
|
+
#
|
152
|
+
# @option opts [ String | nil ] namespace The namespace of the query,
|
153
|
+
# in the format "database_name.collection_name".
|
154
|
+
# @option opts [ Array, Hash ] selector The selector passed to the query.
|
155
|
+
# For most queries, this will be a Hash, but for aggregations, this
|
156
|
+
# will be an Array representing the aggregation pipeline. May not be nil.
|
157
|
+
# @option opts [ Integer | nil ] skip The skip value of the query.
|
158
|
+
# @option opts [ Hash | nil ] sort The order of the query results
|
159
|
+
# (e.g. { name: -1 }).
|
160
|
+
# @option opts [ Integer | nil ] limit The limit value of the query.
|
161
|
+
# @option opts [ Hash | nil ] projection The projection of the query
|
162
|
+
# results (e.g. { name: 1 }).
|
163
|
+
# @option opts [ Hash | nil ] collation The collation of the query
|
164
|
+
# (e.g. { "locale" => "fr_CA" }).
|
165
|
+
# @option opts [ Hash | nil ] read_concern The read concern of the query
|
166
|
+
# (e.g. { level: :majority }).
|
167
|
+
# @option opts [ Hash | nil ] read_preference The read preference of
|
168
|
+
# the query (e.g. { mode: :secondary }).
|
169
|
+
# @option opts [ Boolean | nil ] multi_collection Whether the query
|
170
|
+
# results could potentially come from multiple collections. When true,
|
171
|
+
# these results will be stored under the nil namespace key and cleared
|
172
|
+
# on every write command.
|
173
|
+
#
|
174
|
+
# @return [ Mongo::CachingCursor | nil ] Returns a CachingCursor if one
|
175
|
+
# exists in the query cache, otherwise returns nil.
|
176
|
+
#
|
177
|
+
# @api private
|
178
|
+
def get(**opts)
|
179
|
+
limit = opts[:limit]
|
180
|
+
_namespace_key = namespace_key(**opts)
|
181
|
+
_cache_key = cache_key(**opts)
|
182
|
+
|
183
|
+
namespace_hash = cache_table[_namespace_key]
|
184
|
+
return nil unless namespace_hash
|
185
|
+
|
186
|
+
caching_cursor = namespace_hash[_cache_key]
|
187
|
+
return nil unless caching_cursor
|
188
|
+
|
189
|
+
caching_cursor_limit = caching_cursor.view.limit
|
190
|
+
|
191
|
+
# There are two scenarios in which a caching cursor could fulfill the
|
192
|
+
# query:
|
193
|
+
# 1. The query has a limit, and the stored cursor has no limit or
|
194
|
+
# a larger limit.
|
195
|
+
# 2. The query has no limit and the stored cursor has no limit.
|
196
|
+
#
|
197
|
+
# Otherwise, return nil because the stored cursor will not satisfy
|
198
|
+
# the query.
|
199
|
+
if limit && (caching_cursor_limit.nil? || caching_cursor_limit >= limit)
|
200
|
+
caching_cursor
|
201
|
+
elsif limit.nil? && caching_cursor_limit.nil?
|
202
|
+
caching_cursor
|
203
|
+
else
|
204
|
+
nil
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
private
|
209
|
+
|
210
|
+
def cache_key(**opts)
|
211
|
+
unless opts[:namespace]
|
212
|
+
raise ArgumentError.new("Cannot generate cache key without namespace")
|
213
|
+
end
|
214
|
+
unless opts[:selector]
|
215
|
+
raise ArgumentError.new("Cannot generate cache key without selector")
|
216
|
+
end
|
217
|
+
|
218
|
+
[
|
219
|
+
opts[:namespace],
|
220
|
+
opts[:selector],
|
221
|
+
opts[:skip],
|
222
|
+
opts[:sort],
|
223
|
+
opts[:projection],
|
224
|
+
opts[:collation],
|
225
|
+
opts[:read_concern],
|
226
|
+
opts[:read_preference]
|
227
|
+
]
|
228
|
+
end
|
229
|
+
|
230
|
+
# If the cached results can come from multiple collections, store this
|
231
|
+
# cursor under the nil namespace to be cleared on every write operation.
|
232
|
+
# Otherwise, store it under the specified namespace.
|
233
|
+
def namespace_key(**opts)
|
234
|
+
if opts[:multi_collection]
|
235
|
+
nil
|
236
|
+
else
|
237
|
+
opts[:namespace]
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
data/lib/mongo/retryable.rb
CHANGED
@@ -59,7 +59,14 @@ module Mongo
|
|
59
59
|
def read_with_retry_cursor(session, server_selector, view, &block)
|
60
60
|
read_with_retry(session, server_selector) do |server|
|
61
61
|
result = yield server
|
62
|
-
|
62
|
+
|
63
|
+
# RUBY-2367: This will be updated to allow the query cache to
|
64
|
+
# cache cursors with multi-batch results.
|
65
|
+
if QueryCache.enabled? && !view.collection.system_collection?
|
66
|
+
CachingCursor.new(view, result, server, session: session)
|
67
|
+
else
|
68
|
+
Cursor.new(view, result, server, session: session)
|
69
|
+
end
|
63
70
|
end
|
64
71
|
end
|
65
72
|
|
@@ -105,8 +105,8 @@ module Mongo
|
|
105
105
|
options.select { |k, v| k.to_s.start_with?('ssl') }
|
106
106
|
else
|
107
107
|
# Due to the way options are propagated from the client, if we
|
108
|
-
# decide that we don't want to use TLS we need to have the ssl
|
109
|
-
#
|
108
|
+
# decide that we don't want to use TLS we need to have the :ssl
|
109
|
+
# option explicitly set to false or the value provided to the
|
110
110
|
# connection might be overwritten by the default inherited from
|
111
111
|
# the client.
|
112
112
|
{ssl: false}
|