mongo 2.10.5 → 2.11.0.rc0

Sign up to get free protection for your applications and to get access to all the features.
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