mongo 2.11.0 → 2.11.5

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 (95) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/Rakefile +24 -0
  5. data/lib/mongo/address.rb +53 -37
  6. data/lib/mongo/auth.rb +30 -10
  7. data/lib/mongo/auth/cr.rb +1 -0
  8. data/lib/mongo/auth/cr/conversation.rb +13 -13
  9. data/lib/mongo/auth/ldap.rb +2 -1
  10. data/lib/mongo/auth/ldap/conversation.rb +9 -12
  11. data/lib/mongo/auth/scram.rb +1 -0
  12. data/lib/mongo/auth/scram/conversation.rb +36 -27
  13. data/lib/mongo/auth/user.rb +7 -1
  14. data/lib/mongo/auth/x509.rb +2 -1
  15. data/lib/mongo/auth/x509/conversation.rb +9 -9
  16. data/lib/mongo/bulk_write/transformable.rb +3 -3
  17. data/lib/mongo/client.rb +17 -6
  18. data/lib/mongo/cluster.rb +67 -49
  19. data/lib/mongo/cluster/sdam_flow.rb +87 -3
  20. data/lib/mongo/collection/view/readable.rb +3 -1
  21. data/lib/mongo/collection/view/writable.rb +3 -3
  22. data/lib/mongo/cursor/builder/kill_cursors_command.rb +8 -1
  23. data/lib/mongo/cursor/builder/op_kill_cursors.rb +8 -1
  24. data/lib/mongo/database.rb +1 -1
  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/protocol/serializers.rb +12 -2
  30. data/lib/mongo/retryable.rb +33 -8
  31. data/lib/mongo/server.rb +13 -6
  32. data/lib/mongo/server/connection.rb +15 -8
  33. data/lib/mongo/server/connection_base.rb +7 -4
  34. data/lib/mongo/server/description.rb +34 -21
  35. data/lib/mongo/server/monitor.rb +1 -1
  36. data/lib/mongo/server/monitor/connection.rb +2 -3
  37. data/lib/mongo/session.rb +10 -10
  38. data/lib/mongo/socket.rb +10 -1
  39. data/lib/mongo/uri.rb +1 -1
  40. data/lib/mongo/version.rb +1 -1
  41. data/mongo.gemspec +1 -1
  42. data/spec/README.md +13 -0
  43. data/spec/integration/auth_spec.rb +27 -8
  44. data/spec/integration/bson_symbol_spec.rb +34 -0
  45. data/spec/integration/client_construction_spec.rb +14 -0
  46. data/spec/integration/client_options_spec.rb +5 -5
  47. data/spec/integration/connection_spec.rb +57 -9
  48. data/spec/integration/crud_spec.rb +45 -0
  49. data/spec/integration/cursor_reaping_spec.rb +2 -1
  50. data/spec/integration/grid_fs_bucket_spec.rb +48 -0
  51. data/spec/integration/retryable_errors_spec.rb +204 -39
  52. data/spec/integration/retryable_writes_spec.rb +36 -36
  53. data/spec/integration/size_limit_spec.rb~12e1e9c4f... RUBY-2242 Fix zlib compression (#2021) +98 -0
  54. data/spec/lite_spec_helper.rb +1 -0
  55. data/spec/mongo/address_spec.rb +19 -13
  56. data/spec/mongo/auth/ldap/conversation_spec.rb +1 -1
  57. data/spec/mongo/auth/scram/conversation_spec.rb +25 -14
  58. data/spec/mongo/auth/user/view_spec.rb +36 -1
  59. data/spec/mongo/auth/user_spec.rb +12 -0
  60. data/spec/mongo/auth/x509/conversation_spec.rb +1 -1
  61. data/spec/mongo/bulk_write_spec.rb +2 -2
  62. data/spec/mongo/client_construction_spec.rb +1 -21
  63. data/spec/mongo/cluster_spec.rb +57 -0
  64. data/spec/mongo/collection/view/map_reduce_spec.rb +1 -1
  65. data/spec/mongo/collection_spec.rb +26 -2
  66. data/spec/mongo/cursor/builder/op_kill_cursors_spec.rb +56 -0
  67. data/spec/mongo/server/connection_spec.rb +76 -8
  68. data/spec/mongo/server/monitor/connection_spec.rb +14 -7
  69. data/spec/mongo/socket/ssl_spec.rb +132 -98
  70. data/spec/mongo/socket/tcp_spec.rb +1 -9
  71. data/spec/mongo/uri_spec.rb +1 -1
  72. data/spec/runners/sdam/verifier.rb +91 -0
  73. data/spec/spec_tests/data/sdam/rs/primary_address_change.yml +29 -0
  74. data/spec/spec_tests/data/sdam/rs/primary_mismatched_me.yml +27 -23
  75. data/spec/spec_tests/data/sdam/rs/primary_to_no_primary_mismatched_me.yml +56 -79
  76. data/spec/spec_tests/data/sdam/sharded/primary_address_change.yml +21 -0
  77. data/spec/spec_tests/data/sdam/sharded/primary_mismatched_me.yml +22 -0
  78. data/spec/spec_tests/data/sdam/single/primary_address_change.yml +24 -0
  79. data/spec/spec_tests/data/sdam/single/primary_mismatched_me.yml +25 -0
  80. data/spec/spec_tests/data/sdam_monitoring/replica_set_with_me_mismatch.yml +159 -0
  81. data/spec/spec_tests/data/sdam_monitoring/{replica_set_other_seed.yml → replica_set_with_primary_change.yml} +97 -101
  82. data/spec/spec_tests/data/sdam_monitoring/replica_set_with_primary_removal.yml +22 -18
  83. data/spec/spec_tests/data/sdam_monitoring/standalone_to_rs_with_me_mismatch.yml +90 -0
  84. data/spec/spec_tests/sdam_monitoring_spec.rb +9 -4
  85. data/spec/support/cluster_config.rb +36 -0
  86. data/spec/support/cluster_tools.rb +5 -3
  87. data/spec/support/command_monitoring.rb +1 -1
  88. data/spec/support/constraints.rb +18 -18
  89. data/spec/support/lite_constraints.rb +8 -0
  90. data/spec/support/sdam_monitoring.rb +0 -115
  91. data/spec/support/server_discovery_and_monitoring.rb +2 -0
  92. data/spec/support/spec_config.rb +1 -1
  93. data/spec/support/utils.rb +11 -1
  94. metadata +687 -659
  95. metadata.gz.sig +3 -2
@@ -151,6 +151,8 @@ module Mongo
151
151
  # authorized for.
152
152
  # @option options [ String ] :user The user name.
153
153
  # @option options [ String ] :password The user's password.
154
+ # @option options [ String ] :pwd Legacy option for the user's password.
155
+ # If :password and :pwd are both specified, :password takes precedence.
154
156
  # @option options [ Symbol ] :auth_mech The authorization mechanism.
155
157
  # @option options [ Array<String>, Array<Hash> ] roles The user roles.
156
158
  # @option options [ String ] :client_key The user's client key cached from a previous
@@ -196,7 +198,11 @@ module Mongo
196
198
  #
197
199
  # @since 2.0.0
198
200
  def spec
199
- { pwd: password, roles: roles }
201
+ {roles: roles}.tap do |spec|
202
+ if password
203
+ spec[:pwd] = password
204
+ end
205
+ end
200
206
  end
201
207
 
202
208
  private
@@ -20,6 +20,7 @@ module Mongo
20
20
  # Defines behavior for X.509 authentication.
21
21
  #
22
22
  # @since 2.0.0
23
+ # @api private
23
24
  class X509
24
25
 
25
26
  # The authentication mechinism string.
@@ -67,7 +68,7 @@ module Mongo
67
68
  conversation = Conversation.new(user)
68
69
  reply = connection.dispatch([ conversation.start(connection) ])
69
70
  connection.update_cluster_time(Operation::Result.new(reply))
70
- conversation.finalize(reply)
71
+ conversation.finalize(reply, connection)
71
72
  end
72
73
  end
73
74
  end
@@ -42,26 +42,26 @@ module Mongo
42
42
  #
43
43
  # @param [ Protocol::Message ] reply The reply of the previous
44
44
  # message.
45
+ # @param [ Server::Connection ] connection The connection being
46
+ # authenticated.
45
47
  #
46
48
  # @return [ Protocol::Query ] The next message to send.
47
49
  #
48
50
  # @since 2.0.0
49
- def finalize(reply)
50
- validate!(reply)
51
+ def finalize(reply, connection)
52
+ validate!(reply, connection.server)
51
53
  end
52
54
 
53
55
  # Start the X.509 conversation. This returns the first message that
54
56
  # needs to be sent to the server.
55
57
  #
56
- # @example Start the conversation.
57
- # conversation.start
58
- #
59
- # @param [ Mongo::Server::Connection ] connection The connection being authenticated.
58
+ # @param [ Server::Connection ] connection The connection being
59
+ # authenticated.
60
60
  #
61
61
  # @return [ Protocol::Query ] The first X.509 conversation message.
62
62
  #
63
63
  # @since 2.0.0
64
- def start(connection = nil)
64
+ def start(connection)
65
65
  login = LOGIN.merge(mechanism: X509::MECHANISM)
66
66
  login[:user] = user.name if user.name
67
67
  if connection && connection.features.op_msg_enabled?
@@ -103,9 +103,9 @@ module Mongo
103
103
 
104
104
  private
105
105
 
106
- def validate!(reply)
106
+ def validate!(reply, server)
107
107
  if reply.documents[0][Operation::Result::OK] != 1
108
- raise Unauthorized.new(user, used_mechanism: MECHANISM)
108
+ raise Unauthorized.new(user, used_mechanism: MECHANISM, server: server)
109
109
  end
110
110
  @reply = reply
111
111
  end
@@ -92,7 +92,7 @@ module Mongo
92
92
  Operation::U => doc[:replacement],
93
93
  }.tap do |d|
94
94
  if doc[:upsert]
95
- d[:upsert] = true
95
+ d['upsert'] = true
96
96
  end
97
97
  d[Operation::COLLATION] = doc[:collation] if doc[:collation]
98
98
  end
@@ -108,7 +108,7 @@ module Mongo
108
108
  Operation::MULTI => true,
109
109
  }.tap do |d|
110
110
  if doc[:upsert]
111
- d[:upsert] = true
111
+ d['upsert'] = true
112
112
  end
113
113
  d[Operation::COLLATION] = doc[:collation] if doc[:collation]
114
114
  d[Operation::ARRAY_FILTERS] = doc[:array_filters] if doc[:array_filters]
@@ -124,7 +124,7 @@ module Mongo
124
124
  Operation::U => doc[:update],
125
125
  }.tap do |d|
126
126
  if doc[:upsert]
127
- d[:upsert] = true
127
+ d['upsert'] = true
128
128
  end
129
129
  d[Operation::COLLATION] = doc[:collation] if doc[:collation]
130
130
  d[Operation::ARRAY_FILTERS] = doc[:array_filters] if doc[:array_filters]
@@ -756,8 +756,8 @@ module Mongo
756
756
  #
757
757
  # @since 2.5.0
758
758
  def start_session(options = {})
759
- cluster.send(:get_session, self, options.merge(implicit: false)) ||
760
- (raise Error::InvalidSession.new(Session::SESSIONS_NOT_SUPPORTED))
759
+ get_session(options.merge(implicit: false)) or
760
+ raise Error::InvalidSession.new(Session::SESSIONS_NOT_SUPPORTED)
761
761
  end
762
762
 
763
763
  # As of version 3.6 of the MongoDB server, a ``$changeStream`` pipeline stage is supported
@@ -827,14 +827,25 @@ module Mongo
827
827
  # The session is implicit unless options[:implicit] is given.
828
828
  # If deployment does not support session, returns nil.
829
829
  #
830
- # @note This method will return nil if deployment has no data-bearing
831
- # servers at the time of the call.
830
+ # @return [ Session | nil ] Session object or nil if sessions are not
831
+ # supported by the deployment.
832
832
  def get_session(options = {})
833
- cluster.send(:get_session, self, options)
833
+ if options[:session]
834
+ return options[:session].validate!(self)
835
+ end
836
+
837
+ if cluster.sessions_supported?
838
+ Session.new(cluster.session_pool.checkout, self, { implicit: true }.merge(options))
839
+ end
834
840
  end
835
841
 
836
842
  def with_session(options = {}, &block)
837
- cluster.send(:with_session, self, options, &block)
843
+ session = get_session(options)
844
+ yield(session)
845
+ ensure
846
+ if session && session.implicit?
847
+ session.end_session
848
+ end
838
849
  end
839
850
 
840
851
  def initialize_copy(original)
@@ -109,6 +109,8 @@ module Mongo
109
109
  options[:cleanup] = false
110
110
  end
111
111
 
112
+ seeds = seeds.uniq
113
+
112
114
  @servers = []
113
115
  @monitoring = monitoring
114
116
  @event_listeners = Event::Listeners.new
@@ -162,12 +164,9 @@ module Mongo
162
164
  return
163
165
  end
164
166
 
165
- # Need to record start time prior to starting monitoring
166
- start_time = Time.now
167
-
168
- servers.each do |server|
169
- server.start_monitoring
170
- end
167
+ # Update instance variables prior to starting monitoring threads.
168
+ @connecting = false
169
+ @connected = true
171
170
 
172
171
  if options[:cleanup] != false
173
172
  @cursor_reaper = CursorReaper.new
@@ -182,8 +181,12 @@ module Mongo
182
181
  @periodic_executor.run!
183
182
  end
184
183
 
185
- @connecting = false
186
- @connected = true
184
+ # Need to record start time prior to starting monitoring
185
+ start_time = Time.now
186
+
187
+ servers.each do |server|
188
+ server.start_monitoring
189
+ end
187
190
 
188
191
  if options[:scan] != false
189
192
  server_selection_timeout = options[:server_selection_timeout] || ServerSelector::SERVER_SELECTION_TIMEOUT
@@ -743,30 +746,49 @@ module Mongo
743
746
  # server.remove('127.0.0.1:27017')
744
747
  #
745
748
  # @param [ String ] host The host/port or socket address.
749
+ # @param [ true | false ] disconnect Whether to disconnect the servers
750
+ # being removed. For internal driver use only.
751
+ #
752
+ # @return [ Array<Server> | true | false ] If disconnect is any value other
753
+ # than false, including nil, returns whether any servers were removed.
754
+ # If disconnect is false, returns an array of servers that were removed
755
+ # (and should be disconnected by the caller).
746
756
  #
747
- # @return [ true|false ] Whether any servers were removed.
757
+ # @note The return value of this method is not part of the driver's
758
+ # public API.
748
759
  #
749
- # @since 2.0.0, return value added in 2.7.0
750
- def remove(host)
760
+ # @since 2.0.0
761
+ def remove(host, disconnect: true)
751
762
  address = Address.new(host)
752
763
  removed_servers = @servers.select { |s| s.address == address }
753
764
  @update_lock.synchronize { @servers = @servers - removed_servers }
754
- removed_servers.each do |server|
755
- if server.connected?
756
- server.disconnect!
757
- publish_sdam_event(
758
- Monitoring::SERVER_CLOSED,
759
- Monitoring::Event::ServerClosed.new(address, topology)
760
- )
765
+ if disconnect != false
766
+ removed_servers.each do |server|
767
+ disconnect_server_if_connected(server)
761
768
  end
762
769
  end
763
- removed_servers.any?
770
+ if disconnect != false
771
+ removed_servers.any?
772
+ else
773
+ removed_servers
774
+ end
764
775
  end
765
776
 
766
777
  # @api private
767
778
  def update_topology(new_topology)
768
779
  old_topology = topology
769
780
  @topology = new_topology
781
+
782
+ # If new topology has data bearing servers, we know for sure whether
783
+ # sessions are supported - update our cached value.
784
+ # If new topology has no data bearing servers, leave the old value
785
+ # as it is and sessions_supported? method will perform server selection
786
+ # to try to determine session support accurately, falling back to the
787
+ # last known value.
788
+ if topology.data_bearing_servers?
789
+ @sessions_supported = !!topology.logical_session_timeout
790
+ end
791
+
770
792
  publish_sdam_event(
771
793
  Monitoring::TOPOLOGY_CHANGED,
772
794
  Monitoring::Event::TopologyChanged.new(old_topology, topology)
@@ -778,53 +800,49 @@ module Mongo
778
800
  @update_lock.synchronize { @servers.dup }
779
801
  end
780
802
 
781
- private
782
-
783
- # If options[:session] is set, validates that session and returns it.
784
- # If deployment supports sessions, creates a new session and returns it.
785
- # The session is implicit unless options[:implicit] is given.
786
- # If deployment does not support session, returns nil.
787
- #
788
- # @note This method will return nil if deployment has no data-bearing
789
- # servers at the time of the call.
790
- def get_session(client, options = {})
791
- return options[:session].validate!(self) if options[:session]
792
- if sessions_supported?
793
- Session.new(@session_pool.checkout, client, { implicit: true }.merge(options))
803
+ # @api private
804
+ def disconnect_server_if_connected(server)
805
+ if server.connected?
806
+ server.disconnect!
807
+ publish_sdam_event(
808
+ Monitoring::SERVER_CLOSED,
809
+ Monitoring::Event::ServerClosed.new(server.address, topology)
810
+ )
794
811
  end
795
812
  end
796
813
 
797
- def with_session(client, options = {})
798
- session = get_session(client, options)
799
- yield(session)
800
- ensure
801
- session.end_session if (session && session.implicit?)
802
- end
803
-
804
- # Returns whether the deployment (as this term is defined in the sessions
805
- # spec) supports sessions.
814
+ # Returns whether the deployment that the driver is connected to supports
815
+ # sessions.
806
816
  #
807
- # @note If the cluster has no data bearing servers, for example because
808
- # the deployment is in the middle of a failover, this method returns
809
- # false.
817
+ # Session support may change over time, for example due to servers in the
818
+ # deployment being upgraded or downgraded. This method returns the
819
+ # current information if the client is connected to at least one data
820
+ # bearing server. If the client is currently not connected to any data
821
+ # bearing servers, this method returns the last known value for whether
822
+ # the deployment supports sessions.
810
823
  #
811
- # @note This method returns as soon as the driver connects to any single
812
- # server in the deployment. Whether deployment overall supports sessions
813
- # can change depending on how many servers have been contacted, if
814
- # the servers are configured differently.
824
+ # @return [ true | false ] Whether deployment supports sessions.
825
+ # @api private
815
826
  def sessions_supported?
816
827
  if topology.data_bearing_servers?
817
828
  return !!topology.logical_session_timeout
818
829
  end
819
830
 
831
+ # No data bearing servers known - perform server selection to try to
832
+ # get a response from at least one of them, to return an accurate
833
+ # assessment of whether sessions are currently supported.
820
834
  begin
821
835
  ServerSelector.get(mode: :primary_preferred).select_server(self)
822
836
  !!topology.logical_session_timeout
823
837
  rescue Error::NoServerAvailable
824
- false
838
+ # We haven't been able to contact any servers - use last known
839
+ # value for esssion support.
840
+ @sessions_supported || false
825
841
  end
826
842
  end
827
843
 
844
+ private
845
+
828
846
  # @api private
829
847
  def start_stop_srv_monitor
830
848
  # SRV URI is either always given or not for a given cluster, if one
@@ -30,6 +30,7 @@ class Mongo::Cluster
30
30
  @topology = cluster.topology
31
31
  @original_desc = @previous_desc = previous_desc
32
32
  @updated_desc = updated_desc
33
+ @servers_to_disconnect = []
33
34
  end
34
35
 
35
36
  attr_reader :cluster
@@ -73,6 +74,60 @@ class Mongo::Cluster
73
74
  end
74
75
 
75
76
  def server_description_changed
77
+ if updated_desc.me_mismatch? && updated_desc.primary? &&
78
+ (topology.unknown? || topology.replica_set?)
79
+ then
80
+ # When the driver receives a description claiming to be a primary,
81
+ # we are obligated by spec tests to add and remove hosts in that
82
+ # description even if it also has a me mismatch. The me mismatch
83
+ # scenario though presents a number of problems:
84
+ #
85
+ # 1. Effectively, the server's address changes, meaning we cannot
86
+ # update the description of the server whose description change we
87
+ # are processing (instead servers are added and removed), but we
88
+ # behave to an extent as if we are updating the description, which
89
+ # causes a bunch of awkwardness.
90
+ # 2. The server for which we are processing the response will be
91
+ # removed from topology, which may cause the current thread to terminate
92
+ # prior to running the entire sdam flow. To deal with this we separate
93
+ # the removal event publication from actually removing the server
94
+ # from topology, which again complicates the flow.
95
+
96
+ # Primary-with-me-mismatch response could be the first one we receive
97
+ # when the topology is still unknown. Change to RS without primary
98
+ # in this case.
99
+ if topology.unknown?
100
+ @topology = Topology::ReplicaSetNoPrimary.new(
101
+ topology.options.merge(replica_set_name: updated_desc.replica_set_name),
102
+ topology.monitoring, self)
103
+ end
104
+
105
+ servers = add_servers_from_desc(updated_desc)
106
+ # Spec tests require us to remove servers based on data in descrptions
107
+ # with me mismatches. The driver will be more resilient if it only
108
+ # removed servers from descriptions with matching mes.
109
+ remove_servers_not_in_desc(updated_desc)
110
+
111
+ servers.each do |server|
112
+ server.start_monitoring
113
+ end
114
+
115
+ # The rest of sdam flow assumes the server being removed is not the one
116
+ # whose description we are processing, and publishes description update
117
+ # event. Since we are removing the server whose response we are
118
+ # processing, do not publish description change event but mark it
119
+ # published (by assigning to @previous_desc).
120
+ do_remove(updated_desc.address.to_s)
121
+ @previous_desc = updated_desc
122
+
123
+ # We may have removed the current primary, check if there is a primary.
124
+ check_if_has_primary
125
+ # Publish topology change event.
126
+ commit_changes
127
+ disconnect_servers
128
+ return
129
+ end
130
+
76
131
  unless update_server_descriptions
77
132
  # All of the transitions require that server whose updated_desc we are
78
133
  # processing is still in the cluster (i.e., was not removed as a result
@@ -142,6 +197,7 @@ class Mongo::Cluster
142
197
  end
143
198
 
144
199
  commit_changes
200
+ disconnect_servers
145
201
  end
146
202
 
147
203
  # Transitions from unknown to single topology type, when a standalone
@@ -337,9 +393,14 @@ class Mongo::Cluster
337
393
  end.flatten
338
394
  servers_list.each do |server|
339
395
  unless updated_desc_address_strs.include?(address_str = server.address.to_s)
396
+ updated_host = updated_desc.address.to_s
397
+ if updated_desc.me && updated_desc.me != updated_host
398
+ updated_host += " (self-identified as #{updated_desc.me})"
399
+ end
340
400
  log_warn(
341
401
  "Removing server #{address_str} because it is not in hosts reported by primary " +
342
- "#{updated_desc.address}"
402
+ "#{updated_host}. Reported hosts are: " +
403
+ updated_desc.hosts.join(', ')
343
404
  )
344
405
  do_remove(address_str)
345
406
  end
@@ -356,7 +417,21 @@ class Mongo::Cluster
356
417
  # Removes specified server from topology and warns if the topology ends
357
418
  # up with an empty server list as a result
358
419
  def do_remove(address_str)
359
- cluster.remove(address_str)
420
+ servers = cluster.remove(address_str, disconnect: false)
421
+ servers.each do |server|
422
+ # We need to publish server closed event here, but we cannot close
423
+ # the server because it could be the server owning the monitor in
424
+ # whose thread this flow is presently executing, in which case closing
425
+ # the server can terminate the thread and leave SDAM processing
426
+ # incomplete. Thus we have to remove the server from the cluster,
427
+ # publish the event, but do not call disconnect on the server until
428
+ # the very end when all processing has completed.
429
+ publish_sdam_event(
430
+ Mongo::Monitoring::SERVER_CLOSED,
431
+ Mongo::Monitoring::Event::ServerClosed.new(server.address, cluster.topology)
432
+ )
433
+ end
434
+ @servers_to_disconnect += servers
360
435
  if servers_list.empty?
361
436
  log_warn(
362
437
  "Topology now has no servers - this is likely a misconfiguration of the cluster and/or the application"
@@ -451,6 +526,15 @@ class Mongo::Cluster
451
526
  cluster.update_topology(topology)
452
527
  end
453
528
 
529
+ def disconnect_servers
530
+ while server = @servers_to_disconnect.shift
531
+ if server.connected?
532
+ # Do not publish server closed event, as this was already done
533
+ server.disconnect!
534
+ end
535
+ end
536
+ end
537
+
454
538
  # If the server being processed is identified as data bearing, creates the
455
539
  # server's connection pool so it can start populating
456
540
  def start_pool_if_data_bearing
@@ -468,7 +552,7 @@ class Mongo::Cluster
468
552
  # invoking this method.
469
553
  def check_if_has_primary
470
554
  unless topology.replica_set?
471
- raise ArgumentError, 'check_if_has_primary should only be called when topology is replica set'
555
+ raise ArgumentError, "check_if_has_primary should only be called when topology is replica set, but it is #{topology.class.name.sub(/.*::/, '')}"
472
556
  end
473
557
 
474
558
  primary = servers_list.detect do |server|