mongo 2.3.1 → 2.4.0.rc0
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.tar.gz.sig +2 -3
- data/lib/mongo/bulk_write.rb +8 -7
- data/lib/mongo/bulk_write/combineable.rb +4 -0
- data/lib/mongo/bulk_write/transformable.rb +17 -5
- data/lib/mongo/bulk_write/validatable.rb +1 -0
- data/lib/mongo/client.rb +3 -0
- data/lib/mongo/cluster.rb +8 -0
- data/lib/mongo/cluster/app_metadata.rb +135 -0
- data/lib/mongo/collection.rb +42 -10
- data/lib/mongo/collection/view.rb +15 -1
- data/lib/mongo/collection/view/aggregation.rb +5 -0
- data/lib/mongo/collection/view/builder/aggregation.rb +13 -3
- data/lib/mongo/collection/view/builder/find_command.rb +7 -21
- data/lib/mongo/collection/view/builder/map_reduce.rb +22 -5
- data/lib/mongo/collection/view/iterable.rb +1 -0
- data/lib/mongo/collection/view/map_reduce.rb +5 -0
- data/lib/mongo/collection/view/readable.rb +35 -14
- data/lib/mongo/collection/view/writable.rb +54 -23
- data/lib/mongo/cursor/builder/get_more_command.rb +2 -3
- data/lib/mongo/database.rb +10 -2
- data/lib/mongo/error.rb +2 -0
- data/lib/mongo/error/invalid_application_name.rb +38 -0
- data/lib/mongo/error/invalid_server_preference.rb +24 -3
- data/lib/mongo/error/unsupported_collation.rb +51 -0
- data/lib/mongo/index/view.rb +28 -15
- data/lib/mongo/operation.rb +6 -0
- data/lib/mongo/operation/commands.rb +3 -0
- data/lib/mongo/operation/commands/aggregate.rb +10 -10
- data/lib/mongo/operation/commands/create.rb +45 -0
- data/lib/mongo/operation/commands/drop.rb +45 -0
- data/lib/mongo/operation/commands/drop_database.rb +45 -0
- data/lib/mongo/operation/commands/map_reduce.rb +12 -1
- data/lib/mongo/operation/commands/parallel_scan.rb +1 -0
- data/lib/mongo/operation/read_preference.rb +9 -9
- data/lib/mongo/operation/specifiable.rb +34 -0
- data/lib/mongo/operation/takes_write_concern.rb +35 -0
- data/lib/mongo/operation/write/bulk/bulkable.rb +1 -1
- data/lib/mongo/operation/write/command/create_index.rb +6 -0
- data/lib/mongo/operation/write/command/drop_index.rb +6 -0
- data/lib/mongo/operation/write/command/insert.rb +1 -1
- data/lib/mongo/operation/write/command/update.rb +1 -0
- data/lib/mongo/operation/write/command/writable.rb +2 -2
- data/lib/mongo/operation/write/create_index.rb +2 -2
- data/lib/mongo/operation/write/create_user.rb +1 -1
- data/lib/mongo/operation/write/delete.rb +5 -1
- data/lib/mongo/operation/write/gle.rb +1 -1
- data/lib/mongo/operation/write/insert.rb +2 -2
- data/lib/mongo/operation/write/remove_user.rb +1 -1
- data/lib/mongo/operation/write/update.rb +5 -1
- data/lib/mongo/operation/write/update_user.rb +1 -1
- data/lib/mongo/operation/write/write_command_enabled.rb +10 -2
- data/lib/mongo/protocol/insert.rb +1 -2
- data/lib/mongo/protocol/query.rb +3 -7
- data/lib/mongo/server.rb +8 -3
- data/lib/mongo/server/connection.rb +17 -11
- data/lib/mongo/server/description.rb +22 -0
- data/lib/mongo/server/description/features.rb +2 -0
- data/lib/mongo/server/monitor.rb +5 -0
- data/lib/mongo/server/monitor/connection.rb +11 -0
- data/lib/mongo/server_selector/nearest.rb +9 -6
- data/lib/mongo/server_selector/primary.rb +4 -0
- data/lib/mongo/server_selector/primary_preferred.rb +7 -1
- data/lib/mongo/server_selector/secondary.rb +5 -0
- data/lib/mongo/server_selector/secondary_preferred.rb +7 -2
- data/lib/mongo/server_selector/selectable.rb +57 -10
- data/lib/mongo/socket/ssl.rb +1 -0
- data/lib/mongo/uri.rb +4 -0
- data/lib/mongo/version.rb +1 -1
- data/lib/mongo/write_concern.rb +1 -0
- data/mongo.gemspec +1 -1
- data/spec/mongo/auth/cr_spec.rb +7 -1
- data/spec/mongo/auth/ldap_spec.rb +7 -1
- data/spec/mongo/auth/scram_spec.rb +7 -1
- data/spec/mongo/auth/x509_spec.rb +7 -1
- data/spec/mongo/bulk_write_spec.rb +598 -5
- data/spec/mongo/client_spec.rb +47 -1
- data/spec/mongo/cluster/app_metadata_spec.rb +104 -0
- data/spec/mongo/cluster/topology/replica_set_spec.rb +14 -8
- data/spec/mongo/cluster/topology/sharded_spec.rb +9 -3
- data/spec/mongo/cluster/topology/single_spec.rb +10 -4
- data/spec/mongo/cluster_spec.rb +29 -0
- data/spec/mongo/collection/view/aggregation_spec.rb +139 -0
- data/spec/mongo/collection/view/builder/find_command_spec.rb +6 -243
- data/spec/mongo/collection/view/map_reduce_spec.rb +104 -0
- data/spec/mongo/collection/view/readable_spec.rb +83 -0
- data/spec/mongo/collection/view/writable_spec.rb +447 -1
- data/spec/mongo/collection/view_spec.rb +57 -0
- data/spec/mongo/collection_spec.rb +926 -101
- data/spec/mongo/crud_spec.rb +4 -5
- data/spec/mongo/database_spec.rb +99 -1
- data/spec/mongo/index/view_spec.rb +360 -31
- data/spec/mongo/max_staleness_spec.rb +108 -0
- data/spec/mongo/operation/read_preference_spec.rb +8 -8
- data/spec/mongo/operation/write/command/delete_spec.rb +1 -1
- data/spec/mongo/operation/write/command/insert_spec.rb +1 -1
- data/spec/mongo/operation/write/command/update_spec.rb +1 -1
- data/spec/mongo/server/connection_pool_spec.rb +3 -1
- data/spec/mongo/server/connection_spec.rb +17 -7
- data/spec/mongo/server/description/features_spec.rb +50 -0
- data/spec/mongo/server/description_spec.rb +9 -3
- data/spec/mongo/server_selection_spec.rb +5 -3
- data/spec/mongo/server_selector/nearest_spec.rb +73 -0
- data/spec/mongo/server_selector/primary_preferred_spec.rb +73 -0
- data/spec/mongo/server_selector/primary_spec.rb +36 -0
- data/spec/mongo/server_selector/secondary_preferred_spec.rb +73 -0
- data/spec/mongo/server_selector/secondary_spec.rb +73 -0
- data/spec/mongo/server_selector_spec.rb +53 -0
- data/spec/mongo/server_spec.rb +3 -1
- data/spec/mongo/uri_spec.rb +54 -0
- data/spec/mongo/write_concern_spec.rb +18 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/support/authorization.rb +8 -1
- data/spec/support/crud.rb +15 -0
- data/spec/support/crud/read.rb +27 -19
- data/spec/support/crud/write.rb +28 -3
- data/spec/support/crud_tests/read/aggregate.yml +15 -3
- data/spec/support/crud_tests/read/count.yml +14 -3
- data/spec/support/crud_tests/read/distinct.yml +13 -1
- data/spec/support/crud_tests/read/find.yml +12 -2
- data/spec/support/crud_tests/write/deleteMany.yml +22 -1
- data/spec/support/crud_tests/write/deleteOne.yml +20 -1
- data/spec/support/crud_tests/write/findOneAndDelete.yml +27 -2
- data/spec/support/crud_tests/write/findOneAndReplace.yml +43 -14
- data/spec/support/crud_tests/write/findOneAndUpdate.yml +50 -8
- data/spec/support/crud_tests/write/replaceOne.yml +34 -10
- data/spec/support/crud_tests/write/updateMany.yml +42 -11
- data/spec/support/crud_tests/write/updateOne.yml +32 -7
- data/spec/support/max_staleness/ReplicaSetNoPrimary/DefaultNoMaxStaleness.yml +26 -0
- data/spec/support/max_staleness/ReplicaSetNoPrimary/Incompatible.yml +25 -0
- data/spec/support/max_staleness/ReplicaSetNoPrimary/LastUpdateTime.yml +33 -0
- data/spec/support/max_staleness/ReplicaSetNoPrimary/Nearest.yml +33 -0
- data/spec/support/max_staleness/ReplicaSetNoPrimary/Nearest2.yml +33 -0
- data/spec/support/max_staleness/ReplicaSetNoPrimary/PrimaryPreferred.yml +27 -0
- data/spec/support/max_staleness/ReplicaSetNoPrimary/PrimaryPreferred_tags.yml +36 -0
- data/spec/support/max_staleness/ReplicaSetNoPrimary/Secondary.yml +51 -0
- data/spec/support/max_staleness/ReplicaSetNoPrimary/SecondaryPreferred.yml +26 -0
- data/spec/support/max_staleness/ReplicaSetNoPrimary/SecondaryPreferred_tags.yml +51 -0
- data/spec/support/max_staleness/ReplicaSetWithPrimary/DefaultNoMaxStaleness.yml +26 -0
- data/spec/support/max_staleness/ReplicaSetWithPrimary/Incompatible.yml +25 -0
- data/spec/support/max_staleness/ReplicaSetWithPrimary/LastUpdateTime.yml +35 -0
- data/spec/support/max_staleness/ReplicaSetWithPrimary/MaxStalenessTooSmall.yml +25 -0
- data/spec/support/max_staleness/ReplicaSetWithPrimary/MaxStalenessWithModePrimary.yml +23 -0
- data/spec/support/max_staleness/ReplicaSetWithPrimary/Nearest.yml +33 -0
- data/spec/support/max_staleness/ReplicaSetWithPrimary/Nearest2.yml +33 -0
- data/spec/support/max_staleness/ReplicaSetWithPrimary/Nearest_tags.yml +36 -0
- data/spec/support/max_staleness/ReplicaSetWithPrimary/PrimaryPreferred.yml +27 -0
- data/spec/support/max_staleness/ReplicaSetWithPrimary/PrimaryPreferred_incompatible.yml +27 -0
- data/spec/support/max_staleness/ReplicaSetWithPrimary/SecondaryPreferred.yml +26 -0
- data/spec/support/max_staleness/ReplicaSetWithPrimary/SecondaryPreferred_tags.yml +59 -0
- data/spec/support/max_staleness/ReplicaSetWithPrimary/SecondaryPreferred_tags2.yml +43 -0
- data/spec/support/max_staleness/ReplicaSetWithPrimary/Secondary_tags.yml +59 -0
- data/spec/support/max_staleness/ReplicaSetWithPrimary/Secondary_tags2.yml +43 -0
- data/spec/support/max_staleness/ReplicaSetWithPrimary/ShortHeartbeartShortMaxStaleness.yml +29 -0
- data/spec/support/max_staleness/ReplicaSetWithPrimary/ShortHeartbeartShortMaxStaleness2.yml +29 -0
- data/spec/support/max_staleness/ReplicaSetWithPrimary/ZeroMaxStaleness.yml +27 -0
- data/spec/support/max_staleness/Sharded/Incompatible.yml +25 -0
- data/spec/support/max_staleness/Sharded/SmallMaxStaleness.yml +20 -0
- data/spec/support/max_staleness/Single/Incompatible.yml +18 -0
- data/spec/support/max_staleness/Single/SmallMaxStaleness.yml +20 -0
- data/spec/support/server_selection.rb +25 -0
- data/spec/support/server_selection/selection/ReplicaSetNoPrimary/read/Nearest_multiple.yml +27 -0
- data/spec/support/server_selection/selection/ReplicaSetNoPrimary/read/Secondary_multi_tags.yml +31 -0
- data/spec/support/server_selection/selection/ReplicaSetNoPrimary/read/Secondary_multi_tags2.yml +31 -0
- data/spec/support/server_selection/selection/ReplicaSetWithPrimary/read/Nearest_multiple.yml +34 -0
- data/spec/support/server_selection/selection/ReplicaSetWithPrimary/read/SecondaryPreferred_tags.yml +28 -0
- data/spec/support/shared/server_selector.rb +4 -3
- metadata +91 -6
- metadata.gz.sig +0 -0
@@ -43,7 +43,8 @@ module Mongo
|
|
43
43
|
:features,
|
44
44
|
:max_bson_object_size,
|
45
45
|
:max_message_size,
|
46
|
-
:mongos
|
46
|
+
:mongos?,
|
47
|
+
:app_metadata
|
47
48
|
|
48
49
|
# Tell the underlying socket to establish a connection to the host.
|
49
50
|
#
|
@@ -60,6 +61,7 @@ module Mongo
|
|
60
61
|
unless socket && socket.connectable?
|
61
62
|
@socket = address.socket(timeout, ssl_options)
|
62
63
|
socket.connect!
|
64
|
+
handshake!
|
63
65
|
authenticate!
|
64
66
|
end
|
65
67
|
true
|
@@ -79,6 +81,7 @@ module Mongo
|
|
79
81
|
def disconnect!
|
80
82
|
if socket
|
81
83
|
socket.close
|
84
|
+
@auth_mechanism = nil
|
82
85
|
@socket = nil
|
83
86
|
end
|
84
87
|
true
|
@@ -130,6 +133,7 @@ module Mongo
|
|
130
133
|
@server = server
|
131
134
|
@ssl_options = options.reject { |k, v| !k.to_s.start_with?(SSL) }
|
132
135
|
@socket = nil
|
136
|
+
@auth_mechanism = nil
|
133
137
|
@pid = Process.pid
|
134
138
|
end
|
135
139
|
|
@@ -159,6 +163,17 @@ module Mongo
|
|
159
163
|
messages.last.replyable? ? read(messages.last.request_id) : nil
|
160
164
|
end
|
161
165
|
|
166
|
+
def handshake!
|
167
|
+
if socket && socket.connectable?
|
168
|
+
socket.write(app_metadata.ismaster_bytes)
|
169
|
+
response = Protocol::Reply.deserialize(socket, max_message_size).documents[0]
|
170
|
+
min_wire_version = response[Description::MIN_WIRE_VERSION] || Description::LEGACY_WIRE_VERSION
|
171
|
+
max_wire_version = response[Description::MAX_WIRE_VERSION] || Description::LEGACY_WIRE_VERSION
|
172
|
+
features = Description::Features.new(min_wire_version..max_wire_version)
|
173
|
+
@auth_mechanism = (features.scram_sha_1_enabled? || @server.features.scram_sha_1_enabled?) ? :scram : :mongodb_cr
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
162
177
|
def authenticate!
|
163
178
|
if options[:user]
|
164
179
|
user = Auth::User.new(Options::Redacted.new(:auth_mech => default_mechanism).merge(options))
|
@@ -169,16 +184,7 @@ module Mongo
|
|
169
184
|
end
|
170
185
|
|
171
186
|
def default_mechanism
|
172
|
-
|
173
|
-
socket.write(Monitor::Connection::ISMASTER_BYTES)
|
174
|
-
ismaster = Protocol::Reply.deserialize(socket, max_message_size).documents[0]
|
175
|
-
min_wire_version = ismaster[Description::MIN_WIRE_VERSION] || Description::LEGACY_WIRE_VERSION
|
176
|
-
max_wire_version = ismaster[Description::MAX_WIRE_VERSION] || Description::LEGACY_WIRE_VERSION
|
177
|
-
features = Description::Features.new(min_wire_version..max_wire_version)
|
178
|
-
(features.scram_sha_1_enabled? || @server.features.scram_sha_1_enabled?) ? :scram : :mongodb_cr
|
179
|
-
else
|
180
|
-
@server.features.scram_sha_1_enabled? ? :scram : :mongodb_cr
|
181
|
-
end
|
187
|
+
@auth_mechanism || (@server.features.scram_sha_1_enabled? ? :scram : :mongodb_cr)
|
182
188
|
end
|
183
189
|
|
184
190
|
def write(messages, buffer = BSON::ByteBuffer.new)
|
@@ -84,6 +84,16 @@ module Mongo
|
|
84
84
|
# @since 2.0.0
|
85
85
|
MAX_WRITE_BATCH_SIZE = 'maxWriteBatchSize'.freeze
|
86
86
|
|
87
|
+
# Constant for the lastWrite subdocument.
|
88
|
+
#
|
89
|
+
# @since 2.4.0
|
90
|
+
LAST_WRITE = 'lastWrite'.freeze
|
91
|
+
|
92
|
+
# Constant for the lastWriteDate field in the lastWrite subdocument.
|
93
|
+
#
|
94
|
+
# @since 2.4.0
|
95
|
+
LAST_WRITE_DATE = 'lastWriteDate'.freeze
|
96
|
+
|
87
97
|
# Constant for reading the me field.
|
88
98
|
#
|
89
99
|
# @since 2.1.0
|
@@ -360,6 +370,18 @@ module Mongo
|
|
360
370
|
config[SET_VERSION]
|
361
371
|
end
|
362
372
|
|
373
|
+
# Get the lastWriteDate from the lastWrite subdocument in the config.
|
374
|
+
#
|
375
|
+
# @example Get the lastWriteDate value.
|
376
|
+
# description.last_write_date
|
377
|
+
#
|
378
|
+
# @return [ Time ] The last write date.
|
379
|
+
#
|
380
|
+
# @since 2.4.0
|
381
|
+
def last_write_date
|
382
|
+
config[LAST_WRITE][LAST_WRITE_DATE] if config[LAST_WRITE]
|
383
|
+
end
|
384
|
+
|
363
385
|
# Is the server a mongos?
|
364
386
|
#
|
365
387
|
# @example Is the server a mongos?
|
data/lib/mongo/server/monitor.rb
CHANGED
@@ -53,6 +53,11 @@ module Mongo
|
|
53
53
|
# @return [ Hash ] options The server options.
|
54
54
|
attr_reader :options
|
55
55
|
|
56
|
+
# @return [ Time ] last_scan The time of the last server scan.
|
57
|
+
#
|
58
|
+
# @since 2.4.0
|
59
|
+
attr_reader :last_scan
|
60
|
+
|
56
61
|
# Force the monitor to immediately do a check of its server.
|
57
62
|
#
|
58
63
|
# @example Force a scan.
|
@@ -75,6 +75,7 @@ module Mongo
|
|
75
75
|
unless socket && socket.connectable?
|
76
76
|
@socket = address.socket(timeout, ssl_options)
|
77
77
|
socket.connect!
|
78
|
+
handshake!
|
78
79
|
end
|
79
80
|
true
|
80
81
|
end
|
@@ -115,6 +116,7 @@ module Mongo
|
|
115
116
|
def initialize(address, options = {})
|
116
117
|
@address = address
|
117
118
|
@options = options.freeze
|
119
|
+
@app_metadata = options[:app_metadata]
|
118
120
|
@ssl_options = options.reject { |k, v| !k.to_s.start_with?(SSL) }
|
119
121
|
@socket = nil
|
120
122
|
@pid = Process.pid
|
@@ -131,6 +133,15 @@ module Mongo
|
|
131
133
|
def timeout
|
132
134
|
@timeout ||= options[:connect_timeout] || CONNECT_TIMEOUT
|
133
135
|
end
|
136
|
+
|
137
|
+
private
|
138
|
+
|
139
|
+
def handshake!
|
140
|
+
if @app_metadata
|
141
|
+
socket.write(@app_metadata.ismaster_bytes)
|
142
|
+
Protocol::Reply.deserialize(socket, Mongo::Protocol::Message::MAX_MESSAGE_SIZE).documents[0]
|
143
|
+
end
|
144
|
+
end
|
134
145
|
end
|
135
146
|
end
|
136
147
|
end
|
@@ -67,6 +67,7 @@ module Mongo
|
|
67
67
|
def to_mongos
|
68
68
|
preference = { :mode => 'nearest' }
|
69
69
|
preference.merge!({ :tags => tag_sets }) unless tag_sets.empty?
|
70
|
+
preference.merge!({ maxStalenessMS: max_staleness * 1000 }) if max_staleness
|
70
71
|
preference
|
71
72
|
end
|
72
73
|
|
@@ -76,18 +77,20 @@ module Mongo
|
|
76
77
|
# local threshold between the nearest server and other servers.
|
77
78
|
#
|
78
79
|
# @example Select nearest servers given a list of candidates.
|
79
|
-
# preference = Mongo::
|
80
|
+
# preference = Mongo::ServerSelector::Nearest.new
|
80
81
|
# preference.select_server(cluster)
|
81
82
|
#
|
82
83
|
# @return [ Array ] The nearest servers from the list of candidates.
|
83
84
|
#
|
84
85
|
# @since 2.0.0
|
85
86
|
def select(candidates)
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
87
|
+
matching_servers = filter_stale_servers(candidates, primary(candidates).first)
|
88
|
+
matching_servers = match_tag_sets(matching_servers) unless tag_sets.empty?
|
89
|
+
near_servers(matching_servers)
|
90
|
+
end
|
91
|
+
|
92
|
+
def max_staleness_allowed?
|
93
|
+
true
|
91
94
|
end
|
92
95
|
end
|
93
96
|
end
|
@@ -68,6 +68,7 @@ module Mongo
|
|
68
68
|
def to_mongos
|
69
69
|
preference = { :mode => 'primaryPreferred' }
|
70
70
|
preference.merge!({ :tags => tag_sets }) unless tag_sets.empty?
|
71
|
+
preference.merge!({ maxStalenessMS: max_staleness * 1000 }) if max_staleness
|
71
72
|
preference
|
72
73
|
end
|
73
74
|
|
@@ -87,7 +88,12 @@ module Mongo
|
|
87
88
|
# @since 2.0.0
|
88
89
|
def select(candidates)
|
89
90
|
primary = primary(candidates)
|
90
|
-
|
91
|
+
secondaries = near_servers(secondaries(candidates))
|
92
|
+
primary.first ? primary : secondaries
|
93
|
+
end
|
94
|
+
|
95
|
+
def max_staleness_allowed?
|
96
|
+
true
|
91
97
|
end
|
92
98
|
end
|
93
99
|
end
|
@@ -68,6 +68,7 @@ module Mongo
|
|
68
68
|
def to_mongos
|
69
69
|
preference = { :mode => 'secondary' }
|
70
70
|
preference.merge!({ :tags => tag_sets }) unless tag_sets.empty?
|
71
|
+
preference.merge!({ maxStalenessMS: max_staleness * 1000 }) if max_staleness
|
71
72
|
preference
|
72
73
|
end
|
73
74
|
|
@@ -86,6 +87,10 @@ module Mongo
|
|
86
87
|
def select(candidates)
|
87
88
|
near_servers(secondaries(candidates))
|
88
89
|
end
|
90
|
+
|
91
|
+
def max_staleness_allowed?
|
92
|
+
true
|
93
|
+
end
|
89
94
|
end
|
90
95
|
end
|
91
96
|
end
|
@@ -68,9 +68,10 @@ module Mongo
|
|
68
68
|
#
|
69
69
|
# @since 2.0.0
|
70
70
|
def to_mongos
|
71
|
-
return nil if tag_sets.empty?
|
71
|
+
return nil if tag_sets.empty? && max_staleness.nil?
|
72
72
|
preference = { mode: 'secondaryPreferred' }
|
73
|
-
preference.merge!({ tags: tag_sets })
|
73
|
+
preference.merge!({ tags: tag_sets }) unless tag_sets.empty?
|
74
|
+
preference.merge!({ maxStalenessMS: max_staleness * 1000 }) if max_staleness
|
74
75
|
preference
|
75
76
|
end
|
76
77
|
|
@@ -91,6 +92,10 @@ module Mongo
|
|
91
92
|
def select(candidates)
|
92
93
|
near_servers(secondaries(candidates)) + primary(candidates)
|
93
94
|
end
|
95
|
+
|
96
|
+
def max_staleness_allowed?
|
97
|
+
true
|
98
|
+
end
|
94
99
|
end
|
95
100
|
end
|
96
101
|
end
|
@@ -26,6 +26,12 @@ module Mongo
|
|
26
26
|
# @return [ Array ] tag_sets The tag sets used to select servers.
|
27
27
|
attr_reader :tag_sets
|
28
28
|
|
29
|
+
# @return [ Float ] max_staleness The maximum replication lag, in seconds, that a
|
30
|
+
# secondary can suffer and still be eligible for a read.
|
31
|
+
#
|
32
|
+
# @since 2.4.0
|
33
|
+
attr_reader :max_staleness
|
34
|
+
|
29
35
|
# Check equality of two server selector.
|
30
36
|
#
|
31
37
|
# @example Check server selector equality.
|
@@ -38,7 +44,8 @@ module Mongo
|
|
38
44
|
# @since 2.0.0
|
39
45
|
def ==(other)
|
40
46
|
name == other.name &&
|
41
|
-
tag_sets == other.tag_sets
|
47
|
+
tag_sets == other.tag_sets &&
|
48
|
+
max_staleness == other.max_staleness
|
42
49
|
end
|
43
50
|
|
44
51
|
# Initialize the server selector.
|
@@ -60,9 +67,9 @@ module Mongo
|
|
60
67
|
# @since 2.0.0
|
61
68
|
def initialize(options = {})
|
62
69
|
@options = (options || {}).freeze
|
63
|
-
tag_sets = options[:tag_sets] || []
|
64
|
-
|
65
|
-
|
70
|
+
@tag_sets = (options[:tag_sets] || []).freeze
|
71
|
+
@max_staleness = options[:max_staleness] if options[:max_staleness] && options[:max_staleness] > 0
|
72
|
+
validate!
|
66
73
|
end
|
67
74
|
|
68
75
|
# Inspect the server selector.
|
@@ -74,7 +81,7 @@ module Mongo
|
|
74
81
|
#
|
75
82
|
# @since 2.2.0
|
76
83
|
def inspect
|
77
|
-
"#<#{self.class.name}:0x#{object_id} tag_sets=#{tag_sets.inspect}>"
|
84
|
+
"#<#{self.class.name}:0x#{object_id} tag_sets=#{tag_sets.inspect} max_staleness=#{max_staleness.inspect}>"
|
78
85
|
end
|
79
86
|
|
80
87
|
# Select a server from eligible candidates.
|
@@ -143,10 +150,11 @@ module Mongo
|
|
143
150
|
|
144
151
|
def candidates(cluster)
|
145
152
|
if cluster.single?
|
146
|
-
cluster.servers
|
153
|
+
cluster.servers.each { |server| validate_max_staleness_support!(server) }
|
147
154
|
elsif cluster.sharded?
|
148
|
-
near_servers(cluster.servers)
|
155
|
+
near_servers(cluster.servers).each { |server| validate_max_staleness_support!(server) }
|
149
156
|
else
|
157
|
+
validate_max_staleness_value!(cluster)
|
150
158
|
select(cluster.servers)
|
151
159
|
end
|
152
160
|
end
|
@@ -175,6 +183,7 @@ module Mongo
|
|
175
183
|
# @since 2.0.0
|
176
184
|
def secondaries(candidates)
|
177
185
|
matching_servers = candidates.select(&:secondary?)
|
186
|
+
matching_servers = filter_stale_servers(matching_servers, primary(candidates).first)
|
178
187
|
matching_servers = match_tag_sets(matching_servers) unless tag_sets.empty?
|
179
188
|
matching_servers
|
180
189
|
end
|
@@ -212,9 +221,47 @@ module Mongo
|
|
212
221
|
matches || []
|
213
222
|
end
|
214
223
|
|
215
|
-
def
|
216
|
-
|
217
|
-
|
224
|
+
def filter_stale_servers(candidates, primary = nil)
|
225
|
+
return candidates unless @max_staleness
|
226
|
+
max_staleness_ms = @max_staleness * 1000
|
227
|
+
|
228
|
+
if primary
|
229
|
+
candidates.select do |server|
|
230
|
+
validate_max_staleness_support!(server)
|
231
|
+
staleness = (server.last_scan - server.last_write_date) -
|
232
|
+
(primary.last_scan - primary.last_write_date) +
|
233
|
+
(server.heartbeat_frequency * 1000)
|
234
|
+
staleness <= max_staleness_ms
|
235
|
+
end
|
236
|
+
else
|
237
|
+
max_write_date = candidates.collect(&:last_write_date).max
|
238
|
+
candidates.select do |server|
|
239
|
+
validate_max_staleness_support!(server)
|
240
|
+
staleness = max_write_date - server.last_write_date + (server.heartbeat_frequency * 1000)
|
241
|
+
staleness <= max_staleness_ms
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def validate!
|
247
|
+
if !@tag_sets.all? { |set| set.empty? } && !tags_allowed?
|
248
|
+
raise Error::InvalidServerPreference.new(Error::InvalidServerPreference::NO_TAG_SUPPORT)
|
249
|
+
elsif @max_staleness && !max_staleness_allowed?
|
250
|
+
raise Error::InvalidServerPreference.new(Error::InvalidServerPreference::NO_MAX_STALENESS_SUPPORT)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def validate_max_staleness_support!(server)
|
255
|
+
if @max_staleness && !server.features.max_staleness_enabled?
|
256
|
+
raise Error::InvalidServerPreference.new(Error::InvalidServerPreference::NO_MAX_STALENESS_WITH_LEGACY_SERVER)
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
def validate_max_staleness_value!(cluster)
|
261
|
+
return unless @max_staleness
|
262
|
+
heartbeat_frequency = cluster.options[:heartbeat_frequency] || Server::Monitor::HEARTBEAT_FREQUENCY
|
263
|
+
if @max_staleness < heartbeat_frequency * 2
|
264
|
+
raise Error::InvalidServerPreference.new(Error::InvalidServerPreference::INVALID_MAX_STALENESS)
|
218
265
|
end
|
219
266
|
end
|
220
267
|
end
|
data/lib/mongo/socket/ssl.rb
CHANGED
@@ -56,6 +56,7 @@ module Mongo
|
|
56
56
|
Timeout.timeout(timeout, Error::SocketTimeoutError) do
|
57
57
|
handle_errors { @tcp_socket.connect(::Socket.pack_sockaddr_in(port, host)) }
|
58
58
|
@socket = OpenSSL::SSL::SSLSocket.new(@tcp_socket, context)
|
59
|
+
@socket.hostname = @host_name unless BSON::Environment.jruby?
|
59
60
|
@socket.sync_close = true
|
60
61
|
handle_errors { @socket.connect }
|
61
62
|
verify_certificate!(@socket)
|
data/lib/mongo/uri.rb
CHANGED
@@ -374,6 +374,7 @@ module Mongo
|
|
374
374
|
# Read Options
|
375
375
|
uri_option 'readpreference', :mode, :group => :read, :type => :read_mode
|
376
376
|
uri_option 'readpreferencetags', :tag_sets, :group => :read, :type => :read_tags
|
377
|
+
uri_option 'maxstalenessms', :max_staleness, :group => :read, :type => :ms_convert
|
377
378
|
|
378
379
|
# Pool options
|
379
380
|
uri_option 'minpoolsize', :min_pool_size
|
@@ -391,6 +392,9 @@ module Mongo
|
|
391
392
|
uri_option 'authmechanism', :auth_mech, :type => :auth_mech
|
392
393
|
uri_option 'authmechanismproperties', :auth_mech_properties, :type => :auth_mech_props
|
393
394
|
|
395
|
+
# Client Options
|
396
|
+
uri_option 'appname', :app_name
|
397
|
+
|
394
398
|
# Casts option values that do not have a specifically provided
|
395
399
|
# transformation to the appropriate type.
|
396
400
|
#
|
data/lib/mongo/version.rb
CHANGED
data/lib/mongo/write_concern.rb
CHANGED
data/mongo.gemspec
CHANGED
data/spec/mongo/auth/cr_spec.rb
CHANGED
@@ -14,8 +14,14 @@ describe Mongo::Auth::CR do
|
|
14
14
|
Mongo::Event::Listeners.new
|
15
15
|
end
|
16
16
|
|
17
|
+
let(:cluster) do
|
18
|
+
double('cluster').tap do |cl|
|
19
|
+
allow(cl).to receive(:app_metadata).and_return(app_metadata)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
17
23
|
let(:server) do
|
18
|
-
Mongo::Server.new(address,
|
24
|
+
Mongo::Server.new(address, cluster, monitoring, listeners, TEST_OPTIONS)
|
19
25
|
end
|
20
26
|
|
21
27
|
let(:connection) do
|