mongo 2.13.0.beta1 → 2.13.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (170) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +1 -5
  4. data/Rakefile +15 -9
  5. data/lib/mongo.rb +4 -2
  6. data/lib/mongo/auth/aws/request.rb +4 -2
  7. data/lib/mongo/bulk_write.rb +1 -0
  8. data/lib/mongo/client.rb +143 -21
  9. data/lib/mongo/cluster.rb +53 -17
  10. data/lib/mongo/cluster/sdam_flow.rb +13 -10
  11. data/lib/mongo/cluster/topology/replica_set_no_primary.rb +3 -2
  12. data/lib/mongo/cluster/topology/sharded.rb +1 -1
  13. data/lib/mongo/cluster/topology/single.rb +1 -1
  14. data/lib/mongo/collection.rb +17 -13
  15. data/lib/mongo/collection/view/readable.rb +3 -1
  16. data/lib/mongo/collection/view/writable.rb +41 -5
  17. data/lib/mongo/database.rb +31 -4
  18. data/lib/mongo/database/view.rb +19 -4
  19. data/lib/mongo/distinguishing_semaphore.rb +55 -0
  20. data/lib/mongo/error.rb +1 -0
  21. data/lib/mongo/error/invalid_session.rb +2 -1
  22. data/lib/mongo/error/operation_failure.rb +6 -0
  23. data/lib/mongo/error/sessions_not_supported.rb +35 -0
  24. data/lib/mongo/event/base.rb +6 -0
  25. data/lib/mongo/grid/file.rb +5 -0
  26. data/lib/mongo/grid/file/chunk.rb +2 -0
  27. data/lib/mongo/grid/fs_bucket.rb +15 -13
  28. data/lib/mongo/grid/stream/write.rb +9 -3
  29. data/lib/mongo/monitoring.rb +38 -0
  30. data/lib/mongo/monitoring/command_log_subscriber.rb +10 -2
  31. data/lib/mongo/monitoring/event/command_failed.rb +11 -0
  32. data/lib/mongo/monitoring/event/command_started.rb +37 -2
  33. data/lib/mongo/monitoring/event/command_succeeded.rb +11 -0
  34. data/lib/mongo/monitoring/event/server_closed.rb +1 -1
  35. data/lib/mongo/monitoring/event/server_description_changed.rb +27 -4
  36. data/lib/mongo/monitoring/event/server_heartbeat_failed.rb +9 -2
  37. data/lib/mongo/monitoring/event/server_heartbeat_started.rb +9 -2
  38. data/lib/mongo/monitoring/event/server_heartbeat_succeeded.rb +9 -2
  39. data/lib/mongo/monitoring/event/server_opening.rb +1 -1
  40. data/lib/mongo/monitoring/event/topology_changed.rb +1 -1
  41. data/lib/mongo/monitoring/event/topology_closed.rb +1 -1
  42. data/lib/mongo/monitoring/event/topology_opening.rb +1 -1
  43. data/lib/mongo/monitoring/publishable.rb +6 -3
  44. data/lib/mongo/monitoring/server_description_changed_log_subscriber.rb +9 -1
  45. data/lib/mongo/monitoring/topology_changed_log_subscriber.rb +1 -1
  46. data/lib/mongo/protocol/message.rb +36 -8
  47. data/lib/mongo/protocol/msg.rb +14 -0
  48. data/lib/mongo/protocol/serializers.rb +5 -2
  49. data/lib/mongo/server.rb +10 -3
  50. data/lib/mongo/server/connection.rb +4 -4
  51. data/lib/mongo/server/connection_base.rb +3 -1
  52. data/lib/mongo/server/description.rb +5 -0
  53. data/lib/mongo/server/monitor.rb +76 -44
  54. data/lib/mongo/server/monitor/connection.rb +55 -7
  55. data/lib/mongo/server/pending_connection.rb +14 -4
  56. data/lib/mongo/server/push_monitor.rb +173 -0
  57. data/{spec/runners/transactions/context.rb → lib/mongo/server/push_monitor/connection.rb} +9 -14
  58. data/lib/mongo/server_selector.rb +0 -1
  59. data/lib/mongo/server_selector/base.rb +579 -1
  60. data/lib/mongo/server_selector/nearest.rb +1 -6
  61. data/lib/mongo/server_selector/primary.rb +1 -6
  62. data/lib/mongo/server_selector/primary_preferred.rb +7 -10
  63. data/lib/mongo/server_selector/secondary.rb +1 -6
  64. data/lib/mongo/server_selector/secondary_preferred.rb +1 -7
  65. data/lib/mongo/session.rb +2 -0
  66. data/lib/mongo/socket.rb +20 -8
  67. data/lib/mongo/socket/ssl.rb +1 -1
  68. data/lib/mongo/socket/tcp.rb +1 -1
  69. data/lib/mongo/topology_version.rb +9 -0
  70. data/lib/mongo/utils.rb +62 -0
  71. data/lib/mongo/version.rb +1 -1
  72. data/spec/README.aws-auth.md +2 -2
  73. data/spec/integration/awaited_ismaster_spec.rb +28 -0
  74. data/spec/integration/change_stream_examples_spec.rb +6 -2
  75. data/spec/integration/check_clean_slate_spec.rb +16 -0
  76. data/spec/integration/client_construction_spec.rb +1 -0
  77. data/spec/integration/connect_single_rs_name_spec.rb +5 -2
  78. data/spec/integration/connection_spec.rb +7 -4
  79. data/spec/integration/crud_spec.rb +4 -4
  80. data/spec/integration/docs_examples_spec.rb +6 -0
  81. data/spec/integration/grid_fs_bucket_spec.rb +48 -0
  82. data/spec/integration/heartbeat_events_spec.rb +4 -23
  83. data/spec/integration/read_concern_spec.rb +1 -1
  84. data/spec/integration/retryable_errors_spec.rb +1 -1
  85. data/spec/integration/retryable_writes/shared/performs_legacy_retries.rb +2 -2
  86. data/spec/integration/retryable_writes/shared/performs_modern_retries.rb +3 -3
  87. data/spec/integration/retryable_writes/shared/performs_no_retries.rb +2 -2
  88. data/spec/integration/sdam_error_handling_spec.rb +37 -15
  89. data/spec/integration/sdam_events_spec.rb +77 -6
  90. data/spec/integration/sdam_prose_spec.rb +64 -0
  91. data/spec/integration/server_monitor_spec.rb +25 -1
  92. data/spec/integration/size_limit_spec.rb +7 -3
  93. data/spec/integration/size_limit_spec.rb~12e1e9c4f... RUBY-2242 Fix zlib compression (#2021) +98 -0
  94. data/spec/integration/ssl_uri_options_spec.rb +2 -2
  95. data/spec/integration/zlib_compression_spec.rb +25 -0
  96. data/spec/lite_spec_helper.rb +12 -5
  97. data/spec/mongo/auth/aws/request_spec.rb +76 -0
  98. data/spec/mongo/auth/scram_spec.rb +1 -1
  99. data/spec/mongo/client_construction_spec.rb +207 -0
  100. data/spec/mongo/client_spec.rb +38 -3
  101. data/spec/mongo/cluster/topology/replica_set_spec.rb +52 -9
  102. data/spec/mongo/cluster/topology/single_spec.rb +4 -2
  103. data/spec/mongo/cluster_spec.rb +34 -35
  104. data/spec/mongo/collection/view/change_stream_resume_spec.rb +6 -6
  105. data/spec/mongo/collection_spec.rb +500 -0
  106. data/spec/mongo/database_spec.rb +245 -8
  107. data/spec/mongo/distinguishing_semaphore_spec.rb +63 -0
  108. data/spec/mongo/error/operation_failure_spec.rb +40 -0
  109. data/spec/mongo/index/view_spec.rb +2 -2
  110. data/spec/mongo/monitoring/event/server_description_changed_spec.rb +1 -4
  111. data/spec/mongo/protocol/msg_spec.rb +10 -0
  112. data/spec/mongo/semaphore_spec.rb +51 -0
  113. data/spec/mongo/server/connection_auth_spec.rb +2 -2
  114. data/spec/mongo/server_selector/nearest_spec.rb +23 -23
  115. data/spec/mongo/server_selector/primary_preferred_spec.rb +26 -26
  116. data/spec/mongo/server_selector/primary_spec.rb +9 -9
  117. data/spec/mongo/server_selector/secondary_preferred_spec.rb +22 -22
  118. data/spec/mongo/server_selector/secondary_spec.rb +18 -18
  119. data/spec/mongo/server_selector_spec.rb +4 -4
  120. data/spec/mongo/session_spec.rb +35 -0
  121. data/spec/runners/change_streams/test.rb +2 -2
  122. data/spec/runners/cmap.rb +1 -1
  123. data/spec/runners/command_monitoring.rb +3 -34
  124. data/spec/runners/crud/context.rb +9 -5
  125. data/spec/runners/crud/operation.rb +59 -27
  126. data/spec/runners/crud/spec.rb +0 -8
  127. data/spec/runners/crud/test.rb +1 -1
  128. data/spec/runners/sdam.rb +2 -2
  129. data/spec/runners/server_selection.rb +242 -28
  130. data/spec/runners/transactions.rb +12 -12
  131. data/spec/runners/transactions/operation.rb +151 -25
  132. data/spec/runners/transactions/test.rb +60 -16
  133. data/spec/spec_tests/command_monitoring_spec.rb +22 -12
  134. data/spec/spec_tests/crud_spec.rb +1 -1
  135. data/spec/spec_tests/data/change_streams/change-streams-errors.yml +4 -8
  136. data/spec/spec_tests/data/change_streams/change-streams-resume-whitelist.yml +66 -0
  137. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/MaxStalenessTooSmall.yml +15 -0
  138. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/NoKnownServers.yml +4 -3
  139. data/spec/spec_tests/data/max_staleness/Unknown/SmallMaxStaleness.yml +1 -0
  140. data/spec/spec_tests/data/sdam_integration/cancel-server-check.yml +96 -0
  141. data/spec/spec_tests/data/sdam_integration/connectTimeoutMS.yml +88 -0
  142. data/spec/spec_tests/data/sdam_integration/find-network-error.yml +83 -0
  143. data/spec/spec_tests/data/sdam_integration/find-shutdown-error.yml +116 -0
  144. data/spec/spec_tests/data/sdam_integration/insert-network-error.yml +86 -0
  145. data/spec/spec_tests/data/sdam_integration/insert-shutdown-error.yml +115 -0
  146. data/spec/spec_tests/data/sdam_integration/isMaster-command-error.yml +168 -0
  147. data/spec/spec_tests/data/sdam_integration/isMaster-network-error.yml +162 -0
  148. data/spec/spec_tests/data/sdam_integration/isMaster-timeout.yml +229 -0
  149. data/spec/spec_tests/data/sdam_integration/rediscover-quickly-after-step-down.yml +87 -0
  150. data/spec/spec_tests/max_staleness_spec.rb +4 -142
  151. data/spec/spec_tests/retryable_reads_spec.rb +2 -2
  152. data/spec/spec_tests/sdam_integration_spec.rb +13 -0
  153. data/spec/spec_tests/sdam_monitoring_spec.rb +1 -2
  154. data/spec/spec_tests/server_selection_spec.rb +4 -116
  155. data/spec/stress/cleanup_spec.rb +17 -2
  156. data/spec/stress/connection_pool_stress_spec.rb +10 -8
  157. data/spec/support/child_process_helper.rb +78 -0
  158. data/spec/support/client_registry.rb +1 -0
  159. data/spec/support/cluster_config.rb +4 -0
  160. data/spec/support/event_subscriber.rb +123 -33
  161. data/spec/support/keyword_struct.rb +26 -0
  162. data/spec/support/shared/server_selector.rb +13 -1
  163. data/spec/support/spec_config.rb +38 -13
  164. data/spec/support/spec_organizer.rb +129 -0
  165. data/spec/support/spec_setup.rb +1 -1
  166. data/spec/support/utils.rb +46 -0
  167. metadata +992 -942
  168. metadata.gz.sig +0 -0
  169. data/lib/mongo/server_selector/selectable.rb +0 -560
  170. data/spec/runners/sdam_monitoring.rb +0 -89
metadata.gz.sig CHANGED
Binary file
@@ -1,560 +0,0 @@
1
- # Copyright (C) 2014-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 ServerSelector
17
-
18
- # Provides common behavior for filtering a list of servers by server mode or tag set.
19
- #
20
- # @since 2.0.0
21
- module Selectable
22
-
23
- # Initialize the server selector.
24
- #
25
- # @example Initialize the selector.
26
- # Mongo::ServerSelector::Secondary.new(:tag_sets => [{'dc' => 'nyc'}])
27
- #
28
- # @example Initialize the preference with no options.
29
- # Mongo::ServerSelector::Secondary.new
30
- #
31
- # @param [ Hash ] options The server preference options.
32
- #
33
- # @option options [ Integer ] :local_threshold The local threshold boundary for
34
- # nearest selection in seconds.
35
- # @option options [ Integer ] max_staleness The maximum replication lag,
36
- # in seconds, that a secondary can suffer and still be eligible for a read.
37
- # A value of -1 is treated identically to nil, which is to not
38
- # have a maximum staleness.
39
- # @option options [ Hash | nil ] hedge A Hash specifying whether to enable hedged
40
- # reads on the server. Hedged reads are not enabled by default. When
41
- # specifying this option, it must be in the format: { enabled: true },
42
- # where the value of the :enabled key is a boolean value.
43
- #
44
- # @raise [ Error::InvalidServerPreference ] If tag sets are specified
45
- # but not allowed.
46
- #
47
- # @since 2.0.0
48
- def initialize(options = nil)
49
- options = options ? options.dup : {}
50
- if options[:max_staleness] == -1
51
- options.delete(:max_staleness)
52
- end
53
- @options = options
54
- @tag_sets = options[:tag_sets] || []
55
- @max_staleness = options[:max_staleness]
56
- @hedge = options[:hedge]
57
-
58
- validate!
59
- end
60
-
61
- # @return [ Hash ] options The options.
62
- attr_reader :options
63
-
64
- # @return [ Array ] tag_sets The tag sets used to select servers.
65
- attr_reader :tag_sets
66
-
67
- # @return [ Integer ] max_staleness The maximum replication lag, in seconds, that a
68
- # secondary can suffer and still be eligible for a read.
69
- #
70
- # @since 2.4.0
71
- attr_reader :max_staleness
72
-
73
- # @return [ Hash | nil ] hedge The document specifying whether to enable
74
- # hedged reads.
75
- attr_reader :hedge
76
-
77
- # Check equality of two server selector.
78
- #
79
- # @example Check server selector equality.
80
- # preference == other
81
- #
82
- # @param [ Object ] other The other preference.
83
- #
84
- # @return [ true, false ] Whether the objects are equal.
85
- #
86
- # @since 2.0.0
87
- def ==(other)
88
- name == other.name && hedge == other.hedge &&
89
- max_staleness == other.max_staleness && tag_sets == other.tag_sets
90
- end
91
-
92
- # Inspect the server selector.
93
- #
94
- # @example Inspect the server selector.
95
- # selector.inspect
96
- #
97
- # @return [ String ] The inspection.
98
- #
99
- # @since 2.2.0
100
- def inspect
101
- "#<#{self.class.name}:0x#{object_id} tag_sets=#{tag_sets.inspect} max_staleness=#{max_staleness.inspect} hedge=#{hedge}>"
102
- end
103
-
104
- # Select a server from the specified cluster, taking into account
105
- # mongos pinning for the specified session.
106
- #
107
- # If the session is given and has a pinned server, this server is the
108
- # only server considered for selection. If the server is of type mongos,
109
- # it is returned immediately; otherwise monitoring checks on this
110
- # server are initiated to update its status, and if the server becomes
111
- # a mongos within the server selection timeout, it is returned.
112
- #
113
- # If no session is given or the session does not have a pinned server,
114
- # normal server selection process is performed among all servers in the
115
- # specified cluster matching the preference of this server selector
116
- # object. Monitoring checks are initiated on servers in the cluster until
117
- # a suitable server is found, up to the server selection timeout.
118
- #
119
- # If a suitable server is not found within the server selection timeout,
120
- # this method raises Error::NoServerAvailable.
121
- #
122
- # @param [ Mongo::Cluster ] cluster The cluster from which to select
123
- # an eligible server.
124
- # @param [ true, false ] ping Whether to ping the server before selection.
125
- # Deprecated and ignored.
126
- # @param [ Session | nil ] session Optional session to take into account
127
- # for mongos pinning. Added in version 2.10.0.
128
- #
129
- # @return [ Mongo::Server ] A server matching the server preference.
130
- #
131
- # @raise [ Error::NoServerAvailable ] No server was found matching the
132
- # specified preference / pinning requirement in the server selection
133
- # timeout.
134
- # @raise [ Error::LintError ] An unexpected condition was detected, and
135
- # lint mode is enabled.
136
- #
137
- # @since 2.0.0
138
- def select_server(cluster, ping = nil, session = nil)
139
- server_selection_timeout = cluster.options[:server_selection_timeout] || SERVER_SELECTION_TIMEOUT
140
-
141
- # Special handling for zero timeout: if we have to select a server,
142
- # and the timeout is zero, fail immediately (since server selection
143
- # will take some non-zero amount of time in any case).
144
- if server_selection_timeout == 0
145
- msg = "Failing server selection due to zero timeout. " +
146
- " Requested #{name} in cluster: #{cluster.summary}"
147
- raise Error::NoServerAvailable.new(self, cluster, msg)
148
- end
149
-
150
- deadline = Time.now + server_selection_timeout
151
-
152
- if session && session.pinned_server
153
- if Mongo::Lint.enabled?
154
- unless cluster.sharded?
155
- raise Error::LintError, "Session has a pinned server in a non-sharded topology: #{topology}"
156
- end
157
- end
158
-
159
- if !session.in_transaction?
160
- session.unpin
161
- end
162
-
163
- if server = session.pinned_server
164
- # Here we assume that a mongos stays in the topology indefinitely.
165
- # This will no longer be the case once SRV polling is implemented.
166
-
167
- unless server.mongos?
168
- while (time_remaining = deadline - Time.now) > 0
169
- wait_for_server_selection(cluster, time_remaining)
170
- end
171
-
172
- unless server.mongos?
173
- msg = "The session being used is pinned to the server which is not a mongos: #{server.summary} " +
174
- "(after #{server_selection_timeout} seconds)"
175
- raise Error::NoServerAvailable.new(self, cluster, msg)
176
- end
177
- end
178
-
179
- return server
180
- end
181
- end
182
-
183
- if cluster.replica_set?
184
- validate_max_staleness_value_early!
185
- end
186
- if cluster.addresses.empty?
187
- if Lint.enabled?
188
- unless cluster.servers.empty?
189
- raise Error::LintError, "Cluster has no addresses but has servers: #{cluster.servers.map(&:inspect).join(', ')}"
190
- end
191
- end
192
- msg = "Cluster has no addresses, and therefore will never have a server"
193
- raise Error::NoServerAvailable.new(self, cluster, msg)
194
- end
195
- =begin Add this check in version 3.0.0
196
- unless cluster.connected?
197
- msg = 'Cluster is disconnected'
198
- raise Error::NoServerAvailable.new(self, cluster, msg)
199
- end
200
- =end
201
- loop do
202
- servers = candidates(cluster)
203
- if Lint.enabled?
204
- servers.each do |server|
205
- # It is possible for a server to have a nil average RTT here
206
- # because the ARTT comes from description which may be updated
207
- # by a background thread while server selection is running.
208
- # Currently lint mode is not a public feature, if/when this
209
- # changes (https://jira.mongodb.org/browse/RUBY-1576) the
210
- # requirement for ARTT to be not nil would need to be removed.
211
- if server.average_round_trip_time.nil?
212
- raise Error::LintError, "Server #{server.address} has nil average rtt"
213
- end
214
- end
215
- end
216
- if servers && !servers.compact.empty?
217
- unless cluster.topology.compatible?
218
- raise Error::UnsupportedFeatures, cluster.topology.compatibility_error.to_s
219
- end
220
-
221
- # This list of servers may be ordered in a specific way
222
- # by the selector (e.g. for secondary preferred, the first
223
- # server may be a secondary and the second server may be primary)
224
- # and we should take the first server here respecting the order
225
- server = servers.first
226
-
227
- if session && session.starting_transaction? && cluster.sharded?
228
- session.pin(server)
229
- end
230
-
231
- return server
232
- end
233
-
234
- cluster.scan!(false)
235
-
236
- time_remaining = deadline - Time.now
237
- if time_remaining > 0
238
- wait_for_server_selection(cluster, time_remaining)
239
-
240
- # If we wait for server selection, perform another round of
241
- # attempting to locate a suitable server. Otherwise server selection
242
- # can raise NoServerAvailable message when the diagnostics
243
- # reports an available server of the requested type.
244
- else
245
- break
246
- end
247
- end
248
-
249
- msg = "No #{name} server is available in cluster: #{cluster.summary} " +
250
- "with timeout=#{server_selection_timeout}, " +
251
- "LT=#{local_threshold_with_cluster(cluster)}"
252
- msg += server_selection_diagnostic_message(cluster)
253
- raise Error::NoServerAvailable.new(self, cluster, msg)
254
- rescue Error::NoServerAvailable => e
255
- if session && session.in_transaction? && !session.committing_transaction?
256
- e.add_label('TransientTransactionError')
257
- end
258
- if session && session.committing_transaction?
259
- e.add_label('UnknownTransactionCommitResult')
260
- end
261
- raise e
262
- end
263
-
264
- # Get the timeout for server selection.
265
- #
266
- # @example Get the server selection timeout, in seconds.
267
- # selector.server_selection_timeout
268
- #
269
- # @return [ Float ] The timeout.
270
- #
271
- # @since 2.0.0
272
- #
273
- # @deprecated This setting is now taken from the cluster options when a server is selected.
274
- # Will be removed in 3.0.
275
- def server_selection_timeout
276
- @server_selection_timeout ||=
277
- (options[:server_selection_timeout] || ServerSelector::SERVER_SELECTION_TIMEOUT)
278
- end
279
-
280
- def local_threshold_with_cluster(cluster)
281
- options[:local_threshold] || cluster.options[:local_threshold] || LOCAL_THRESHOLD
282
- end
283
-
284
- # Get the local threshold boundary for nearest selection in seconds.
285
- #
286
- # @example Get the local threshold.
287
- # selector.local_threshold
288
- #
289
- # @return [ Float ] The local threshold.
290
- #
291
- # @since 2.0.0
292
- #
293
- # @deprecated This setting is now taken from the cluster options when a server is selected.
294
- # Will be removed in 3.0.
295
- def local_threshold
296
- @local_threshold ||= (options[:local_threshold] || ServerSelector::LOCAL_THRESHOLD)
297
- end
298
-
299
- # Get the potential candidates to select from the cluster.
300
- #
301
- # @example Get the server candidates.
302
- # selectable.candidates(cluster)
303
- #
304
- # @param [ Cluster ] cluster The cluster.
305
- #
306
- # @return [ Array<Server> ] The candidate servers.
307
- #
308
- # @since 2.4.0
309
- def candidates(cluster)
310
- if cluster.single?
311
- cluster.servers.each { |server| validate_max_staleness_support!(server) }
312
- elsif cluster.sharded?
313
- local_threshold = local_threshold_with_cluster(cluster)
314
- near_servers(cluster.servers, local_threshold).each do |server|
315
- validate_max_staleness_support!(server)
316
- end
317
- else
318
- validate_max_staleness_value!(cluster) unless cluster.unknown?
319
- select(cluster.servers)
320
- end
321
- end
322
-
323
- private
324
-
325
- # Select the primary from a list of provided candidates.
326
- #
327
- # @param [ Array ] candidates List of candidate servers to select the
328
- # primary from.
329
- #
330
- # @return [ Array ] The primary.
331
- #
332
- # @since 2.0.0
333
- def primary(candidates)
334
- candidates.select do |server|
335
- server.primary?
336
- end
337
- end
338
-
339
- # Select the secondaries from a list of provided candidates.
340
- #
341
- # @param [ Array ] candidates List of candidate servers to select the
342
- # secondaries from.
343
- #
344
- # @return [ Array ] The secondary servers.
345
- #
346
- # @since 2.0.0
347
- def secondaries(candidates)
348
- matching_servers = candidates.select(&:secondary?)
349
- matching_servers = filter_stale_servers(matching_servers, primary(candidates).first)
350
- matching_servers = match_tag_sets(matching_servers) unless tag_sets.empty?
351
- # Per server selection spec the server selected MUST be a random
352
- # one matching staleness and latency requirements.
353
- # Selectors always pass the output of #secondaries to #nearest
354
- # which shuffles the server list, fulfilling this requirement.
355
- matching_servers
356
- end
357
-
358
- # Select the near servers from a list of provided candidates, taking the
359
- # local threshold into account.
360
- #
361
- # @param [ Array ] candidates List of candidate servers to select the
362
- # near servers from.
363
- # @param [ Integer ] local_threshold Local threshold. This parameter
364
- # will be required in driver version 3.0.
365
- #
366
- # @return [ Array ] The near servers.
367
- #
368
- # @since 2.0.0
369
- def near_servers(candidates = [], local_threshold = nil)
370
- return candidates if candidates.empty?
371
-
372
- # Average RTT on any server may change at any time by the server
373
- # monitor's background thread. ARTT may also become nil if the
374
- # server is marked unknown. Take a snapshot of ARTTs for the duration
375
- # of this method.
376
-
377
- candidates = candidates.map do |server|
378
- {server: server, artt: server.average_round_trip_time}
379
- end.reject do |candidate|
380
- candidate[:artt].nil?
381
- end
382
-
383
- return candidates if candidates.empty?
384
-
385
- nearest_candidate = candidates.min_by do |candidate|
386
- candidate[:artt]
387
- end
388
-
389
- # Default for legacy signarure
390
- local_threshold ||= self.local_threshold
391
-
392
- threshold = nearest_candidate[:artt] + local_threshold
393
-
394
- candidates.select do |candidate|
395
- candidate[:artt] <= threshold
396
- end.map do |candidate|
397
- candidate[:server]
398
- end.shuffle!
399
- end
400
-
401
- # Select the servers matching the defined tag sets.
402
- #
403
- # @param [ Array ] candidates List of candidate servers from which those
404
- # matching the defined tag sets should be selected.
405
- #
406
- # @return [ Array ] The servers matching the defined tag sets.
407
- #
408
- # @since 2.0.0
409
- def match_tag_sets(candidates)
410
- matches = []
411
- tag_sets.find do |tag_set|
412
- matches = candidates.select { |server| server.matches_tag_set?(tag_set) }
413
- !matches.empty?
414
- end
415
- matches || []
416
- end
417
-
418
- def filter_stale_servers(candidates, primary = nil)
419
- return candidates unless @max_staleness
420
-
421
- # last_scan is filled out by the Monitor, and can be nil if a server
422
- # had its description manually set rather than being normally updated
423
- # via the SDAM flow. We don't handle the possibility of a nil
424
- # last_scan here.
425
- if primary
426
- candidates.select do |server|
427
- validate_max_staleness_support!(server)
428
- staleness = (server.last_scan - server.last_write_date) -
429
- (primary.last_scan - primary.last_write_date) +
430
- server.cluster.heartbeat_interval
431
- staleness <= @max_staleness
432
- end
433
- else
434
- max_write_date = candidates.collect(&:last_write_date).max
435
- candidates.select do |server|
436
- validate_max_staleness_support!(server)
437
- staleness = max_write_date - server.last_write_date + server.cluster.heartbeat_interval
438
- staleness <= @max_staleness
439
- end
440
- end
441
- end
442
-
443
- def validate!
444
- if !@tag_sets.all? { |set| set.empty? } && !tags_allowed?
445
- raise Error::InvalidServerPreference.new(Error::InvalidServerPreference::NO_TAG_SUPPORT)
446
- elsif @max_staleness && !max_staleness_allowed?
447
- raise Error::InvalidServerPreference.new(Error::InvalidServerPreference::NO_MAX_STALENESS_SUPPORT)
448
- end
449
-
450
- if @hedge
451
- unless hedge_allowed?
452
- raise Error::InvalidServerPreference.new(Error::InvalidServerPreference::NO_HEDGE_SUPPORT)
453
- end
454
-
455
- unless @hedge.is_a?(Hash) && @hedge.key?(:enabled) &&
456
- [true, false].include?(@hedge[:enabled])
457
- raise Error::InvalidServerPreference.new(
458
- "`hedge` value (#{hedge}) is invalid - hedge must be a Hash in the " \
459
- "format { enabled: true }"
460
- )
461
- end
462
- end
463
- end
464
-
465
- def validate_max_staleness_support!(server)
466
- if @max_staleness && !server.features.max_staleness_enabled?
467
- raise Error::InvalidServerPreference.new(Error::InvalidServerPreference::NO_MAX_STALENESS_WITH_LEGACY_SERVER)
468
- end
469
- end
470
-
471
- def validate_max_staleness_value_early!
472
- if @max_staleness
473
- unless @max_staleness >= SMALLEST_MAX_STALENESS_SECONDS
474
- msg = "`max_staleness` value (#{@max_staleness}) is too small - it must be at least " +
475
- "`Mongo::ServerSelector::SMALLEST_MAX_STALENESS_SECONDS` (#{ServerSelector::SMALLEST_MAX_STALENESS_SECONDS})"
476
- raise Error::InvalidServerPreference.new(msg)
477
- end
478
- end
479
- end
480
-
481
- def validate_max_staleness_value!(cluster)
482
- if @max_staleness
483
- heartbeat_interval = cluster.heartbeat_interval
484
- unless @max_staleness >= [
485
- SMALLEST_MAX_STALENESS_SECONDS,
486
- min_cluster_staleness = heartbeat_interval + Cluster::IDLE_WRITE_PERIOD_SECONDS,
487
- ].max
488
- msg = "`max_staleness` value (#{@max_staleness}) is too small - it must be at least " +
489
- "`Mongo::ServerSelector::SMALLEST_MAX_STALENESS_SECONDS` (#{ServerSelector::SMALLEST_MAX_STALENESS_SECONDS}) and (the cluster's heartbeat_frequency " +
490
- "setting + `Mongo::Cluster::IDLE_WRITE_PERIOD_SECONDS`) (#{min_cluster_staleness})"
491
- raise Error::InvalidServerPreference.new(msg)
492
- end
493
- end
494
- end
495
-
496
- # Waits for server state changes in the specified cluster.
497
- #
498
- # If the cluster has a server selection semaphore, waits on that
499
- # semaphore up to the specified remaining time. Any change in server
500
- # state resulting from SDAM will immediately wake up this method and
501
- # cause it to return.
502
- #
503
- # If the cluster des not have a server selection semaphore, waits
504
- # the smaller of 0.25 seconds and the specified remaining time.
505
- # This functionality is provided for backwards compatibilty only for
506
- # applications directly invoking the server selection process.
507
- # If lint mode is enabled and the cluster does not have a server
508
- # selection semaphore, Error::LintError will be raised.
509
- #
510
- # @param [ Cluster ] cluster The cluster to wait for.
511
- # @param [ Numeric ] time_remaining Maximum time to wait, in seconds.
512
- def wait_for_server_selection(cluster, time_remaining)
513
- if cluster.server_selection_semaphore
514
- # Since the semaphore may have been signaled between us checking
515
- # the servers list earlier and the wait call below, we should not
516
- # wait for the full remaining time - wait for up to 1 second, then
517
- # recheck the state.
518
- cluster.server_selection_semaphore.wait([time_remaining, 1].min)
519
- else
520
- if Lint.enabled?
521
- raise Error::LintError, 'Waiting for server selection without having a server selection semaphore'
522
- end
523
- sleep [time_remaining, 0.25].min
524
- end
525
- end
526
-
527
- # Creates a diagnostic message when server selection fails.
528
- #
529
- # The diagnostic message includes the following information, as applicable:
530
- #
531
- # - Servers having dead monitor threads
532
- # - Cluster is disconnected
533
- #
534
- # If none of the conditions for diagnostic messages apply, an empty string
535
- # is returned.
536
- #
537
- # @param [ Cluster ] cluster The cluster on which server selection was
538
- # performed.
539
- #
540
- # @return [ String ] The diagnostic message.
541
- def server_selection_diagnostic_message(cluster)
542
- msg = ''
543
- dead_monitors = []
544
- cluster.servers_list.each do |server|
545
- thread = server.monitor.instance_variable_get('@thread')
546
- if thread.nil? || !thread.alive?
547
- dead_monitors << server
548
- end
549
- end
550
- if dead_monitors.any?
551
- msg += ". The following servers have dead monitor threads: #{dead_monitors.map(&:summary).join(', ')}"
552
- end
553
- unless cluster.connected?
554
- msg += ". The cluster is disconnected (client may have been closed)"
555
- end
556
- msg
557
- end
558
- end
559
- end
560
- end