mongo 2.10.5 → 2.11.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.
Files changed (191) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/CONTRIBUTING.md +1 -1
  5. data/lib/mongo.rb +2 -0
  6. data/lib/mongo/address.rb +4 -0
  7. data/lib/mongo/address/validator.rb +99 -0
  8. data/lib/mongo/auth.rb +7 -2
  9. data/lib/mongo/auth/user.rb +1 -7
  10. data/lib/mongo/background_thread.rb +135 -0
  11. data/lib/mongo/bulk_write/transformable.rb +3 -3
  12. data/lib/mongo/client.rb +74 -16
  13. data/lib/mongo/cluster.rb +193 -41
  14. data/lib/mongo/cluster/periodic_executor.rb +31 -43
  15. data/lib/mongo/cluster/sdam_flow.rb +26 -3
  16. data/lib/mongo/cluster/srv_monitor.rb +127 -0
  17. data/lib/mongo/collection/view/readable.rb +3 -5
  18. data/lib/mongo/collection/view/writable.rb +3 -3
  19. data/lib/mongo/cursor/builder/get_more_command.rb +1 -4
  20. data/lib/mongo/cursor/builder/kill_cursors_command.rb +5 -23
  21. data/lib/mongo/cursor/builder/op_get_more.rb +2 -2
  22. data/lib/mongo/cursor/builder/op_kill_cursors.rb +5 -24
  23. data/lib/mongo/error.rb +1 -0
  24. data/lib/mongo/error/auth_error.rb +1 -1
  25. data/lib/mongo/error/connection_check_out_timeout.rb +7 -8
  26. data/lib/mongo/error/invalid_address.rb +24 -0
  27. data/lib/mongo/error/notable.rb +2 -2
  28. data/lib/mongo/error/operation_failure.rb +3 -3
  29. data/lib/mongo/error/pool_closed_error.rb +11 -4
  30. data/lib/mongo/event.rb +1 -1
  31. data/lib/mongo/grid/file.rb +0 -5
  32. data/lib/mongo/grid/file/chunk.rb +0 -2
  33. data/lib/mongo/grid/fs_bucket.rb +13 -15
  34. data/lib/mongo/grid/stream/write.rb +3 -9
  35. data/lib/mongo/loggable.rb +5 -1
  36. data/lib/mongo/monitoring.rb +1 -0
  37. data/lib/mongo/monitoring/event/cmap/connection_check_out_failed.rb +7 -0
  38. data/lib/mongo/monitoring/event/cmap/connection_checked_in.rb +11 -3
  39. data/lib/mongo/monitoring/event/cmap/connection_checked_out.rb +11 -3
  40. data/lib/mongo/monitoring/event/cmap/pool_closed.rb +11 -3
  41. data/lib/mongo/monitoring/event/cmap/pool_created.rb +12 -3
  42. data/lib/mongo/monitoring/unified_sdam_log_subscriber.rb +62 -0
  43. data/lib/mongo/operation/shared/executable.rb +5 -10
  44. data/lib/mongo/operation/shared/sessions_supported.rb +1 -5
  45. data/lib/mongo/protocol/get_more.rb +1 -2
  46. data/lib/mongo/protocol/kill_cursors.rb +13 -6
  47. data/lib/mongo/protocol/serializers.rb +4 -20
  48. data/lib/mongo/retryable.rb +9 -34
  49. data/lib/mongo/semaphore.rb +1 -1
  50. data/lib/mongo/server.rb +113 -42
  51. data/lib/mongo/server/connection.rb +12 -5
  52. data/lib/mongo/server/connection_pool.rb +250 -40
  53. data/lib/mongo/server/connection_pool/populator.rb +58 -0
  54. data/lib/mongo/server/description.rb +9 -2
  55. data/lib/mongo/server/monitor.rb +68 -93
  56. data/lib/mongo/server/monitor/connection.rb +2 -0
  57. data/lib/mongo/server_selector/selectable.rb +13 -5
  58. data/lib/mongo/session.rb +0 -13
  59. data/lib/mongo/srv.rb +17 -0
  60. data/lib/mongo/srv/monitor.rb +96 -0
  61. data/lib/mongo/srv/resolver.rb +130 -0
  62. data/lib/mongo/srv/result.rb +126 -0
  63. data/lib/mongo/srv/warning_result.rb +35 -0
  64. data/lib/mongo/uri.rb +45 -55
  65. data/lib/mongo/uri/srv_protocol.rb +89 -42
  66. data/lib/mongo/version.rb +1 -1
  67. data/mongo.gemspec +3 -4
  68. data/spec/README.md +6 -1
  69. data/spec/enterprise_auth/kerberos_spec.rb +7 -6
  70. data/spec/integration/change_stream_examples_spec.rb +0 -4
  71. data/spec/integration/client_construction_spec.rb +14 -2
  72. data/spec/integration/connect_single_rs_name_spec.rb +2 -2
  73. data/spec/integration/connection_pool_populator_spec.rb +296 -0
  74. data/spec/integration/connection_spec.rb +31 -22
  75. data/spec/integration/cursor_reaping_spec.rb +1 -2
  76. data/spec/integration/docs_examples_spec.rb +0 -4
  77. data/spec/integration/heartbeat_events_spec.rb +17 -15
  78. data/spec/integration/reconnect_spec.rb +144 -1
  79. data/spec/integration/retryable_writes_errors_spec.rb +0 -4
  80. data/spec/integration/retryable_writes_spec.rb +36 -36
  81. data/spec/integration/sdam_error_handling_spec.rb +31 -25
  82. data/spec/integration/sdam_events_spec.rb +2 -6
  83. data/spec/integration/server_monitor_spec.rb +28 -0
  84. data/spec/integration/server_selector_spec.rb +7 -5
  85. data/spec/integration/srv_monitoring_spec.rb +360 -0
  86. data/spec/integration/step_down_spec.rb +4 -6
  87. data/spec/lite_spec_helper.rb +22 -0
  88. data/spec/mongo/address/validator_spec.rb +51 -0
  89. data/spec/mongo/auth/cr_spec.rb +1 -29
  90. data/spec/mongo/auth/ldap_spec.rb +1 -29
  91. data/spec/mongo/auth/scram/conversation_spec.rb +0 -2
  92. data/spec/mongo/auth/scram/negotiation_spec.rb +1 -1
  93. data/spec/mongo/auth/scram_spec.rb +1 -29
  94. data/spec/mongo/auth/user/view_spec.rb +1 -36
  95. data/spec/mongo/auth/user_spec.rb +0 -12
  96. data/spec/mongo/auth/x509_spec.rb +1 -29
  97. data/spec/mongo/bulk_write_spec.rb +2 -2
  98. data/spec/mongo/client_construction_spec.rb +56 -15
  99. data/spec/mongo/client_spec.rb +31 -27
  100. data/spec/mongo/cluster/periodic_executor_spec.rb +16 -0
  101. data/spec/mongo/cluster/srv_monitor_spec.rb +214 -0
  102. data/spec/mongo/cluster/topology/replica_set_spec.rb +16 -11
  103. data/spec/mongo/cluster/topology/sharded_spec.rb +12 -9
  104. data/spec/mongo/cluster/topology/single_spec.rb +20 -11
  105. data/spec/mongo/cluster_spec.rb +45 -29
  106. data/spec/mongo/collection/view/map_reduce_spec.rb +14 -9
  107. data/spec/mongo/collection/view/readable_spec.rb +0 -16
  108. data/spec/mongo/collection_spec.rb +0 -44
  109. data/spec/mongo/cursor/builder/get_more_command_spec.rb +2 -4
  110. data/spec/mongo/cursor/builder/op_get_more_spec.rb +2 -4
  111. data/spec/mongo/cursor_spec.rb +27 -7
  112. data/spec/mongo/monitoring/event/cmap/connection_checked_in_spec.rb +10 -3
  113. data/spec/mongo/monitoring/event/cmap/connection_checked_out_spec.rb +10 -3
  114. data/spec/mongo/monitoring/event/cmap/pool_closed_spec.rb +10 -3
  115. data/spec/mongo/monitoring/event/cmap/pool_created_spec.rb +10 -3
  116. data/spec/mongo/operation/delete/op_msg_spec.rb +17 -8
  117. data/spec/mongo/operation/insert/op_msg_spec.rb +50 -35
  118. data/spec/mongo/operation/update/op_msg_spec.rb +14 -7
  119. data/spec/mongo/retryable_spec.rb +52 -31
  120. data/spec/mongo/server/app_metadata_spec.rb +0 -8
  121. data/spec/mongo/server/connection_auth_spec.rb +5 -2
  122. data/spec/mongo/server/connection_pool/populator_spec.rb +101 -0
  123. data/spec/mongo/server/connection_pool_spec.rb +256 -107
  124. data/spec/mongo/server/connection_spec.rb +22 -33
  125. data/spec/mongo/server/description_spec.rb +42 -4
  126. data/spec/mongo/server/monitor/connection_spec.rb +22 -11
  127. data/spec/mongo/server/monitor_spec.rb +66 -107
  128. data/spec/mongo/server_spec.rb +82 -60
  129. data/spec/mongo/session/session_pool_spec.rb +1 -5
  130. data/spec/mongo/session_spec.rb +0 -4
  131. data/spec/mongo/socket/ssl_spec.rb +2 -2
  132. data/spec/mongo/srv/monitor_spec.rb +211 -0
  133. data/spec/mongo/srv/result_spec.rb +54 -0
  134. data/spec/mongo/uri/srv_protocol_spec.rb +30 -15
  135. data/spec/mongo/uri_spec.rb +125 -4
  136. data/spec/spec_helper.rb +6 -0
  137. data/spec/spec_tests/auth_spec.rb +39 -0
  138. data/spec/spec_tests/cmap_spec.rb +55 -8
  139. data/spec/spec_tests/connection_string_spec.rb +6 -31
  140. data/spec/spec_tests/data/auth/connection-string.yml +297 -0
  141. data/spec/spec_tests/data/cmap/pool-checkout-error-closed.yml +4 -1
  142. data/spec/spec_tests/data/cmap/pool-create-with-options.yml +1 -0
  143. data/spec/spec_tests/data/command_monitoring/insertMany.yml +1 -1
  144. data/spec/spec_tests/data/connection_string/invalid-uris.yml +20 -0
  145. data/spec/spec_tests/data/connection_string/valid-auth.yml +16 -0
  146. data/spec/spec_tests/data/connection_string/valid-warnings.yml +26 -30
  147. data/spec/spec_tests/data/transactions/abort.yml +3 -3
  148. data/spec/spec_tests/data/transactions/error-labels.yml +3 -3
  149. data/spec/spec_tests/data/transactions_api/callback-retry.yml +3 -3
  150. data/spec/spec_tests/data/uri_options/auth-options.yml +1 -1
  151. data/spec/spec_tests/max_staleness_spec.rb +7 -2
  152. data/spec/spec_tests/retryable_reads_spec.rb +0 -31
  153. data/spec/spec_tests/sdam_monitoring_spec.rb +12 -12
  154. data/spec/spec_tests/sdam_spec.rb +4 -7
  155. data/spec/spec_tests/server_selection_spec.rb +6 -2
  156. data/spec/spec_tests/transactions_spec.rb +0 -2
  157. data/spec/spec_tests/uri_options_spec.rb +4 -2
  158. data/spec/stress/connection_pool_stress_spec.rb +203 -0
  159. data/spec/stress/connection_pool_timing_spec.rb +181 -0
  160. data/spec/support/auth.rb +113 -0
  161. data/spec/support/background_thread_registry.rb +63 -0
  162. data/spec/support/client_registry.rb +11 -2
  163. data/spec/support/cluster_config.rb +65 -46
  164. data/spec/support/cluster_tools.rb +2 -2
  165. data/spec/support/cmap.rb +13 -14
  166. data/spec/support/cmap/verifier.rb +4 -5
  167. data/spec/support/command_monitoring.rb +0 -5
  168. data/spec/support/common_shortcuts.rb +101 -1
  169. data/spec/support/constraints.rb +25 -0
  170. data/spec/support/dns.rb +13 -0
  171. data/spec/support/event_subscriber.rb +0 -7
  172. data/spec/support/json_ext_formatter.rb +5 -1
  173. data/spec/support/lite_constraints.rb +22 -6
  174. data/spec/support/local_resource_registry.rb +34 -0
  175. data/spec/support/sdam_monitoring.rb +115 -0
  176. data/spec/support/spec_config.rb +20 -6
  177. data/spec/support/spec_setup.rb +2 -2
  178. data/spec/support/transactions.rb +1 -1
  179. data/spec/support/transactions/test.rb +1 -1
  180. data/spec/support/utils.rb +1 -16
  181. metadata +685 -659
  182. metadata.gz.sig +0 -0
  183. data/lib/mongo/event/description_changed.rb +0 -52
  184. data/spec/integration/bson_symbol_spec.rb +0 -34
  185. data/spec/integration/crud_spec.rb +0 -45
  186. data/spec/integration/get_more_spec.rb +0 -32
  187. data/spec/integration/grid_fs_bucket_spec.rb +0 -48
  188. data/spec/integration/retryable_errors_spec.rb +0 -265
  189. data/spec/integration/size_limit_spec.rb~12e1e9c4f... RUBY-2242 Fix zlib compression (#2021) +0 -98
  190. data/spec/mongo/cursor/builder/op_kill_cursors_spec.rb +0 -56
  191. data/spec/runners/sdam/verifier.rb +0 -88
@@ -33,6 +33,8 @@ module Mongo
33
33
  # @since 2.5.0
34
34
  class SRVProtocol < URI
35
35
 
36
+ attr_reader :srv_records
37
+
36
38
  # Gets the options hash that needs to be passed to a Mongo::Client on instantiation, so we
37
39
  # don't have to merge the txt record options, credentials, and database in at that point -
38
40
  # we only have a single point here.
@@ -49,49 +51,93 @@ module Mongo
49
51
  @user ? opts.merge(credentials) : opts
50
52
  end
51
53
 
52
- private
54
+ # @return [ Srv::Result ] SRV lookup result.
55
+ #
56
+ # @api private
57
+ attr_reader :srv_result
58
+
59
+ # The hostname that is specified in the URI and used to look up
60
+ # SRV records.
61
+ #
62
+ # This attribute needs to be defined because SRVProtocol changes
63
+ # #servers to be the result of the lookup rather than the hostname
64
+ # specified in the URI.
65
+ #
66
+ # @return [ String ] The hostname used in SRV lookup.
67
+ #
68
+ # @api private
69
+ attr_reader :query_hostname
53
70
 
54
- RECORD_PREFIX = '_mongodb._tcp.'.freeze
71
+ private
55
72
 
73
+ # @return [ String ] DOT_PARTITION The '.' character used to delineate the parts of a
74
+ # hostname.
75
+ #
76
+ # @deprecated
56
77
  DOT_PARTITION = '.'.freeze
57
78
 
79
+ # @return [ Array<String> ] VALID_TXT_OPTIONS The valid options for a TXT record to specify.
58
80
  VALID_TXT_OPTIONS = ['replicaset', 'authsource'].freeze
59
81
 
82
+ # @return [ String ] INVALID_HOST Error message format string indicating that the hostname in
83
+ # in the URI does not fit the expected form.
60
84
  INVALID_HOST = "One and only one host is required in a connection string with the " +
61
85
  "'#{MONGODB_SRV_SCHEME}' protocol.".freeze
62
86
 
87
+ # @return [ String ] INVALID_PORT Error message format string indicating that a port was
88
+ # included with an SRV hostname.
63
89
  INVALID_PORT = "It is not allowed to specify a port in a connection string with the " +
64
90
  "'#{MONGODB_SRV_SCHEME}' protocol.".freeze
65
91
 
92
+ # @return [ String ] INVALID_DOMAIN Error message format string indicating that the domain name
93
+ # of the hostname does not fit the expected form.
66
94
  # @deprecated
67
95
  INVALID_DOMAIN = "The domain name must consist of at least two parts: the domain name, " +
68
96
  "and a TLD.".freeze
69
97
 
98
+ # @return [ String ] NO_SRV_RECORDS Error message format string indicating that no SRV records
99
+ # were found.
70
100
  NO_SRV_RECORDS = "The DNS query returned no SRV records at hostname (%s)".freeze
71
101
 
72
- MORE_THAN_ONE_TXT_RECORD_FOUND = "Only one TXT record is allowed. Querying hostname (%s) " +
73
- "returned more than one result.".freeze
74
-
102
+ # @return [ String ] INVALID_TXT_RECORD_OPTION Error message format string indicating that an
103
+ # unexpected TXT record option was found.
75
104
  INVALID_TXT_RECORD_OPTION = "TXT records can only specify the options " +
76
105
  "[#{VALID_TXT_OPTIONS.join(', ')}].".freeze
77
106
 
78
- MISMATCHED_DOMAINNAME = "Parent domain name in SRV record result (%s) does not match " +
79
- "that of the hostname (%s)".freeze
80
-
107
+ # @return [ String ] FORMAT The expected SRV URI format.
81
108
  FORMAT = 'mongodb+srv://[username:password@]host[/[database][?options]]'.freeze
82
109
 
110
+ # Gets the MongoDB SRV URI scheme.
111
+ #
112
+ # @return [ String ] The MongoDB SRV URI scheme.
83
113
  def scheme
84
114
  MONGODB_SRV_SCHEME
85
115
  end
86
116
 
117
+ # Raises an InvalidURI error.
118
+ #
119
+ # @param [ String ] details A detailed error message.
120
+ #
121
+ # @raise [ Mongo::Error::InvalidURI ]
87
122
  def raise_invalid_error!(details)
88
123
  raise Error::InvalidURI.new(@string, details, FORMAT)
89
124
  end
90
125
 
126
+ # Gets the SRV resolver.
127
+ #
128
+ # @return [ Mongo::Srv::Resolver ]
91
129
  def resolver
92
- @resolver ||= Resolv::DNS.new
130
+ @resolver ||= Srv::Resolver.new(
131
+ raise_on_invalid: true,
132
+ resolv_options: options[:resolv_options],
133
+ )
93
134
  end
94
135
 
136
+ # Parses the credentials from the URI and performs DNS queries to obtain
137
+ # the hosts and TXT options.
138
+ #
139
+ # @param [ String ] remaining The portion of the URI pertaining to the
140
+ # authentication credentials and the hosts.
95
141
  def parse!(remaining)
96
142
  super
97
143
 
@@ -99,14 +145,21 @@ module Mongo
99
145
  raise_invalid_error!(INVALID_HOST)
100
146
  end
101
147
  hostname = @servers.first
102
- validate_hostname(hostname)
148
+ validate_srv_hostname(hostname)
149
+ @query_hostname = hostname
103
150
 
104
- records = get_records(hostname)
105
- @txt_options = get_txt_opts(hostname) || {}
151
+ @srv_result = resolver.get_records(hostname)
152
+ if srv_result.empty?
153
+ raise Error::NoSRVRecords.new(NO_SRV_RECORDS % hostname)
154
+ end
155
+ @txt_options = get_txt_options(hostname) || {}
156
+ records = srv_result.address_strs
106
157
  records.each do |record|
107
- validate_host!(record)
158
+ validate_address_str!(record)
108
159
  end
109
160
  @servers = records
161
+ rescue Error::InvalidAddress => e
162
+ raise_invalid_error!(e.message)
110
163
  end
111
164
 
112
165
  # Validates the hostname used in an SRV URI.
@@ -118,7 +171,7 @@ module Mongo
118
171
  # components (foo.bar.tld).
119
172
  #
120
173
  # Raises Error::InvalidURI if validation fails.
121
- def validate_hostname(hostname)
174
+ def validate_srv_hostname(hostname)
122
175
  raise_invalid_error!(INVALID_PORT) if hostname.include?(HOST_PORT_DELIM)
123
176
 
124
177
  if hostname.start_with?('.')
@@ -136,39 +189,33 @@ module Mongo
136
189
  end
137
190
  end
138
191
 
139
- def get_records(hostname)
140
- query_name = RECORD_PREFIX + hostname
141
- records = resolver.getresources(query_name, Resolv::DNS::Resource::IN::SRV).collect do |record|
142
- record_host = record.target.to_s
143
- port = record.port
144
- validate_record!(record_host, hostname)
145
- "#{record_host}#{HOST_PORT_DELIM}#{port}"
146
- end
147
- raise Error::NoSRVRecords.new(NO_SRV_RECORDS % hostname) if records.empty?
148
- records
149
- end
150
-
151
- def validate_record!(record_host, hostname)
152
- domainname = hostname.split(DOT_PARTITION)[1..-1]
153
- host_parts = record_host.split(DOT_PARTITION)
154
- unless (host_parts.size > domainname.size) && (domainname == host_parts[-domainname.length..-1])
155
- raise Error::MismatchedDomain.new(MISMATCHED_DOMAINNAME % [record_host, domainname])
156
- end
157
- end
158
-
159
- def get_txt_opts(host)
160
- records = resolver.getresources(host, Resolv::DNS::Resource::IN::TXT)
161
- unless records.empty?
162
- if records.size > 1
163
- raise Error::InvalidTXTRecord.new(MORE_THAN_ONE_TXT_RECORD_FOUND % host)
164
- end
165
- options_string = records[0].strings.join
192
+ # Obtains the TXT options of a host.
193
+ #
194
+ # @param [ String ] hostname The hostname whose records should be obtained.
195
+ #
196
+ # @return [ Hash ] The TXT record options (an empyt hash if no TXT
197
+ # records are found).
198
+ #
199
+ # @raise [ Mongo::Error::InvalidTXTRecord ] If more than one TXT record is found.
200
+ def get_txt_options(hostname)
201
+ options_string = resolver.get_txt_options_string(hostname)
202
+ if options_string
166
203
  parse_txt_options!(options_string)
204
+ else
205
+ {}
167
206
  end
168
207
  end
169
208
 
209
+ # Parses the TXT record options into a hash and adds the options to set of all URI options
210
+ # parsed.
211
+ #
212
+ # @param [ String ] string The concatenated TXT options.
213
+ #
214
+ # @return [ Hash ] The parsed TXT options.
215
+ #
216
+ # @raise [ Mongo::Error::InvalidTXTRecord ] If the TXT record does not fit the expected form
217
+ # or the option specified is not a valid TXT option.
170
218
  def parse_txt_options!(string)
171
- return {} unless string
172
219
  string.split(INDIV_URI_OPTS_DELIM).reduce({}) do |txt_options, opt|
173
220
  raise Error::InvalidTXTRecord.new(INVALID_OPTS_VALUE_DELIM) unless opt.index(URI_OPTS_VALUE_DELIM)
174
221
  key, value = opt.split(URI_OPTS_VALUE_DELIM)
data/lib/mongo/version.rb CHANGED
@@ -17,5 +17,5 @@ module Mongo
17
17
  # The current version of the driver.
18
18
  #
19
19
  # @since 2.0.0
20
- VERSION = '2.10.5'.freeze
20
+ VERSION = '2.11.0.rc0'.freeze
21
21
  end
data/mongo.gemspec CHANGED
@@ -4,10 +4,7 @@ require 'mongo/version'
4
4
 
5
5
  Gem::Specification.new do |s|
6
6
  s.name = 'mongo'
7
- # The dup call makes `bundle install` work on ruby 1.9.3.
8
- # Without it rubygems tries to modify version which fails because
9
- # Mongo::VERSION is frozen.
10
- s.version = Mongo::VERSION.dup
7
+ s.version = Mongo::VERSION
11
8
  s.platform = Gem::Platform::RUBY
12
9
 
13
10
  s.authors = ['Tyler Brock', 'Emily Stolfo', 'Durran Jordan']
@@ -32,5 +29,7 @@ Gem::Specification.new do |s|
32
29
  s.require_paths = ['lib']
33
30
  s.bindir = 'bin'
34
31
 
32
+ s.required_ruby_version = ">= 2.3"
33
+
35
34
  s.add_dependency 'bson', '>=4.4.2', '<5.0.0'
36
35
  end
data/spec/README.md CHANGED
@@ -78,12 +78,17 @@ configuration is needed:
78
78
 
79
79
  A sharded cluster can be configured with mlaunch:
80
80
 
81
- mlaunch init --replicaset --name ruby-driver-rs --sharded 1 \
81
+ mlaunch init --replicaset --name ruby-driver-rs --sharded 1 --mongos 2 \
82
82
  --dir /tmp/mdb-sc --setParameter enableTestCommands=1
83
83
 
84
84
  As with the replica set, the test suite will automatically detect sharded
85
85
  cluster topology.
86
86
 
87
+ Note that some tests require a sharded cluster with exactly one shard and
88
+ other tests require a sharded cluster with more than one shard. Tests requiring
89
+ a single shard can be run against a deployment with multiple shards by
90
+ specifying only one mongos address in MONGODB_URI.
91
+
87
92
  ## TLS With Verification
88
93
 
89
94
  The test suite includes a set of TLS certificates for configuring a server
@@ -1,6 +1,13 @@
1
1
  require 'mongo'
2
+ require 'support/lite_constraints'
3
+
4
+ RSpec.configure do |config|
5
+ config.extend(LiteConstraints)
6
+ end
2
7
 
3
8
  describe 'kerberos authentication' do
9
+ require_mongo_kerberos
10
+
4
11
  let(:user) do
5
12
  "#{ENV['SASL_USER']}%40#{ENV['SASL_HOST'].upcase}"
6
13
  end
@@ -25,13 +32,7 @@ describe 'kerberos authentication' do
25
32
  Mongo::Client.new(uri)
26
33
  end
27
34
 
28
- before do
29
- skip 'ENTERPRISE_AUTH_TESTS env var not specified' unless ENV['ENTERPRISE_AUTH_TESTS']
30
- end
31
-
32
35
  let(:doc) do
33
- require 'mongo_kerberos'
34
-
35
36
  client.database[:test].find.first
36
37
  end
37
38
 
@@ -22,10 +22,6 @@ describe 'change streams examples in Ruby' do
22
22
  inventory.drop
23
23
  end
24
24
 
25
- after do
26
- client.close(true)
27
- end
28
-
29
25
  context 'example 1 - basic watching'do
30
26
 
31
27
  it 'returns a change after an insertion' do
@@ -31,6 +31,18 @@ describe 'Client construction' do
31
31
  expect(client.cluster.topology).to be_a(Mongo::Cluster::Topology::Single)
32
32
  expect(client.options[:connect]).to eq :direct
33
33
  end
34
+
35
+ it 'creates connection pool and keeps it populated' do
36
+ client = ClientRegistry.instance.new_local_client([SpecConfig.instance.addresses.first],
37
+ base_options.merge(min_pool_size: 1))
38
+ # allow connection pool to populate
39
+ sleep 0.1
40
+
41
+ server = client.cluster.next_primary
42
+ expect(server.pool.size).to eq(1)
43
+ client['client_construction'].insert_one(test: 1)
44
+ expect(server.pool.size).to eq(1)
45
+ end
34
46
  end
35
47
 
36
48
  context 'in replica set topology' do
@@ -61,7 +73,7 @@ describe 'Client construction' do
61
73
  end
62
74
 
63
75
  it 'connects directly' do
64
- primary_address = ClusterConfig.instance.primary_address
76
+ primary_address = ClusterConfig.instance.primary_address_str
65
77
  client = ClientRegistry.instance.new_local_client([primary_address],
66
78
  base_options.merge(connect: :direct))
67
79
  client['client_construction'].insert_one(test: 1)
@@ -84,7 +96,7 @@ describe 'Client construction' do
84
96
  end
85
97
 
86
98
  it 'connects directly' do
87
- primary_address = ClusterConfig.instance.primary_address
99
+ primary_address = ClusterConfig.instance.primary_address_str
88
100
  client = ClientRegistry.instance.new_local_client([SpecConfig.instance.addresses.first],
89
101
  base_options.merge(connect: :direct))
90
102
  client['client_construction'].insert_one(test: 1)
@@ -3,11 +3,11 @@ require 'spec_helper'
3
3
  describe 'Direct connection with RS name' do
4
4
  before(:all) do
5
5
  # preload
6
- ClientRegistry.instance.close_all_clients
7
6
  ClusterConfig.instance.replica_set_name
8
- ClientRegistry.instance.close_all_clients
9
7
  end
10
8
 
9
+ clean_slate_for_all
10
+
11
11
  shared_examples_for 'passes RS name to topology' do
12
12
  it 'passes RS name to topology' do
13
13
  expect(client.cluster.topology.replica_set_name).to eq(replica_set_name)
@@ -0,0 +1,296 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Connection pool populator integration' do
4
+ let(:options) { {} }
5
+
6
+ let(:server_options) do
7
+ SpecConfig.instance.test_options.merge(options).merge(SpecConfig.instance.auth_options)
8
+ end
9
+
10
+ let(:address) do
11
+ Mongo::Address.new(SpecConfig.instance.addresses.first)
12
+ end
13
+
14
+ let(:monitoring) do
15
+ Mongo::Monitoring.new(monitoring: false)
16
+ end
17
+
18
+ let(:listeners) do
19
+ Mongo::Event::Listeners.new
20
+ end
21
+
22
+ declare_topology_double
23
+
24
+ let(:cluster) do
25
+ double('cluster').tap do |cl|
26
+ allow(cl).to receive(:topology).and_return(topology)
27
+ allow(cl).to receive(:app_metadata).and_return(app_metadata)
28
+ allow(cl).to receive(:options).and_return({})
29
+ allow(cl).to receive(:update_cluster_time)
30
+ allow(cl).to receive(:cluster_time).and_return(nil)
31
+ allow(cl).to receive(:run_sdam_flow)
32
+ end
33
+ end
34
+
35
+ let(:server) do
36
+ register_server(
37
+ Mongo::Server.new(address, cluster, monitoring, listeners,
38
+ {monitoring_io: false}.update(server_options)
39
+ ).tap do |server|
40
+ allow(server).to receive(:description).and_return(ClusterConfig.instance.primary_description)
41
+ end
42
+ )
43
+ end
44
+
45
+ let(:pool) do
46
+ server.pool
47
+ end
48
+
49
+ describe '#initialize' do
50
+ context 'when a min size is provided' do
51
+
52
+ let(:options) do
53
+ { min_pool_size: 2, max_pool_size: 5 }
54
+ end
55
+
56
+ it 'creates the pool with min pool size connections' do
57
+ pool
58
+ sleep 2
59
+
60
+ expect(pool.size).to eq(2)
61
+ expect(pool.available_count).to eq(2)
62
+ end
63
+
64
+ it 'does not use the same objects in the pool' do
65
+ expect(pool.check_out).to_not equal(pool.check_out)
66
+ end
67
+ end
68
+
69
+ context 'when min size is zero' do
70
+
71
+ it 'does not start the background thread' do
72
+ pool
73
+ sleep 2
74
+
75
+ expect(pool.size).to eq(0)
76
+ expect(pool.instance_variable_get('@populator').running?).to be false
77
+ end
78
+ end
79
+ end
80
+
81
+ describe '#clear' do
82
+ context 'when a min size is provided' do
83
+ let(:options) do
84
+ { min_pool_size: 1 }
85
+ end
86
+
87
+ it 'repopulates the pool periodically only up to min size' do
88
+ pool
89
+
90
+ sleep 2
91
+ expect(pool.size).to eq(1)
92
+ expect(pool.available_count).to eq(1)
93
+ first_connection = pool.check_out
94
+ pool.check_in(first_connection)
95
+
96
+ pool.clear
97
+
98
+ sleep 2
99
+ expect(pool.size).to eq(1)
100
+ expect(pool.available_count).to eq(1)
101
+ second_connection = pool.check_out
102
+ pool.check_in(second_connection)
103
+ expect(second_connection).to_not eq(first_connection)
104
+
105
+ # When populate is re-run, the pool size should not change
106
+ pool.populate
107
+ expect(pool.size).to eq(1)
108
+ expect(pool.available_count).to eq(1)
109
+ third_connection = pool.check_out
110
+ expect(third_connection).to eq(second_connection)
111
+ end
112
+ end
113
+ end
114
+
115
+ describe '#check_in' do
116
+ context 'when a min size is provided' do
117
+ let(:options) do
118
+ { min_pool_size: 1 }
119
+ end
120
+
121
+ it 'repopulates the pool after check_in of a closed connection' do
122
+ pool
123
+
124
+ sleep 2
125
+ expect(pool.size).to eq(1)
126
+ first_connection = pool.check_out
127
+ first_connection.disconnect!
128
+ expect(pool.size).to eq(1)
129
+
130
+ pool.check_in(first_connection)
131
+
132
+ sleep 2
133
+ expect(pool.size).to eq(1)
134
+ expect(pool.available_count).to eq(1)
135
+ second_connection = pool.check_out
136
+ expect(second_connection).to_not eq(first_connection)
137
+ end
138
+ end
139
+ end
140
+
141
+ describe '#check_out' do
142
+ context 'when min size and idle time are provided' do
143
+
144
+ let(:options) do
145
+ { max_pool_size: 2, min_pool_size: 2, max_idle_time: 0.5 }
146
+ end
147
+
148
+ it 'repopulates the pool after check_out empties idle connections' do
149
+ pool
150
+
151
+ first_connection = pool.check_out
152
+ second_connection = pool.check_out
153
+
154
+ first_connection.record_checkin!
155
+ second_connection.record_checkin!
156
+
157
+ pool.check_in(first_connection)
158
+ pool.check_in(second_connection)
159
+
160
+ expect(pool.size).to eq(2)
161
+
162
+ # let both connections become idle
163
+ sleep 0.5
164
+
165
+ # check_out should discard first two connections, trigger in-flow
166
+ # creation of a single connection, then wake up populate thread
167
+ third_connection = pool.check_out
168
+ expect(third_connection).to_not eq(first_connection)
169
+ expect(third_connection).to_not eq(second_connection)
170
+
171
+ # populate thread should create a new connection for the pool
172
+ sleep 2
173
+ expect(pool.size).to eq(2)
174
+ fourth_connection = pool.check_out
175
+ expect(fourth_connection).to_not eq(first_connection)
176
+ expect(fourth_connection).to_not eq(second_connection)
177
+ expect(fourth_connection).to_not eq(third_connection)
178
+ end
179
+ end
180
+ end
181
+
182
+ describe '#close' do
183
+ context 'when min size is provided' do
184
+
185
+ let(:options) do
186
+ { min_pool_size: 2, max_pool_size: 5 }
187
+ end
188
+
189
+ it 'terminates and does not repopulate the pool after pool is closed' do
190
+ pool
191
+
192
+ sleep 2
193
+ expect(pool.size).to eq(2)
194
+
195
+ connection = pool.check_out
196
+
197
+ expect(pool.size).to eq(2)
198
+ pool.close(force: true)
199
+
200
+ expect(pool.closed?).to be true
201
+ expect(pool.instance_variable_get('@available_connections').empty?).to be true
202
+ expect(pool.instance_variable_get('@checked_out_connections').empty?).to be true
203
+
204
+ # populate thread should terminate
205
+ sleep 2
206
+ expect(pool.instance_variable_get('@populator').running?).to be false
207
+ expect(pool.closed?).to be true
208
+ end
209
+ end
210
+ end
211
+
212
+ describe '#close_idle_sockets' do
213
+ context 'when min size and idle time are provided' do
214
+ let(:options) do
215
+ { min_pool_size: 1, max_idle_time: 0.5 }
216
+ end
217
+
218
+ it 'repopulates pool after sockets are closes' do
219
+ pool
220
+
221
+ sleep 2
222
+ expect(pool.size).to eq(1)
223
+
224
+ connection = pool.check_out
225
+ connection.record_checkin!
226
+ pool.check_in(connection)
227
+
228
+ # let the connection become idle
229
+ sleep 0.5
230
+
231
+ # close idle_sockets should trigger populate
232
+ pool.close_idle_sockets
233
+
234
+ sleep 2
235
+ expect(pool.size).to eq(1)
236
+ expect(pool.check_out).not_to eq(connection)
237
+ end
238
+ end
239
+ end
240
+
241
+ describe '#populate' do
242
+ let(:options) do
243
+ { min_pool_size: 1 }
244
+ end
245
+
246
+ context 'when populate encounters a network error twice' do
247
+ it 'retries once and does not stop the populator' do
248
+ expect_any_instance_of(Mongo::Server::ConnectionPool).to \
249
+ receive(:create_and_add_connection).twice.and_raise(Mongo::Error::SocketError)
250
+ pool
251
+ sleep 2
252
+ expect(pool.populator.running?).to be true
253
+ end
254
+ end
255
+
256
+ context 'when populate encounters a non-network error' do
257
+ it 'does not retry and does not stop the populator' do
258
+ expect_any_instance_of(Mongo::Server::ConnectionPool).to \
259
+ receive(:create_and_add_connection).and_raise(Mongo::Error)
260
+ pool
261
+ sleep 2
262
+ expect(pool.populator.running?).to be true
263
+ end
264
+ end
265
+ end
266
+
267
+ describe 'when forking is enabled' do
268
+ only_mri
269
+
270
+ context 'when min size is provided' do
271
+ min_server_version '2.8'
272
+
273
+ it 'populates the parent and child pools' do
274
+ client = ClientRegistry.instance.new_local_client([SpecConfig.instance.addresses.first],
275
+ server_options.merge(min_pool_size: 2, max_pool_size: 5))
276
+ # let pool populate
277
+ sleep 2
278
+ server = client.cluster.next_primary
279
+ pool = server.pool
280
+ expect(pool.size).to eq(2)
281
+
282
+ fork do
283
+ # follow forking guidance
284
+ client.close
285
+ client.reconnect
286
+ # let pool populate
287
+ sleep 2
288
+
289
+ server = client.cluster.next_primary
290
+ pool = server.pool
291
+ expect(pool.size).to eq(2)
292
+ end
293
+ end
294
+ end
295
+ end
296
+ end