From 51690479074471afc21e46b9395bcf0a09bc9b1f Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Fri, 22 Jan 2016 18:51:58 +0100 Subject: [PATCH 01/23] Event modules --- lib/mongo/cluster/topology.rb | 8 +++- .../event/server_description_changed.rb | 40 +++++++++++++++++++ .../monitoring/event/topology_changed.rb | 36 +++++++++++++++++ lib/mongo/monitoring/event/topology_closed.rb | 30 ++++++++++++++ .../monitoring/event/topology_opening.rb | 31 ++++++++++++++ 5 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 lib/mongo/monitoring/event/server_description_changed.rb create mode 100644 lib/mongo/monitoring/event/topology_changed.rb create mode 100644 lib/mongo/monitoring/event/topology_closed.rb create mode 100644 lib/mongo/monitoring/event/topology_opening.rb diff --git a/lib/mongo/cluster/topology.rb b/lib/mongo/cluster/topology.rb index 09eeac80f3..79d0b87d2e 100644 --- a/lib/mongo/cluster/topology.rb +++ b/lib/mongo/cluster/topology.rb @@ -46,7 +46,7 @@ module Topology # @return [ ReplicaSet, Sharded, Single ] The topology. # # @since 2.0.0 - def initial(seeds, options) + def initial(seeds, monitoring, options) if options.has_key?(:connect) OPTIONS.fetch(options[:connect]).new(options, seeds) elsif options.has_key?(:replica_set) @@ -55,6 +55,12 @@ def initial(seeds, options) Unknown.new(options, seeds) end end + + private + + def publish(monitoring, &block) + monitoring.completed(Monitoring::TOPOLOGY_OPENING, Monitoring::Event::TopologyOpening.new) + end end end end diff --git a/lib/mongo/monitoring/event/server_description_changed.rb b/lib/mongo/monitoring/event/server_description_changed.rb new file mode 100644 index 0000000000..4bed51c685 --- /dev/null +++ b/lib/mongo/monitoring/event/server_description_changed.rb @@ -0,0 +1,40 @@ +# Copyright (C) 2016 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module Mongo + class Monitoring + module Event + + # @since 2.3.0 + class ServerDescriptionChanged + + attr_reader :address + + attr_reader :cluster_id + + attr_reader :old_description + + attr_reader :new_description + + def initialize(address, cluster_id, old_description, new_description) + @address = address + @cluster_id = cluster_id + @old_description = old_description + @new_description = new_description + end + end + end + end +end + diff --git a/lib/mongo/monitoring/event/topology_changed.rb b/lib/mongo/monitoring/event/topology_changed.rb new file mode 100644 index 0000000000..fc658cf19f --- /dev/null +++ b/lib/mongo/monitoring/event/topology_changed.rb @@ -0,0 +1,36 @@ +# Copyright (C) 2016 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module Mongo + class Monitoring + module Event + + # @since 2.3.0 + class TopologyChanged + + attr_reader :cluster_id + + attr_reader :old_topology + + attr_reader :new_topology + + def initialize(cluster_id, old_topology, new_topology) + @cluster_id = cluster_id + @old_topology = old_topology + @new_topology = new_topology + end + end + end + end +end diff --git a/lib/mongo/monitoring/event/topology_closed.rb b/lib/mongo/monitoring/event/topology_closed.rb new file mode 100644 index 0000000000..3d22fbb7f6 --- /dev/null +++ b/lib/mongo/monitoring/event/topology_closed.rb @@ -0,0 +1,30 @@ +# Copyright (C) 2016 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module Mongo + class Monitoring + module Event + + # @since 2.3.0 + class TopologyClosed + + attr_reader :cluster_id + + def initialize(cluster_id) + @cluster_id = cluster_id + end + end + end + end +end diff --git a/lib/mongo/monitoring/event/topology_opening.rb b/lib/mongo/monitoring/event/topology_opening.rb new file mode 100644 index 0000000000..db11ddf14e --- /dev/null +++ b/lib/mongo/monitoring/event/topology_opening.rb @@ -0,0 +1,31 @@ +# Copyright (C) 2016 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module Mongo + class Monitoring + module Event + + # @since 2.3.0 + class TopologyOpening + + attr_reader :cluster_id + + def initialize(cluster_id) + @cluster_id = cluster_id + end + end + end + end +end + From 256f91b84ee116f66f93e98bf4d3a069ba586442 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Sun, 24 Jan 2016 19:15:52 +0100 Subject: [PATCH 02/23] Adding Rdoc --- lib/mongo/monitoring/event.rb | 4 ++++ .../event/server_description_changed.rb | 19 +++++++++++++++++++ .../monitoring/event/topology_changed.rb | 15 +++++++++++++++ lib/mongo/monitoring/event/topology_closed.rb | 11 +++++++++++ .../monitoring/event/topology_opening.rb | 11 +++++++++++ 5 files changed, 60 insertions(+) diff --git a/lib/mongo/monitoring/event.rb b/lib/mongo/monitoring/event.rb index 65c2844d18..afa22badba 100644 --- a/lib/mongo/monitoring/event.rb +++ b/lib/mongo/monitoring/event.rb @@ -16,3 +16,7 @@ require 'mongo/monitoring/event/command_started' require 'mongo/monitoring/event/command_succeeded' require 'mongo/monitoring/event/command_failed' +require 'mongo/monitoring/event/server_description_changed' +require 'mongo/monitoring/event/topology_changed' +require 'mongo/monitoring/event/topology_closed' +require 'mongo/monitoring/event/topology_opening' diff --git a/lib/mongo/monitoring/event/server_description_changed.rb b/lib/mongo/monitoring/event/server_description_changed.rb index 4bed51c685..5830ab8d66 100644 --- a/lib/mongo/monitoring/event/server_description_changed.rb +++ b/lib/mongo/monitoring/event/server_description_changed.rb @@ -16,17 +16,36 @@ module Mongo class Monitoring module Event + # Event fired when a server's description changes. + # # @since 2.3.0 class ServerDescriptionChanged + # @return [ Address ] address The server address. attr_reader :address + # @return [ Integer ] cluster_id The cluster id. attr_reader :cluster_id + # @return [ Server::Description ] old_description The old server + # description. attr_reader :old_description + # @return [ Server::Description ] new_description The new server + # description. attr_reader :new_description + # Create the event. + # + # @example Create the event. + # ServerDescriptionChanged.new(address, 1, old, new) + # + # @param [ Address ] address The server address. + # @param [ Integer ] cluster_id The cluster id. + # @param [ Server::Description ] old_description The old description. + # @param [ Server::Description ] new_description The new description. + # + # @since 2.3.0 def initialize(address, cluster_id, old_description, new_description) @address = address @cluster_id = cluster_id diff --git a/lib/mongo/monitoring/event/topology_changed.rb b/lib/mongo/monitoring/event/topology_changed.rb index fc658cf19f..17b50f2504 100644 --- a/lib/mongo/monitoring/event/topology_changed.rb +++ b/lib/mongo/monitoring/event/topology_changed.rb @@ -16,15 +16,30 @@ module Mongo class Monitoring module Event + # Event fired when the topology changes. + # # @since 2.3.0 class TopologyChanged + # @return [ Integer ] cluster_id The cluster id. attr_reader :cluster_id + # @return [ Cluster::Topology ] old_topology The old topology. attr_reader :old_topology + # @return [ Cluster::Topology ] new_topology The new topology. attr_reader :new_topology + # Create the event. + # + # @example Create the event. + # TopologyChanged.new(1, old, new) + # + # @param [ Integer ] cluster_id The cluster id. + # @param [ Cluster::Topology ] old_topology The old topology. + # @param [ Cluster::Topology ] new_topology The new topology. + # + # @since 2.3.0 def initialize(cluster_id, old_topology, new_topology) @cluster_id = cluster_id @old_topology = old_topology diff --git a/lib/mongo/monitoring/event/topology_closed.rb b/lib/mongo/monitoring/event/topology_closed.rb index 3d22fbb7f6..81b36ddc91 100644 --- a/lib/mongo/monitoring/event/topology_closed.rb +++ b/lib/mongo/monitoring/event/topology_closed.rb @@ -16,11 +16,22 @@ module Mongo class Monitoring module Event + # Event fired when the topology closes. + # # @since 2.3.0 class TopologyClosed + # @return [ Integer ] cluster_id The cluster id. attr_reader :cluster_id + # Create the event. + # + # @example Create the event. + # TopologyClosed.new(1) + # + # @param [ Integer ] cluster_id The cluster id. + # + # @since 2.3.0 def initialize(cluster_id) @cluster_id = cluster_id end diff --git a/lib/mongo/monitoring/event/topology_opening.rb b/lib/mongo/monitoring/event/topology_opening.rb index db11ddf14e..2f60058bc1 100644 --- a/lib/mongo/monitoring/event/topology_opening.rb +++ b/lib/mongo/monitoring/event/topology_opening.rb @@ -16,11 +16,22 @@ module Mongo class Monitoring module Event + # Event fired when the topology is opening. + # # @since 2.3.0 class TopologyOpening + # @return [ Integer ] cluster_id The cluster id. attr_reader :cluster_id + # Create the event. + # + # @example Create the event. + # TopologyOpening.new(1) + # + # @param [ Integer ] cluster_id The cluster id. + # + # @since 2.3.0 def initialize(cluster_id) @cluster_id = cluster_id end From f746b96e8862e98175f89dd1146a0e94dcbcc887 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Sun, 24 Jan 2016 19:18:14 +0100 Subject: [PATCH 03/23] Adding SDAM Monitoring topics --- lib/mongo/monitoring.rb | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/mongo/monitoring.rb b/lib/mongo/monitoring.rb index c2ddf1b91d..c39bfe6032 100644 --- a/lib/mongo/monitoring.rb +++ b/lib/mongo/monitoring.rb @@ -28,6 +28,26 @@ class Monitoring # @since 2.1.0 COMMAND = 'Command'.freeze + # Server description changed topic. + # + # @since 2.3.0 + SERVER_DESCRIPTION_CHANGED = 'ServerDescriptionChnaged'.freeze + + # Topology changed topic. + # + # @since 2.3.0 + TOPOLOGY_CHANGED = 'TopologyChanged'.freeze + + # Topology closed topic. + # + # @since 2.3.0 + TOPOLOGY_CLOSED = 'TopologyClosed'.freeze + + # Topology opening topic. + # + # @since 2.3.0 + TOPOLOGY_OPENING = 'TopologyOpening'.freeze + @@operation_id = 0 @@operation_id_lock = Mutex.new From 1be6d83ad20fe4659912ffa0446e69f9af7e93d4 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Sun, 24 Jan 2016 20:14:53 +0100 Subject: [PATCH 04/23] Add monitoring into topologies --- lib/mongo/cluster.rb | 2 +- lib/mongo/cluster/topology.rb | 19 ++++++++++++------- lib/mongo/cluster/topology/replica_set.rb | 8 +++++++- lib/mongo/cluster/topology/sharded.rb | 8 +++++++- lib/mongo/cluster/topology/single.rb | 14 ++++++++++---- lib/mongo/cluster/topology/unknown.rb | 14 ++++++++++---- .../event/server_description_changed.rb | 12 ++++++------ .../monitoring/event/topology_changed.rb | 12 ++++++------ lib/mongo/monitoring/event/topology_closed.rb | 12 ++++++------ .../monitoring/event/topology_opening.rb | 12 ++++++------ .../cluster/topology/replica_set_spec.rb | 16 ++++++++-------- spec/mongo/cluster/topology/sharded_spec.rb | 2 +- spec/mongo/cluster/topology/single_spec.rb | 8 ++++---- spec/mongo/cluster/topology/unknown_spec.rb | 6 +++++- spec/mongo/cluster/topology_spec.rb | 18 +++++++++++------- spec/mongo/cluster_spec.rb | 8 ++++---- spec/mongo/server_selection_spec.rb | 8 ++++---- 17 files changed, 108 insertions(+), 71 deletions(-) diff --git a/lib/mongo/cluster.rb b/lib/mongo/cluster.rb index 79b4b05da7..3fe26747a7 100644 --- a/lib/mongo/cluster.rb +++ b/lib/mongo/cluster.rb @@ -104,7 +104,7 @@ def initialize(seeds, monitoring, options = Options::Redacted.new) @monitoring = monitoring @event_listeners = Event::Listeners.new @options = options.freeze - @topology = Topology.initial(seeds, options) + @topology = Topology.initial(seeds, monitoring, options) @update_lock = Mutex.new @pool_lock = Mutex.new diff --git a/lib/mongo/cluster/topology.rb b/lib/mongo/cluster/topology.rb index 79d0b87d2e..507a204f4b 100644 --- a/lib/mongo/cluster/topology.rb +++ b/lib/mongo/cluster/topology.rb @@ -33,7 +33,7 @@ module Topology replica_set: ReplicaSet, sharded: Sharded, direct: Single - } + }.freeze # Get the initial cluster topology for the provided options. # @@ -41,25 +41,30 @@ module Topology # Topology.initial(topology: :replica_set) # # @param [ Array ] seeds The addresses of the configured servers. + # @param [ Monitoring ] monitoring The monitoring. # @param [ Hash ] options The cluster options. # # @return [ ReplicaSet, Sharded, Single ] The topology. # # @since 2.0.0 def initial(seeds, monitoring, options) - if options.has_key?(:connect) - OPTIONS.fetch(options[:connect]).new(options, seeds) + topology = if options.has_key?(:connect) + OPTIONS.fetch(options[:connect]).new(options, monitoring, seeds) elsif options.has_key?(:replica_set) - ReplicaSet.new(options, seeds) + ReplicaSet.new(options, monitoring, options) else - Unknown.new(options, seeds) + Unknown.new(options, monitoring, seeds) end + publish(monitoring, topology) unless options[:monitoring] == false + topology end private - def publish(monitoring, &block) - monitoring.completed(Monitoring::TOPOLOGY_OPENING, Monitoring::Event::TopologyOpening.new) + def publish(monitoring, topology) + monitoring.succeeded( + Monitoring::TOPOLOGY_OPENING, Monitoring::Event::TopologyOpening.new(topology) + ) end end end diff --git a/lib/mongo/cluster/topology/replica_set.rb b/lib/mongo/cluster/topology/replica_set.rb index a63fae8296..92cf4a4ca5 100644 --- a/lib/mongo/cluster/topology/replica_set.rb +++ b/lib/mongo/cluster/topology/replica_set.rb @@ -30,6 +30,9 @@ class ReplicaSet # @return [ Hash ] options The options. attr_reader :options + # @return [ Monitoring ] monitoring The monitoring. + attr_reader :monitoring + # The display name for the topology. # # @since 2.0.0 @@ -85,10 +88,13 @@ def elect_primary(description, servers) # ReplicaSet.new(options) # # @param [ Hash ] options The options. + # @param [ Monitoring ] monitoring The monitoring. + # @param [ Array ] seeds The seeds. # # @since 2.0.0 - def initialize(options, seeds = []) + def initialize(options, monitoring, seeds = []) @options = options + @monitoring = monitoring @max_election_id = nil @max_set_version = nil end diff --git a/lib/mongo/cluster/topology/sharded.rb b/lib/mongo/cluster/topology/sharded.rb index 4b463429e8..11cf348b04 100644 --- a/lib/mongo/cluster/topology/sharded.rb +++ b/lib/mongo/cluster/topology/sharded.rb @@ -26,6 +26,9 @@ class Sharded # @since 2.0.0 NAME = 'Sharded'.freeze + # @return [ Monitoring ] monitoring The monitoring. + attr_reader :monitoring + # Get the display name. # # @example Get the display name. @@ -57,10 +60,13 @@ def elect_primary(description, servers); self; end # Sharded.new(options) # # @param [ Hash ] options The options. + # @param [ Monitoring ] monitoring The monitoring. + # @param [ Array ] seeds The seeds. # # @since 2.0.0 - def initialize(options, seeds = []) + def initialize(options, monitoring, seeds = []) @options = options + @monitoring = monitoring end # A sharded topology is not a replica set. diff --git a/lib/mongo/cluster/topology/single.rb b/lib/mongo/cluster/topology/single.rb index 4bd46aa5d8..47af8f707d 100644 --- a/lib/mongo/cluster/topology/single.rb +++ b/lib/mongo/cluster/topology/single.rb @@ -21,14 +21,17 @@ module Topology # @since 2.0.0 class Single - # @return [ String ] seed The seed address. - attr_reader :seed - # The display name for the topology. # # @since 2.0.0 NAME = 'Single'.freeze + # @return [ String ] seed The seed address. + attr_reader :seed + + # @return [ monitoring ] monitoring the monitoring. + attr_reader :monitoring + # Get the display name. # # @example Get the display name. @@ -60,10 +63,13 @@ def elect_primary(description, servers); self; end # Single.new(options) # # @param [ Hash ] options The options. + # @param [ Monitoring ] monitoring The monitoring. + # @param [ Array ] seeds The seeds. # # @since 2.0.0 - def initialize(options, seeds = []) + def initialize(options, monitoring, seeds = []) @options = options + @monitoring = monitoring @seed = seeds.first end diff --git a/lib/mongo/cluster/topology/unknown.rb b/lib/mongo/cluster/topology/unknown.rb index 017030393c..3de3dce61a 100644 --- a/lib/mongo/cluster/topology/unknown.rb +++ b/lib/mongo/cluster/topology/unknown.rb @@ -30,6 +30,9 @@ class Unknown # @return [ Hash ] options The options. attr_reader :options + # @return [ Monitoring ] monitoring The monitoring. + attr_reader :monitoring + # Get the display name. # # @example Get the display name. @@ -56,7 +59,7 @@ def display_name def elect_primary(description, servers) if description.mongos? log_debug("Mongos #{description.address.to_s} discovered.") - Sharded.new(options) + Sharded.new(options, monitoring) else initialize_replica_set(description, servers) end @@ -68,10 +71,13 @@ def elect_primary(description, servers) # Unknown.new(options) # # @param [ Hash ] options The options. + # @param [ Monitoring ] monitoring The monitoring. + # @param [ Array ] seeds The seeds. # # @since 2.0.0 - def initialize(options, seeds = []) + def initialize(options, monitoring, seeds = []) @options = options + @monitoring = monitoring @seeds = seeds end @@ -195,7 +201,7 @@ def remove_server?(description, server) # @since 2.0.6 def standalone_discovered if @seeds.size == 1 - Single.new(options, @seeds) + Single.new(options, monitoring, @seeds) else self end @@ -213,7 +219,7 @@ def initialize_replica_set(description, servers) server.description.unknown! end end - ReplicaSet.new(options.merge(:replica_set => description.replica_set_name)) + ReplicaSet.new(options.merge(:replica_set => description.replica_set_name), monitoring) end end end diff --git a/lib/mongo/monitoring/event/server_description_changed.rb b/lib/mongo/monitoring/event/server_description_changed.rb index 5830ab8d66..e0d75a0f55 100644 --- a/lib/mongo/monitoring/event/server_description_changed.rb +++ b/lib/mongo/monitoring/event/server_description_changed.rb @@ -24,8 +24,8 @@ class ServerDescriptionChanged # @return [ Address ] address The server address. attr_reader :address - # @return [ Integer ] cluster_id The cluster id. - attr_reader :cluster_id + # @return [ Topology ] topology The topology. + attr_reader :topology # @return [ Server::Description ] old_description The old server # description. @@ -38,17 +38,17 @@ class ServerDescriptionChanged # Create the event. # # @example Create the event. - # ServerDescriptionChanged.new(address, 1, old, new) + # ServerDescriptionChanged.new(address, topology, old, new) # # @param [ Address ] address The server address. - # @param [ Integer ] cluster_id The cluster id. + # @param [ Integer ] topology The topology. # @param [ Server::Description ] old_description The old description. # @param [ Server::Description ] new_description The new description. # # @since 2.3.0 - def initialize(address, cluster_id, old_description, new_description) + def initialize(address, topology, old_description, new_description) @address = address - @cluster_id = cluster_id + @topology = topology @old_description = old_description @new_description = new_description end diff --git a/lib/mongo/monitoring/event/topology_changed.rb b/lib/mongo/monitoring/event/topology_changed.rb index 17b50f2504..6a64c7cf97 100644 --- a/lib/mongo/monitoring/event/topology_changed.rb +++ b/lib/mongo/monitoring/event/topology_changed.rb @@ -21,8 +21,8 @@ module Event # @since 2.3.0 class TopologyChanged - # @return [ Integer ] cluster_id The cluster id. - attr_reader :cluster_id + # @return [ Topology ] topology The topology. + attr_reader :topology # @return [ Cluster::Topology ] old_topology The old topology. attr_reader :old_topology @@ -33,15 +33,15 @@ class TopologyChanged # Create the event. # # @example Create the event. - # TopologyChanged.new(1, old, new) + # TopologyChanged.new(topology, old, new) # - # @param [ Integer ] cluster_id The cluster id. + # @param [ Integer ] topology The topology. # @param [ Cluster::Topology ] old_topology The old topology. # @param [ Cluster::Topology ] new_topology The new topology. # # @since 2.3.0 - def initialize(cluster_id, old_topology, new_topology) - @cluster_id = cluster_id + def initialize(topology, old_topology, new_topology) + @topology = topology @old_topology = old_topology @new_topology = new_topology end diff --git a/lib/mongo/monitoring/event/topology_closed.rb b/lib/mongo/monitoring/event/topology_closed.rb index 81b36ddc91..4c5970af8e 100644 --- a/lib/mongo/monitoring/event/topology_closed.rb +++ b/lib/mongo/monitoring/event/topology_closed.rb @@ -21,19 +21,19 @@ module Event # @since 2.3.0 class TopologyClosed - # @return [ Integer ] cluster_id The cluster id. - attr_reader :cluster_id + # @return [ Topology ] topology The topology. + attr_reader :topology # Create the event. # # @example Create the event. - # TopologyClosed.new(1) + # TopologyClosed.new(topology) # - # @param [ Integer ] cluster_id The cluster id. + # @param [ Integer ] topology The topology. # # @since 2.3.0 - def initialize(cluster_id) - @cluster_id = cluster_id + def initialize(topology) + @topology = topology end end end diff --git a/lib/mongo/monitoring/event/topology_opening.rb b/lib/mongo/monitoring/event/topology_opening.rb index 2f60058bc1..f17a541565 100644 --- a/lib/mongo/monitoring/event/topology_opening.rb +++ b/lib/mongo/monitoring/event/topology_opening.rb @@ -21,19 +21,19 @@ module Event # @since 2.3.0 class TopologyOpening - # @return [ Integer ] cluster_id The cluster id. - attr_reader :cluster_id + # @return [ Topology ] topology The topology. + attr_reader :topology # Create the event. # # @example Create the event. - # TopologyOpening.new(1) + # TopologyOpening.new(topology) # - # @param [ Integer ] cluster_id The cluster id. + # @param [ Integer ] topology The topology. # # @since 2.3.0 - def initialize(cluster_id) - @cluster_id = cluster_id + def initialize(topology) + @topology = topology end end end diff --git a/spec/mongo/cluster/topology/replica_set_spec.rb b/spec/mongo/cluster/topology/replica_set_spec.rb index 406a83bbec..46a20c5291 100644 --- a/spec/mongo/cluster/topology/replica_set_spec.rb +++ b/spec/mongo/cluster/topology/replica_set_spec.rb @@ -58,7 +58,7 @@ context 'when no replica set name is provided' do let(:topology) do - described_class.new({}) + described_class.new({}, monitoring, []) end let(:servers) do @@ -73,7 +73,7 @@ context 'when a replica set name is provided' do let(:topology) do - described_class.new(:replica_set => 'testing') + described_class.new({ :replica_set => 'testing' }, monitoring) end let(:servers) do @@ -89,21 +89,21 @@ describe '.replica_set?' do it 'returns true' do - expect(described_class.new({})).to be_replica_set + expect(described_class.new({}, monitoring)).to be_replica_set end end describe '.sharded?' do it 'returns false' do - expect(described_class.new({})).to_not be_sharded + expect(described_class.new({}, monitoring)).to_not be_sharded end end describe '.single?' do it 'returns false' do - expect(described_class.new({})).to_not be_single + expect(described_class.new({}, monitoring)).to_not be_single end end @@ -127,7 +127,7 @@ end let(:topology) do - described_class.new(:replica_set => 'testing') + described_class.new({ :replica_set => 'testing' }, monitoring) end before do @@ -201,7 +201,7 @@ end let(:topology) do - described_class.new(:replica_set => 'testing') + described_class.new({ :replica_set => 'testing' }, monitoring) end before do @@ -283,7 +283,7 @@ end let(:topology) do - described_class.new(:replica_set => 'testing') + described_class.new({ :replica_set => 'testing' }, monitoring) end before do diff --git a/spec/mongo/cluster/topology/sharded_spec.rb b/spec/mongo/cluster/topology/sharded_spec.rb index bec1e2df13..ff318bb48b 100644 --- a/spec/mongo/cluster/topology/sharded_spec.rb +++ b/spec/mongo/cluster/topology/sharded_spec.rb @@ -7,7 +7,7 @@ end let(:topology) do - described_class.new({}) + described_class.new({}, monitoring) end let(:monitoring) do diff --git a/spec/mongo/cluster/topology/single_spec.rb b/spec/mongo/cluster/topology/single_spec.rb index 3bd6bb407b..c22f74106e 100644 --- a/spec/mongo/cluster/topology/single_spec.rb +++ b/spec/mongo/cluster/topology/single_spec.rb @@ -6,14 +6,14 @@ Mongo::Address.new('127.0.0.1:27017') end - let(:topology) do - described_class.new({}) - end - let(:monitoring) do Mongo::Monitoring.new end + let(:topology) do + described_class.new({}, monitoring) + end + let(:listeners) do Mongo::Event::Listeners.new end diff --git a/spec/mongo/cluster/topology/unknown_spec.rb b/spec/mongo/cluster/topology/unknown_spec.rb index 58c2845b1b..b95d3aca0a 100644 --- a/spec/mongo/cluster/topology/unknown_spec.rb +++ b/spec/mongo/cluster/topology/unknown_spec.rb @@ -2,8 +2,12 @@ describe Mongo::Cluster::Topology::Unknown do + let(:monitoring) do + Mongo::Monitoring.new + end + let(:topology) do - described_class.new({}) + described_class.new({}, monitoring) end describe '.servers' do diff --git a/spec/mongo/cluster/topology_spec.rb b/spec/mongo/cluster/topology_spec.rb index 6f3079b898..9054c17419 100644 --- a/spec/mongo/cluster/topology_spec.rb +++ b/spec/mongo/cluster/topology_spec.rb @@ -2,12 +2,16 @@ describe Mongo::Cluster::Topology do + let(:monitoring) do + Mongo::Monitoring.new + end + describe '.initial' do context 'when provided a replica set option' do let(:topology) do - described_class.initial([ 'a' ], connect: :replica_set) + described_class.initial([ 'a' ], monitoring, connect: :replica_set) end it 'returns a replica set topology' do @@ -18,7 +22,7 @@ context 'when provided a single option' do let(:topology) do - described_class.initial([ 'a' ], connect: :direct) + described_class.initial([ 'a' ], monitoring, connect: :direct) end it 'returns a single topology' do @@ -33,7 +37,7 @@ context 'when provided a sharded option' do let(:topology) do - described_class.initial([ 'a' ], connect: :sharded) + described_class.initial([ 'a' ], monitoring, connect: :sharded) end it 'returns a sharded topology' do @@ -46,7 +50,7 @@ context 'when a set name is in the options' do let(:topology) do - described_class.initial([], replica_set: 'testing') + described_class.initial([], monitoring, replica_set: 'testing') end it 'returns a replica set topology' do @@ -57,7 +61,7 @@ context 'when no set name is in the options' do let(:topology) do - described_class.initial([], {}) + described_class.initial([], monitoring, {}) end it 'returns an unknown topology' do @@ -68,7 +72,7 @@ context 'when provided a single mongos', if: single_mongos? do let(:topology) do - described_class.initial(ADDRESSES, TEST_OPTIONS) + described_class.initial(ADDRESSES, monitoring, TEST_OPTIONS) end it 'returns a sharded topology' do @@ -79,7 +83,7 @@ context 'when provided a single replica set member', if: single_rs_member? do let(:topology) do - described_class.initial(ADDRESSES, TEST_OPTIONS) + described_class.initial(ADDRESSES, monitoring, TEST_OPTIONS) end it 'returns a single topology' do diff --git a/spec/mongo/cluster_spec.rb b/spec/mongo/cluster_spec.rb index 3c5f3a2858..bd4e4738e8 100644 --- a/spec/mongo/cluster_spec.rb +++ b/spec/mongo/cluster_spec.rb @@ -156,7 +156,7 @@ context 'when topology is Single' do let(:topology) do - Mongo::Cluster::Topology::Single.new({}) + Mongo::Cluster::Topology::Single.new({}, monitoring) end it 'returns an empty array' do @@ -167,7 +167,7 @@ context 'when topology is ReplicaSet' do let(:topology) do - Mongo::Cluster::Topology::ReplicaSet.new({}) + Mongo::Cluster::Topology::ReplicaSet.new({}, monitoring) end it 'returns an empty array' do @@ -178,7 +178,7 @@ context 'when topology is Sharded' do let(:topology) do - Mongo::Cluster::Topology::Sharded.new({}) + Mongo::Cluster::Topology::Sharded.new({}, monitoring) end it 'returns an empty array' do @@ -189,7 +189,7 @@ context 'when topology is Unknown' do let(:topology) do - Mongo::Cluster::Topology::Unknown.new({}) + Mongo::Cluster::Topology::Unknown.new({}, monitoring) end it 'returns an empty array' do diff --git a/spec/mongo/server_selection_spec.rb b/spec/mongo/server_selection_spec.rb index 24ef6fb2d2..90a15ebf85 100644 --- a/spec/mongo/server_selection_spec.rb +++ b/spec/mongo/server_selection_spec.rb @@ -10,14 +10,14 @@ context(spec.description) do - let(:topology) do - spec.type.new({}) - end - let(:monitoring) do Mongo::Monitoring.new end + let(:topology) do + spec.type.new({}, monitoring) + end + let(:listeners) do Mongo::Event::Listeners.new end From 63f75cbf10282bd19d02cd6d3230a24e3331201a Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Sun, 24 Jan 2016 20:31:58 +0100 Subject: [PATCH 05/23] Fire topology changed events --- lib/mongo/cluster/topology/unknown.rb | 23 ++++++++++++++++--- .../monitoring/event/topology_changed.rb | 9 ++------ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/lib/mongo/cluster/topology/unknown.rb b/lib/mongo/cluster/topology/unknown.rb index 3de3dce61a..bb6d98ebe8 100644 --- a/lib/mongo/cluster/topology/unknown.rb +++ b/lib/mongo/cluster/topology/unknown.rb @@ -58,8 +58,11 @@ def display_name # @return [ Sharded, ReplicaSet ] The new topology. def elect_primary(description, servers) if description.mongos? + # @todo: Remove. log_debug("Mongos #{description.address.to_s} discovered.") - Sharded.new(options, monitoring) + sharded = Sharded.new(options, monitoring) + topology_changed(sharded) + sharded else initialize_replica_set(description, servers) end @@ -201,7 +204,9 @@ def remove_server?(description, server) # @since 2.0.6 def standalone_discovered if @seeds.size == 1 - Single.new(options, monitoring, @seeds) + single = Single.new(options, monitoring, @seeds) + topology_changed(single) + single else self end @@ -210,6 +215,7 @@ def standalone_discovered private def initialize_replica_set(description, servers) + # @todo: Remove. log_debug( "Server #{description.address.to_s} discovered as primary in replica set: " + "'#{description.replica_set_name}'. Changing topology to replica set." @@ -219,7 +225,18 @@ def initialize_replica_set(description, servers) server.description.unknown! end end - ReplicaSet.new(options.merge(:replica_set => description.replica_set_name), monitoring) + replica_set = ReplicaSet.new(options.merge(:replica_set => description.replica_set_name), monitoring) + topology_changed(replica_set) + replica_set + end + + def topology_changed(new_topology) + unless options[:monitoring] == false + monitoring.succeeded( + Monitoring::TOPOLOGY_CHANGED, + Monitoring::Event::TopologyChanged.new(self, new_topology) + ) + end end end end diff --git a/lib/mongo/monitoring/event/topology_changed.rb b/lib/mongo/monitoring/event/topology_changed.rb index 6a64c7cf97..71c1fbd7c2 100644 --- a/lib/mongo/monitoring/event/topology_changed.rb +++ b/lib/mongo/monitoring/event/topology_changed.rb @@ -21,9 +21,6 @@ module Event # @since 2.3.0 class TopologyChanged - # @return [ Topology ] topology The topology. - attr_reader :topology - # @return [ Cluster::Topology ] old_topology The old topology. attr_reader :old_topology @@ -33,15 +30,13 @@ class TopologyChanged # Create the event. # # @example Create the event. - # TopologyChanged.new(topology, old, new) + # TopologyChanged.new(old, new) # - # @param [ Integer ] topology The topology. # @param [ Cluster::Topology ] old_topology The old topology. # @param [ Cluster::Topology ] new_topology The new topology. # # @since 2.3.0 - def initialize(topology, old_topology, new_topology) - @topology = topology + def initialize(old_topology, new_topology) @old_topology = old_topology @new_topology = new_topology end From 9a04dfc03e8503c2a734333ab98140840ecc6754 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Mon, 25 Jan 2016 16:13:09 +0100 Subject: [PATCH 06/23] Adding the log subscribers --- lib/mongo/monitoring.rb | 5 ++ lib/mongo/monitoring/sdam_log_subscriber.rb | 54 +++++++++++++++++++ .../topology_changed_log_subscriber.rb | 33 ++++++++++++ .../topology_opening_log_subscriber.rb | 30 +++++++++++ 4 files changed, 122 insertions(+) create mode 100644 lib/mongo/monitoring/sdam_log_subscriber.rb create mode 100644 lib/mongo/monitoring/topology_changed_log_subscriber.rb create mode 100644 lib/mongo/monitoring/topology_opening_log_subscriber.rb diff --git a/lib/mongo/monitoring.rb b/lib/mongo/monitoring.rb index c39bfe6032..c860d8bbff 100644 --- a/lib/mongo/monitoring.rb +++ b/lib/mongo/monitoring.rb @@ -15,6 +15,9 @@ require 'mongo/monitoring/event' require 'mongo/monitoring/publishable' require 'mongo/monitoring/command_log_subscriber' +require 'mongo/monitoring/sdam_log_subscriber' +require 'mongo/monitoring/topology_changed_log_subscriber' +require 'mongo/monitoring/topology_opening_log_subscriber' module Mongo @@ -121,6 +124,8 @@ def initialize(options = {}) end end subscribe(COMMAND, CommandLogSubscriber.new(options)) + subscribe(TOPOLOGY_OPENING, TopologyOpeningLogSubscriber.new(options)) + subscribe(TOPOLOGY_CHANGED, TopologyChangedLogSubscriber.new(options)) end end diff --git a/lib/mongo/monitoring/sdam_log_subscriber.rb b/lib/mongo/monitoring/sdam_log_subscriber.rb new file mode 100644 index 0000000000..494ed35adc --- /dev/null +++ b/lib/mongo/monitoring/sdam_log_subscriber.rb @@ -0,0 +1,54 @@ +# Copyright (C) 2016 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module Mongo + class Monitoring + + # Subscribes to SDAM events and logs them. + # + # @since 2.3.0 + class SDAMLogSubscriber + include Loggable + + # @return [ Hash ] options The options. + attr_reader :options + + # Create the new log subscriber. + # + # @example Create the log subscriber. + # SDAMLogSubscriber.new + # + # @param [ Hash ] options The options. + # + # @option options [ Logger ] :logger An optional custom logger. + # + # @since 2.1.0 + def initialize(options = {}) + @options = options + end + + # Handle the SDAM succeeded event. + # + # @example Handle the event. + # subscriber.succeeded(event) + # + # @param [ Event ] event The event. + # + # @since 2.3.0 + def succeeded(event) + log_event(event) if logger.debug? + end + end + end +end diff --git a/lib/mongo/monitoring/topology_changed_log_subscriber.rb b/lib/mongo/monitoring/topology_changed_log_subscriber.rb new file mode 100644 index 0000000000..25942de3c1 --- /dev/null +++ b/lib/mongo/monitoring/topology_changed_log_subscriber.rb @@ -0,0 +1,33 @@ +# Copyright (C) 2016 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module Mongo + class Monitoring + + # Subscribes to Topology Changed events and logs them. + # + # @since 2.3.0 + class TopologyChangedLogSubscriber < SDAMLogSubscriber + + private + + def log_event(event) + log_debug( + "Topology type '#{event.old_topology.display_name}' changed to " + + "type '#{event.new_topology.display_name}'." + ) + end + end + end +end diff --git a/lib/mongo/monitoring/topology_opening_log_subscriber.rb b/lib/mongo/monitoring/topology_opening_log_subscriber.rb new file mode 100644 index 0000000000..500bd9ce2c --- /dev/null +++ b/lib/mongo/monitoring/topology_opening_log_subscriber.rb @@ -0,0 +1,30 @@ +# Copyright (C) 2016 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module Mongo + class Monitoring + + # Subscribes to Topology Openeing events and logs them. + # + # @since 2.3.0 + class TopologyOpeningLogSubscriber < SDAMLogSubscriber + + private + + def log_event(event) + log_debug("Topology type '#{event.topology.display_name}' initializing.") + end + end + end +end From bf934f8ae7dd54d5f171a56c8d919418287d73b1 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Thu, 4 Feb 2016 17:18:38 +0100 Subject: [PATCH 07/23] SPEC-222: Implementing more SDAM events --- lib/mongo/monitoring.rb | 4 +- lib/mongo/monitoring/event.rb | 2 + lib/mongo/monitoring/event/server_closed.rb | 46 +++++++++++++++++++ .../event/server_description_changed.rb | 13 +++--- lib/mongo/monitoring/event/server_opening.rb | 46 +++++++++++++++++++ .../monitoring/event/topology_changed.rb | 12 ++--- .../monitoring/event/topology_opening.rb | 1 - 7 files changed, 108 insertions(+), 16 deletions(-) create mode 100644 lib/mongo/monitoring/event/server_closed.rb create mode 100644 lib/mongo/monitoring/event/server_opening.rb diff --git a/lib/mongo/monitoring.rb b/lib/mongo/monitoring.rb index c860d8bbff..cf67ccac62 100644 --- a/lib/mongo/monitoring.rb +++ b/lib/mongo/monitoring.rb @@ -124,8 +124,8 @@ def initialize(options = {}) end end subscribe(COMMAND, CommandLogSubscriber.new(options)) - subscribe(TOPOLOGY_OPENING, TopologyOpeningLogSubscriber.new(options)) - subscribe(TOPOLOGY_CHANGED, TopologyChangedLogSubscriber.new(options)) + # subscribe(TOPOLOGY_OPENING, TopologyOpeningLogSubscriber.new(options)) + # subscribe(TOPOLOGY_CHANGED, TopologyChangedLogSubscriber.new(options)) end end diff --git a/lib/mongo/monitoring/event.rb b/lib/mongo/monitoring/event.rb index afa22badba..d9bc8ad979 100644 --- a/lib/mongo/monitoring/event.rb +++ b/lib/mongo/monitoring/event.rb @@ -16,7 +16,9 @@ require 'mongo/monitoring/event/command_started' require 'mongo/monitoring/event/command_succeeded' require 'mongo/monitoring/event/command_failed' +require 'mongo/monitoring/event/server_closed' require 'mongo/monitoring/event/server_description_changed' +require 'mongo/monitoring/event/server_opening' require 'mongo/monitoring/event/topology_changed' require 'mongo/monitoring/event/topology_closed' require 'mongo/monitoring/event/topology_opening' diff --git a/lib/mongo/monitoring/event/server_closed.rb b/lib/mongo/monitoring/event/server_closed.rb new file mode 100644 index 0000000000..6313bec99a --- /dev/null +++ b/lib/mongo/monitoring/event/server_closed.rb @@ -0,0 +1,46 @@ +# Copyright (C) 2016 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module Mongo + class Monitoring + module Event + + # Event fired when the server is closed. + # + # @since 2.3.0 + class ServerClosed + + # @return [ Address ] address The server address. + attr_reader :address + + # @return [ Topology ] topology The topology. + attr_reader :topology + + # Create the event. + # + # @example Create the event. + # ServerClosed.new(address) + # + # @param [ Address ] address The server address. + # @param [ Integer ] topology The topology. + # + # @since 2.3.0 + def initialize(address, topology) + @address = address + @topology = topology + end + end + end + end +end diff --git a/lib/mongo/monitoring/event/server_description_changed.rb b/lib/mongo/monitoring/event/server_description_changed.rb index e0d75a0f55..99f4945c2d 100644 --- a/lib/mongo/monitoring/event/server_description_changed.rb +++ b/lib/mongo/monitoring/event/server_description_changed.rb @@ -27,9 +27,9 @@ class ServerDescriptionChanged # @return [ Topology ] topology The topology. attr_reader :topology - # @return [ Server::Description ] old_description The old server + # @return [ Server::Description ] previous_description The previous server # description. - attr_reader :old_description + attr_reader :previous_description # @return [ Server::Description ] new_description The new server # description. @@ -38,22 +38,21 @@ class ServerDescriptionChanged # Create the event. # # @example Create the event. - # ServerDescriptionChanged.new(address, topology, old, new) + # ServerDescriptionChanged.new(address, topology, previous, new) # # @param [ Address ] address The server address. # @param [ Integer ] topology The topology. - # @param [ Server::Description ] old_description The old description. + # @param [ Server::Description ] previous_description The previous description. # @param [ Server::Description ] new_description The new description. # # @since 2.3.0 - def initialize(address, topology, old_description, new_description) + def initialize(address, topology, previous_description, new_description) @address = address @topology = topology - @old_description = old_description + @previous_description = previous_description @new_description = new_description end end end end end - diff --git a/lib/mongo/monitoring/event/server_opening.rb b/lib/mongo/monitoring/event/server_opening.rb new file mode 100644 index 0000000000..721f139d82 --- /dev/null +++ b/lib/mongo/monitoring/event/server_opening.rb @@ -0,0 +1,46 @@ +# Copyright (C) 2016 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module Mongo + class Monitoring + module Event + + # Event fired when the server is opening. + # + # @since 2.3.0 + class ServerOpening + + # @return [ Address ] address The server address. + attr_reader :address + + # @return [ Topology ] topology The topology. + attr_reader :topology + + # Create the event. + # + # @example Create the event. + # ServerOpening.new(address) + # + # @param [ Address ] address The server address. + # @param [ Integer ] topology The topology. + # + # @since 2.3.0 + def initialize(address, topology) + @address = address + @topology = topology + end + end + end + end +end diff --git a/lib/mongo/monitoring/event/topology_changed.rb b/lib/mongo/monitoring/event/topology_changed.rb index 71c1fbd7c2..e415718db7 100644 --- a/lib/mongo/monitoring/event/topology_changed.rb +++ b/lib/mongo/monitoring/event/topology_changed.rb @@ -21,8 +21,8 @@ module Event # @since 2.3.0 class TopologyChanged - # @return [ Cluster::Topology ] old_topology The old topology. - attr_reader :old_topology + # @return [ Cluster::Topology ] previous_topology The previous topology. + attr_reader :previous_topology # @return [ Cluster::Topology ] new_topology The new topology. attr_reader :new_topology @@ -30,14 +30,14 @@ class TopologyChanged # Create the event. # # @example Create the event. - # TopologyChanged.new(old, new) + # TopologyChanged.new(previous, new) # - # @param [ Cluster::Topology ] old_topology The old topology. + # @param [ Cluster::Topology ] previous_topology The previous topology. # @param [ Cluster::Topology ] new_topology The new topology. # # @since 2.3.0 - def initialize(old_topology, new_topology) - @old_topology = old_topology + def initialize(previous_topology, new_topology) + @previous_topology = previous_topology @new_topology = new_topology end end diff --git a/lib/mongo/monitoring/event/topology_opening.rb b/lib/mongo/monitoring/event/topology_opening.rb index f17a541565..643bd70a54 100644 --- a/lib/mongo/monitoring/event/topology_opening.rb +++ b/lib/mongo/monitoring/event/topology_opening.rb @@ -39,4 +39,3 @@ def initialize(topology) end end end - From 111c46cb8608eeb80339a5b53089d2e9a5e401a8 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Mon, 8 Feb 2016 14:22:46 -0500 Subject: [PATCH 08/23] SPEC-222: Implementing test runner --- lib/mongo/monitoring.rb | 12 ++- spec/mongo/sdam_monitoring_spec.rb | 90 +++++++++++++++++++ spec/spec_helper.rb | 1 + spec/support/sdam_monitoring.rb | 5 ++ .../replica_set_with_removal.yml | 88 ++++++++++++++++++ spec/support/sdam_monitoring/standalone.yml | 74 +++++++++++++++ .../server_discovery_and_monitoring.rb | 35 +++++++- 7 files changed, 303 insertions(+), 2 deletions(-) create mode 100644 spec/mongo/sdam_monitoring_spec.rb create mode 100644 spec/support/sdam_monitoring.rb create mode 100644 spec/support/sdam_monitoring/replica_set_with_removal.yml create mode 100644 spec/support/sdam_monitoring/standalone.yml diff --git a/lib/mongo/monitoring.rb b/lib/mongo/monitoring.rb index cf67ccac62..9185194d02 100644 --- a/lib/mongo/monitoring.rb +++ b/lib/mongo/monitoring.rb @@ -31,10 +31,20 @@ class Monitoring # @since 2.1.0 COMMAND = 'Command'.freeze + # Server closed topic. + # + # @since 2.3.0 + SERVER_CLOSED = 'ServerClosed'.freeze + # Server description changed topic. # # @since 2.3.0 - SERVER_DESCRIPTION_CHANGED = 'ServerDescriptionChnaged'.freeze + SERVER_DESCRIPTION_CHANGED = 'ServerDescriptionChanged'.freeze + + # Server opening topic. + # + # @since 2.3.0 + SERVER_OPENING = 'ServerOpening'.freeze # Topology changed topic. # diff --git a/spec/mongo/sdam_monitoring_spec.rb b/spec/mongo/sdam_monitoring_spec.rb new file mode 100644 index 0000000000..d9b2aaaade --- /dev/null +++ b/spec/mongo/sdam_monitoring_spec.rb @@ -0,0 +1,90 @@ +require 'spec_helper' + +describe 'SDAM Monitoring' do + include Mongo::SDAM + + SDAM_MONITORING_TESTS.each do |file| + + spec = Mongo::SDAM::Spec.new(file) + + context(spec.description) do + + before(:all) do + + module Mongo + # We monkey-patch the server here, so the monitors do not run and no + # real TCP connection is attempted. Thus we can control the server + # descriptions per-phase. + # + # @since 2.0.0 + class Server + + alias :original_initialize :initialize + def initialize(address, cluster, monitoring, event_listeners, options = {}) + @address = address + @cluster = cluster + @monitoring = monitoring + @options = options.freeze + @monitor = Monitor.new(address, event_listeners, options) + end + + alias :original_disconnect! :disconnect! + def disconnect!; true; end + end + end + + # Client is set as an instance variable inside the scope of the spec to + # retain its modifications across contexts/phases. Let is no good + # here as we have a clean slate for each context/phase. + @client = Mongo::Client.new(spec.uri_string) + end + + after(:all) do + @client.close + + # Return the server implementation to its original for the other + # tests in the suite. + module Mongo + class Server + alias :initialize :original_initialize + remove_method(:original_initialize) + + alias :disconnect! :original_disconnect! + remove_method(:original_disconnect!) + end + end + end + + spec.phases.each_with_index do |phase, index| + + context("Phase: #{index + 1}") do + + phase.responses.each do |response| + + before do + # For each response in the phase, we need to change that server's + # description. + server = find_server(@client, response.address) + server = Mongo::Server.new( + Mongo::Address.new(response.address), + @client.cluster, + @client.instance_variable_get(:@monitoring), + @client.cluster.send(:event_listeners), + @client.cluster.options + ) unless server + monitor = server.instance_variable_get(:@monitor) + description = monitor.inspector.run(server.description, response.ismaster, 0.5) + monitor.instance_variable_set(:@description, description) + end + end + + phase.outcome.events.each do |event| + + it "expects a #{event.name} to be fired" do + end + end + end + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 8f6dffdcc1..21f496b492 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,6 +2,7 @@ COVERAGE_MIN = 90 CURRENT_PATH = File.expand_path(File.dirname(__FILE__)) SERVER_DISCOVERY_TESTS = Dir.glob("#{CURRENT_PATH}/support/sdam/**/*.yml") +SDAM_MONITORING_TESTS = Dir.glob("#{CURRENT_PATH}/support/sdam_monitoring/**/*.yml") SERVER_SELECTION_RTT_TESTS = Dir.glob("#{CURRENT_PATH}/support/server_selection/rtt/*.yml") SERVER_SELECTION_TESTS = Dir.glob("#{CURRENT_PATH}/support/server_selection/selection/**/*.yml") CRUD_TESTS = Dir.glob("#{CURRENT_PATH}/support/crud_tests/**/*.yml") diff --git a/spec/support/sdam_monitoring.rb b/spec/support/sdam_monitoring.rb new file mode 100644 index 0000000000..75c799ab7b --- /dev/null +++ b/spec/support/sdam_monitoring.rb @@ -0,0 +1,5 @@ +module Mongo + module SDAMMonitoring + + end +end diff --git a/spec/support/sdam_monitoring/replica_set_with_removal.yml b/spec/support/sdam_monitoring/replica_set_with_removal.yml new file mode 100644 index 0000000000..85888fe253 --- /dev/null +++ b/spec/support/sdam_monitoring/replica_set_with_removal.yml @@ -0,0 +1,88 @@ +description: "Monitoring a replica set with non member" +uri: "mongodb://a,b/?replicaSet=rs" +phases: + - + responses: + "a:27017": + ok: 1 + ismaster: true + setName: "rs" + setVersion: 1.0 + hosts: [ "a:27017" ] + minWireVersion: 0 + maxWireVersion: 4 + "b:27017": + ok: 1 + ismaster: true + outcome: + events: + - + topology_opening_event: + topologyId: "42" + - + server_opening_event: + topologyId: "42" + address: "a:27017" + - + server_opening_event: + topologyId: "42" + address: "b:27017" + - + server_description_changed_event: + topologyId: "42" + address: "a:27017" + previousDescription: + address: "127.0.0.1:27017" + arbiters: [] + electionId: null + error: null + hosts: [] + maxWireVersion: 0 + me: null + minWireVersion: 0 + passives: [] + primary: null + setName: null + setVersion: null + tags: [] + type: "Unknown" + newDescription: + address: "a:27017" + arbiters: [] + electionId: null + error: null + hosts: [ "a:27017" ] + maxWireVersion: 4 + me: "a:27017" + minWireVersion: 0 + passives: [] + primary: true + setName: "rs" + setVersion: 1.0 + tags: [] + type: "RSPrimary" + - + server_closed_event: + topologyId: "42" + address: "b:27017" + - + topology_description_changed_event: + topologyId: "42" + previousDescription: + type: "Unknown" + setName: null + maxSetVersion: null + maxElectionId: null + servers: [ "127.0.0.1:27017" ] + stale: false + compatible: true + compatibilityError: null + newDescription: + topologyType: "ReplicaSetWithPrimary" + setName: "rs" + maxSetVersion: 1.0 + maxElectionId: null + servers: [ "a:27017" ] + stale: false + compatible: true + compatibilityError: null diff --git a/spec/support/sdam_monitoring/standalone.yml b/spec/support/sdam_monitoring/standalone.yml new file mode 100644 index 0000000000..3cc861d948 --- /dev/null +++ b/spec/support/sdam_monitoring/standalone.yml @@ -0,0 +1,74 @@ +description: "Monitoring a standalone connection" +uri: "mongodb://a:27017" +phases: + - + responses: + "a:27017": + ok: 1 + ismaster: true + minWireVersion: 0 + maxWireVersion: 4 + outcome: + events: + - + topology_opening_event: + topologyId: "42" + - + server_opening_event: + topologyId: "42" + address: "a:27017" + - + server_description_changed_event: + topologyId: "42" + address: "a:27017" + previousDescription: + address: "127.0.0.1:27017" + arbiters: [] + electionId: null + error: null + hosts: [] + maxWireVersion: 0 + me: null + minWireVersion: 0 + passives: [] + primary: null + setName: null + setVersion: null + tags: [] + type: "Unknown" + newDescription: + address: "a:27017" + arbiters: [] + electionId: null + error: null + hosts: [] + maxWireVersion: 4 + me: null + minWireVersion: 0 + passives: [] + primary: null + setName: null + setVersion: null + tags: [] + type: "Standalone" + - + topology_description_changed_event: + topologyId: "42" + previousDescription: + type: "Unknown" + setName: null + maxSetVersion: null + maxElectionId: null + servers: [ "127.0.0.1:27017" ] + stale: false + compatible: true + compatibilityError: null + newDescription: + topologyType: "Single" + setName: null + maxSetVersion: null + maxElectionId: null + servers: [ "a:27017" ] + stale: false + compatible: true + compatibilityError: null diff --git a/spec/support/server_discovery_and_monitoring.rb b/spec/support/server_discovery_and_monitoring.rb index d4c84ae159..c2c75ebc68 100644 --- a/spec/support/server_discovery_and_monitoring.rb +++ b/spec/support/server_discovery_and_monitoring.rb @@ -150,6 +150,9 @@ def initialize(response, uri) # @since 2.0.0 class Outcome + # @return [ Array ] events The expected events. + attr_reader :events + # @return [ Hash ] servers The expecations for # server states. attr_reader :servers @@ -169,18 +172,48 @@ class Outcome # # @since 2.0.0 def initialize(outcome) - @servers = process_servers(outcome['servers']) + @servers = process_servers(outcome['servers']) if outcome['servers'] @set_name = outcome['setName'] @topology_type = outcome['topologyType'] + @events = process_events(outcome['events']) if outcome['events'] end private + def process_events(events) + events.map do |event| + Event.new(event.keys.first, event.values.first) + end + end + def process_servers(servers) servers.each do |s| SDAM::convert_election_ids([ s[1] ]) end end end + + class Event + + MAPPINGS = { + 'server_closed_event' => Mongo::Monitoring::Event::ServerClosed, + 'server_description_changed_event' => Mongo::Monitoring::Event::ServerDescriptionChanged, + 'server_opening_event' => Mongo::Monitoring::Event::ServerOpening, + 'topology_description_changed_event' => Mongo::Monitoring::Event::TopologyChanged, + 'topology_opening_event' => Mongo::Monitoring::Event::TopologyOpening + } + + attr_reader :name + attr_reader :data + + def initialize(name, data) + @name = name + @data = data + end + + def expected + MAPPINGS.fetch(name) + end + end end end From 614eb4c94ddb5679b997312d199dbac886b37553 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Tue, 9 Feb 2016 09:37:22 -0500 Subject: [PATCH 09/23] SPEC-222: Publishing description changed events --- lib/mongo/cluster.rb | 4 ++- lib/mongo/cluster/topology.rb | 9 ------ lib/mongo/cluster/topology/replica_set.rb | 5 ++++ lib/mongo/cluster/topology/sharded.rb | 5 ++++ lib/mongo/cluster/topology/single.rb | 8 +++++ lib/mongo/cluster/topology/unknown.rb | 5 ++++ lib/mongo/event/description_changed.rb | 20 ++++++++++++- lib/mongo/monitoring.rb | 8 +++-- lib/mongo/monitoring/publishable.rb | 12 ++++++++ ...rver_description_changed_log_subscriber.rb | 30 +++++++++++++++++++ .../server_opening_log_subscriber.rb | 30 +++++++++++++++++++ .../topology_changed_log_subscriber.rb | 2 +- lib/mongo/server.rb | 5 ++++ .../inspector/description_changed.rb | 2 +- spec/spec_helper.rb | 2 +- 15 files changed, 131 insertions(+), 16 deletions(-) create mode 100644 lib/mongo/monitoring/server_description_changed_log_subscriber.rb create mode 100644 lib/mongo/monitoring/server_opening_log_subscriber.rb diff --git a/lib/mongo/cluster.rb b/lib/mongo/cluster.rb index 3fe26747a7..e0413c15f1 100644 --- a/lib/mongo/cluster.rb +++ b/lib/mongo/cluster.rb @@ -38,6 +38,9 @@ class Cluster # @return [ Hash ] The options hash. attr_reader :options + # @return [ Monitoring ] monitoring The monitoring. + attr_reader :monitoring + # @return [ Object ] The cluster topology. attr_reader :topology @@ -75,7 +78,6 @@ def add(host) address = Address.new(host) if !addresses.include?(address) if addition_allowed?(address) - log_debug("Adding #{address.to_s} to the cluster.") @update_lock.synchronize { @addresses.push(address) } server = Server.new(address, self, @monitoring, event_listeners, options) @update_lock.synchronize { @servers.push(server) } diff --git a/lib/mongo/cluster/topology.rb b/lib/mongo/cluster/topology.rb index 507a204f4b..efecedc547 100644 --- a/lib/mongo/cluster/topology.rb +++ b/lib/mongo/cluster/topology.rb @@ -55,17 +55,8 @@ def initial(seeds, monitoring, options) else Unknown.new(options, monitoring, seeds) end - publish(monitoring, topology) unless options[:monitoring] == false topology end - - private - - def publish(monitoring, topology) - monitoring.succeeded( - Monitoring::TOPOLOGY_OPENING, Monitoring::Event::TopologyOpening.new(topology) - ) - end end end end diff --git a/lib/mongo/cluster/topology/replica_set.rb b/lib/mongo/cluster/topology/replica_set.rb index 92cf4a4ca5..424913c556 100644 --- a/lib/mongo/cluster/topology/replica_set.rb +++ b/lib/mongo/cluster/topology/replica_set.rb @@ -21,6 +21,7 @@ module Topology # @since 2.0.0 class ReplicaSet include Loggable + include Monitoring::Publishable # Constant for the replica set name configuration option. # @@ -97,6 +98,10 @@ def initialize(options, monitoring, seeds = []) @monitoring = monitoring @max_election_id = nil @max_set_version = nil + publish_sdam_event( + Monitoring::TOPOLOGY_OPENING, + Monitoring::Event::TopologyOpening.new(self) + ) end # A replica set topology is a replica set. diff --git a/lib/mongo/cluster/topology/sharded.rb b/lib/mongo/cluster/topology/sharded.rb index 11cf348b04..0b5f346890 100644 --- a/lib/mongo/cluster/topology/sharded.rb +++ b/lib/mongo/cluster/topology/sharded.rb @@ -20,6 +20,7 @@ module Topology # # @since 2.0.0 class Sharded + include Monitoring::Publishable # The display name for the topology. # @@ -67,6 +68,10 @@ def elect_primary(description, servers); self; end def initialize(options, monitoring, seeds = []) @options = options @monitoring = monitoring + publish_sdam_event( + Monitoring::TOPOLOGY_OPENING, + Monitoring::Event::TopologyOpening.new(self) + ) end # A sharded topology is not a replica set. diff --git a/lib/mongo/cluster/topology/single.rb b/lib/mongo/cluster/topology/single.rb index 47af8f707d..48ce0cc30b 100644 --- a/lib/mongo/cluster/topology/single.rb +++ b/lib/mongo/cluster/topology/single.rb @@ -20,12 +20,16 @@ module Topology # # @since 2.0.0 class Single + include Monitoring::Publishable # The display name for the topology. # # @since 2.0.0 NAME = 'Single'.freeze + # @return [ Hash ] options The options. + attr_reader :options + # @return [ String ] seed The seed address. attr_reader :seed @@ -71,6 +75,10 @@ def initialize(options, monitoring, seeds = []) @options = options @monitoring = monitoring @seed = seeds.first + publish_sdam_event( + Monitoring::TOPOLOGY_OPENING, + Monitoring::Event::TopologyOpening.new(self) + ) end # A single topology is not a replica set. diff --git a/lib/mongo/cluster/topology/unknown.rb b/lib/mongo/cluster/topology/unknown.rb index bb6d98ebe8..d0491164e6 100644 --- a/lib/mongo/cluster/topology/unknown.rb +++ b/lib/mongo/cluster/topology/unknown.rb @@ -21,6 +21,7 @@ module Topology # @since 2.0.0 class Unknown include Loggable + include Monitoring::Publishable # The display name for the topology. # @@ -82,6 +83,10 @@ def initialize(options, monitoring, seeds = []) @options = options @monitoring = monitoring @seeds = seeds + publish_sdam_event( + Monitoring::TOPOLOGY_OPENING, + Monitoring::Event::TopologyOpening.new(self) + ) end # An unknown topology is not a replica set. diff --git a/lib/mongo/event/description_changed.rb b/lib/mongo/event/description_changed.rb index 2f5da4d708..b02be6fe52 100644 --- a/lib/mongo/event/description_changed.rb +++ b/lib/mongo/event/description_changed.rb @@ -20,10 +20,17 @@ module Event # # @since 2.0.6 class DescriptionChanged + include Monitoring::Publishable # @return [ Mongo::Cluster ] cluster The event publisher. attr_reader :cluster + # @return [ Hash ] options The options. + attr_reader :options + + # @return [ Monitoring ] monitoring The monitoring. + attr_reader :monitoring + # Initialize the new host added event handler. # # @example Create the new handler. @@ -34,6 +41,8 @@ class DescriptionChanged # @since 2.0.0 def initialize(cluster) @cluster = cluster + @options = cluster.options + @monitoring = cluster.monitoring end # This event publishes an event to add the cluster and logs the @@ -45,7 +54,16 @@ def initialize(cluster) # @param [ Server::Description ] updated The changed description. # # @since 2.0.0 - def handle(updated) + def handle(previous, updated) + publish_sdam_event( + Monitoring::SERVER_DESCRIPTION_CHANGED, + Monitoring::Event::ServerDescriptionChanged.new( + updated.address, + cluster.topology, + previous, + updated + ) + ) cluster.add_hosts(updated) cluster.remove_hosts(updated) end diff --git a/lib/mongo/monitoring.rb b/lib/mongo/monitoring.rb index 9185194d02..a6d8de9cbd 100644 --- a/lib/mongo/monitoring.rb +++ b/lib/mongo/monitoring.rb @@ -16,6 +16,8 @@ require 'mongo/monitoring/publishable' require 'mongo/monitoring/command_log_subscriber' require 'mongo/monitoring/sdam_log_subscriber' +require 'mongo/monitoring/server_description_changed_log_subscriber' +require 'mongo/monitoring/server_opening_log_subscriber' require 'mongo/monitoring/topology_changed_log_subscriber' require 'mongo/monitoring/topology_opening_log_subscriber' @@ -134,8 +136,10 @@ def initialize(options = {}) end end subscribe(COMMAND, CommandLogSubscriber.new(options)) - # subscribe(TOPOLOGY_OPENING, TopologyOpeningLogSubscriber.new(options)) - # subscribe(TOPOLOGY_CHANGED, TopologyChangedLogSubscriber.new(options)) + subscribe(SERVER_OPENING, ServerOpeningLogSubscriber.new(options)) + subscribe(SERVER_DESCRIPTION_CHANGED, ServerDescriptionChangedLogSubscriber.new(options)) + subscribe(TOPOLOGY_OPENING, TopologyOpeningLogSubscriber.new(options)) + subscribe(TOPOLOGY_CHANGED, TopologyChangedLogSubscriber.new(options)) end end diff --git a/lib/mongo/monitoring/publishable.rb b/lib/mongo/monitoring/publishable.rb index 54b8f4ad01..62fe0ebe6b 100644 --- a/lib/mongo/monitoring/publishable.rb +++ b/lib/mongo/monitoring/publishable.rb @@ -55,6 +55,14 @@ def publish_command(messages, operation_id = Monitoring.next_operation_id) end end + def publish_event(topic, event) + monitoring.succeeded(topic, event) + end + + def publish_sdam_event(topic, event) + monitoring.succeeded(topic, event) if monitoring? + end + private def command_started(address, operation_id, payload) @@ -101,6 +109,10 @@ def duration(start) def error?(document) document && (document['ok'] == 0 || document.key?('$err')) end + + def monitoring? + options[:monitoring] != false + end end end end diff --git a/lib/mongo/monitoring/server_description_changed_log_subscriber.rb b/lib/mongo/monitoring/server_description_changed_log_subscriber.rb new file mode 100644 index 0000000000..1d5e458fd5 --- /dev/null +++ b/lib/mongo/monitoring/server_description_changed_log_subscriber.rb @@ -0,0 +1,30 @@ +# Copyright (C) 2016 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module Mongo + class Monitoring + + # Subscribes to Server Description Changed events and logs them. + # + # @since 2.3.0 + class ServerDescriptionChangedLogSubscriber < SDAMLogSubscriber + + private + + def log_event(event) + log_debug("Server description for #{event.address} changed.") + end + end + end +end diff --git a/lib/mongo/monitoring/server_opening_log_subscriber.rb b/lib/mongo/monitoring/server_opening_log_subscriber.rb new file mode 100644 index 0000000000..9c4849da73 --- /dev/null +++ b/lib/mongo/monitoring/server_opening_log_subscriber.rb @@ -0,0 +1,30 @@ +# Copyright (C) 2016 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module Mongo + class Monitoring + + # Subscribes to Server Opening events and logs them. + # + # @since 2.3.0 + class ServerOpeningLogSubscriber < SDAMLogSubscriber + + private + + def log_event(event) + log_debug("Server #{event.address} initializing.") + end + end + end +end diff --git a/lib/mongo/monitoring/topology_changed_log_subscriber.rb b/lib/mongo/monitoring/topology_changed_log_subscriber.rb index 25942de3c1..900bc128c0 100644 --- a/lib/mongo/monitoring/topology_changed_log_subscriber.rb +++ b/lib/mongo/monitoring/topology_changed_log_subscriber.rb @@ -24,7 +24,7 @@ class TopologyChangedLogSubscriber < SDAMLogSubscriber def log_event(event) log_debug( - "Topology type '#{event.old_topology.display_name}' changed to " + + "Topology type '#{event.previous_topology.display_name}' changed to " + "type '#{event.new_topology.display_name}'." ) end diff --git a/lib/mongo/server.rb b/lib/mongo/server.rb index 6bda231fca..ae6cd61d63 100644 --- a/lib/mongo/server.rb +++ b/lib/mongo/server.rb @@ -27,6 +27,7 @@ module Mongo # @since 2.0.0 class Server extend Forwardable + include Monitoring::Publishable # @return [ String ] The configured address for the server. attr_reader :address @@ -155,6 +156,10 @@ def initialize(address, cluster, monitoring, event_listeners, options = {}) @cluster = cluster @monitoring = monitoring @options = options.freeze + publish_sdam_event( + Monitoring::SERVER_OPENING, + Monitoring::Event::ServerOpening.new(address, cluster.topology) + ) @monitor = Monitor.new(address, event_listeners, options) monitor.scan! monitor.run! diff --git a/lib/mongo/server/description/inspector/description_changed.rb b/lib/mongo/server/description/inspector/description_changed.rb index c843919cd6..4ac8649702 100644 --- a/lib/mongo/server/description/inspector/description_changed.rb +++ b/lib/mongo/server/description/inspector/description_changed.rb @@ -47,7 +47,7 @@ def initialize(event_listeners) # @since 2.0.0 def run(description, updated) unless description == updated - publish(Event::DESCRIPTION_CHANGED, updated) + publish(Event::DESCRIPTION_CHANGED, description, updated) end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 21f496b492..b0c06d76af 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -19,7 +19,7 @@ require 'mongo' Mongo::Logger.logger = Logger.new($stdout) -Mongo::Logger.logger.level = Logger::INFO +Mongo::Logger.logger.level = Logger::DEBUG require 'support/travis' require 'support/matchers' From b31e7ad241dea9e83484cd29137c5e48e28e0ddc Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Tue, 9 Feb 2016 10:10:46 -0500 Subject: [PATCH 10/23] SPEC-222: Monitoring server closed events --- lib/mongo/cluster.rb | 6 +- lib/mongo/cluster/topology/unknown.rb | 10 +-- lib/mongo/monitoring.rb | 2 + .../server_closed_log_subscriber.rb | 30 +++++++ ...rver_description_changed_log_subscriber.rb | 5 +- .../topology_changed_log_subscriber.rb | 4 +- .../topology_opening_log_subscriber.rb | 2 +- lib/mongo/server/description.rb | 18 ++++ spec/mongo/server/description_spec.rb | 84 +++++++++++++++++++ 9 files changed, 150 insertions(+), 11 deletions(-) create mode 100644 lib/mongo/monitoring/server_closed_log_subscriber.rb diff --git a/lib/mongo/cluster.rb b/lib/mongo/cluster.rb index e0413c15f1..27f0d62893 100644 --- a/lib/mongo/cluster.rb +++ b/lib/mongo/cluster.rb @@ -22,6 +22,7 @@ module Mongo # @since 2.0.0 class Cluster extend Forwardable + include Monitoring::Publishable include Event::Subscriber include Loggable @@ -244,11 +245,14 @@ def standalone_discovered # # @since 2.0.0 def remove(host) - log_debug("#{host} being removed from the cluster.") address = Address.new(host) removed_servers = @servers.select { |s| s.address == address } @update_lock.synchronize { @servers = @servers - removed_servers } removed_servers.each{ |server| server.disconnect! } if removed_servers + publish_sdam_event( + Monitoring::SERVER_CLOSED, + Monitoring::Event::ServerClosed.new(address, topology) + ) @update_lock.synchronize { @addresses.reject! { |addr| addr == address } } end diff --git a/lib/mongo/cluster/topology/unknown.rb b/lib/mongo/cluster/topology/unknown.rb index d0491164e6..08446429d6 100644 --- a/lib/mongo/cluster/topology/unknown.rb +++ b/lib/mongo/cluster/topology/unknown.rb @@ -236,12 +236,10 @@ def initialize_replica_set(description, servers) end def topology_changed(new_topology) - unless options[:monitoring] == false - monitoring.succeeded( - Monitoring::TOPOLOGY_CHANGED, - Monitoring::Event::TopologyChanged.new(self, new_topology) - ) - end + publish_sdam_event( + Monitoring::TOPOLOGY_CHANGED, + Monitoring::Event::TopologyChanged.new(self, new_topology) + ) end end end diff --git a/lib/mongo/monitoring.rb b/lib/mongo/monitoring.rb index a6d8de9cbd..5b81d28c6d 100644 --- a/lib/mongo/monitoring.rb +++ b/lib/mongo/monitoring.rb @@ -17,6 +17,7 @@ require 'mongo/monitoring/command_log_subscriber' require 'mongo/monitoring/sdam_log_subscriber' require 'mongo/monitoring/server_description_changed_log_subscriber' +require 'mongo/monitoring/server_closed_log_subscriber' require 'mongo/monitoring/server_opening_log_subscriber' require 'mongo/monitoring/topology_changed_log_subscriber' require 'mongo/monitoring/topology_opening_log_subscriber' @@ -137,6 +138,7 @@ def initialize(options = {}) end subscribe(COMMAND, CommandLogSubscriber.new(options)) subscribe(SERVER_OPENING, ServerOpeningLogSubscriber.new(options)) + subscribe(SERVER_CLOSED, ServerClosedLogSubscriber.new(options)) subscribe(SERVER_DESCRIPTION_CHANGED, ServerDescriptionChangedLogSubscriber.new(options)) subscribe(TOPOLOGY_OPENING, TopologyOpeningLogSubscriber.new(options)) subscribe(TOPOLOGY_CHANGED, TopologyChangedLogSubscriber.new(options)) diff --git a/lib/mongo/monitoring/server_closed_log_subscriber.rb b/lib/mongo/monitoring/server_closed_log_subscriber.rb new file mode 100644 index 0000000000..350d5030d0 --- /dev/null +++ b/lib/mongo/monitoring/server_closed_log_subscriber.rb @@ -0,0 +1,30 @@ +# Copyright (C) 2016 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module Mongo + class Monitoring + + # Subscribes to Server Closed events and logs them. + # + # @since 2.3.0 + class ServerClosedLogSubscriber < SDAMLogSubscriber + + private + + def log_event(event) + log_debug("Server #{event.address} connection closed.") + end + end + end +end diff --git a/lib/mongo/monitoring/server_description_changed_log_subscriber.rb b/lib/mongo/monitoring/server_description_changed_log_subscriber.rb index 1d5e458fd5..e31bac0a26 100644 --- a/lib/mongo/monitoring/server_description_changed_log_subscriber.rb +++ b/lib/mongo/monitoring/server_description_changed_log_subscriber.rb @@ -23,7 +23,10 @@ class ServerDescriptionChangedLogSubscriber < SDAMLogSubscriber private def log_event(event) - log_debug("Server description for #{event.address} changed.") + log_debug( + "Server description for #{event.address} changed from " + + "'#{event.previous_description.server_type}' to '#{event.new_description.server_type}'." + ) end end end diff --git a/lib/mongo/monitoring/topology_changed_log_subscriber.rb b/lib/mongo/monitoring/topology_changed_log_subscriber.rb index 900bc128c0..48ca30a93e 100644 --- a/lib/mongo/monitoring/topology_changed_log_subscriber.rb +++ b/lib/mongo/monitoring/topology_changed_log_subscriber.rb @@ -24,8 +24,8 @@ class TopologyChangedLogSubscriber < SDAMLogSubscriber def log_event(event) log_debug( - "Topology type '#{event.previous_topology.display_name}' changed to " + - "type '#{event.new_topology.display_name}'." + "Topology type '#{event.previous_topology.display_name.downcase}' changed to " + + "type '#{event.new_topology.display_name.downcase}'." ) end end diff --git a/lib/mongo/monitoring/topology_opening_log_subscriber.rb b/lib/mongo/monitoring/topology_opening_log_subscriber.rb index 500bd9ce2c..29bb8e1871 100644 --- a/lib/mongo/monitoring/topology_opening_log_subscriber.rb +++ b/lib/mongo/monitoring/topology_opening_log_subscriber.rb @@ -23,7 +23,7 @@ class TopologyOpeningLogSubscriber < SDAMLogSubscriber private def log_event(event) - log_debug("Topology type '#{event.topology.display_name}' initializing.") + log_debug("Topology type '#{event.topology.display_name.downcase}' initializing.") end end end diff --git a/lib/mongo/server/description.rb b/lib/mongo/server/description.rb index 59299918e3..8db0dc5737 100644 --- a/lib/mongo/server/description.rb +++ b/lib/mongo/server/description.rb @@ -458,6 +458,24 @@ def secondary? !!config[SECONDARY] && !replica_set_name.nil? end + # Returns the server type as a symbol. + # + # @example Get the server type. + # description.server_type + # + # @return [ Symbol ] The server type. + # + # @since 2.3.0 + def server_type + return :arbiter if arbiter? + return :ghost if ghost? + return :sharded if mongos? + return :primary if primary? + return :secondary if secondary? + return :standalone if standalone? + :unknown + end + # Is this server a standalone server? # # @example Is the server standalone? diff --git a/spec/mongo/server/description_spec.rb b/spec/mongo/server/description_spec.rb index 477dd2ecac..1793f67975 100644 --- a/spec/mongo/server/description_spec.rb +++ b/spec/mongo/server/description_spec.rb @@ -540,6 +540,90 @@ end end + describe '#server_type' do + + context 'when the server is an arbiter' do + + let(:description) do + described_class.new(address, { 'arbiterOnly' => true, 'setName' => 'test' }) + end + + it 'returns :arbiter' do + expect(description.server_type).to eq(:arbiter) + end + end + + context 'when the server is a ghost' do + + let(:description) do + described_class.new(address, { 'isreplicaset' => true }) + end + + it 'returns :ghost' do + expect(description.server_type).to eq(:ghost) + end + end + + context 'when the server is a mongos' do + + let(:config) do + { 'msg' => 'isdbgrid', 'ismaster' => true } + end + + let(:description) do + described_class.new(address, config) + end + + it 'returns :sharded' do + expect(description.server_type).to eq(:sharded) + end + end + + context 'when the server is a primary' do + + let(:description) do + described_class.new(address, replica) + end + + it 'returns :primary' do + expect(description.server_type).to eq(:primary) + end + end + + context 'when the server is a secondary' do + + let(:description) do + described_class.new(address, { 'secondary' => true, 'setName' => 'test' }) + end + + it 'returns :secondary' do + expect(description.server_type).to eq(:secondary) + end + end + + context 'when the server is standalone' do + + let(:description) do + described_class.new(address, { 'ismaster' => true, 'ok' => 1 }) + end + + it 'returns :standalone' do + expect(description.server_type).to eq(:standalone) + end + end + + context 'when the description has no configuration' do + + let(:description) do + described_class.new(address) + end + + it 'returns :unknown' do + expect(description.server_type).to eq(:unknown) + end + end + end + describe '#unknown?' do context 'when the description has no configuration' do From c8256c7b17ed44b87e4bd45cb532cd2398a2ac6a Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Thu, 18 Feb 2016 15:22:58 +0100 Subject: [PATCH 11/23] Removing non-important fields for tests, defaults are correct --- spec/spec_helper.rb | 2 +- .../replica_set_with_removal.yml | 30 ++----------------- spec/support/sdam_monitoring/standalone.yml | 30 ++----------------- 3 files changed, 7 insertions(+), 55 deletions(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index b0c06d76af..21f496b492 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -19,7 +19,7 @@ require 'mongo' Mongo::Logger.logger = Logger.new($stdout) -Mongo::Logger.logger.level = Logger::DEBUG +Mongo::Logger.logger.level = Logger::INFO require 'support/travis' require 'support/matchers' diff --git a/spec/support/sdam_monitoring/replica_set_with_removal.yml b/spec/support/sdam_monitoring/replica_set_with_removal.yml index 85888fe253..465d7e2c33 100644 --- a/spec/support/sdam_monitoring/replica_set_with_removal.yml +++ b/spec/support/sdam_monitoring/replica_set_with_removal.yml @@ -32,34 +32,20 @@ phases: topologyId: "42" address: "a:27017" previousDescription: - address: "127.0.0.1:27017" + address: "a:27017" arbiters: [] - electionId: null - error: null hosts: [] - maxWireVersion: 0 - me: null - minWireVersion: 0 passives: [] primary: null setName: null - setVersion: null - tags: [] type: "Unknown" newDescription: address: "a:27017" arbiters: [] - electionId: null - error: null hosts: [ "a:27017" ] - maxWireVersion: 4 - me: "a:27017" - minWireVersion: 0 passives: [] primary: true setName: "rs" - setVersion: 1.0 - tags: [] type: "RSPrimary" - server_closed_event: @@ -69,20 +55,10 @@ phases: topology_description_changed_event: topologyId: "42" previousDescription: - type: "Unknown" + topologyType: "Unknown" setName: null - maxSetVersion: null - maxElectionId: null - servers: [ "127.0.0.1:27017" ] - stale: false - compatible: true - compatibilityError: null + servers: [ "a:27017" ] newDescription: topologyType: "ReplicaSetWithPrimary" setName: "rs" - maxSetVersion: 1.0 - maxElectionId: null servers: [ "a:27017" ] - stale: false - compatible: true - compatibilityError: null diff --git a/spec/support/sdam_monitoring/standalone.yml b/spec/support/sdam_monitoring/standalone.yml index 3cc861d948..0aaee6dedb 100644 --- a/spec/support/sdam_monitoring/standalone.yml +++ b/spec/support/sdam_monitoring/standalone.yml @@ -22,53 +22,29 @@ phases: topologyId: "42" address: "a:27017" previousDescription: - address: "127.0.0.1:27017" + address: "a:27017" arbiters: [] - electionId: null - error: null hosts: [] - maxWireVersion: 0 - me: null - minWireVersion: 0 passives: [] primary: null setName: null - setVersion: null - tags: [] type: "Unknown" newDescription: address: "a:27017" arbiters: [] - electionId: null - error: null hosts: [] - maxWireVersion: 4 - me: null - minWireVersion: 0 passives: [] primary: null setName: null - setVersion: null - tags: [] type: "Standalone" - topology_description_changed_event: topologyId: "42" previousDescription: - type: "Unknown" + topologyType: "Unknown" setName: null - maxSetVersion: null - maxElectionId: null - servers: [ "127.0.0.1:27017" ] - stale: false - compatible: true - compatibilityError: null + servers: [ "a:27017" ] newDescription: topologyType: "Single" setName: null - maxSetVersion: null - maxElectionId: null servers: [ "a:27017" ] - stale: false - compatible: true - compatibilityError: null From 5b0954adf5c6bfd3e8d5e2404908d77507a9a267 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Fri, 19 Feb 2016 17:52:01 +0100 Subject: [PATCH 12/23] Adding secondary server to previous description --- spec/support/sdam_monitoring/replica_set_with_removal.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/support/sdam_monitoring/replica_set_with_removal.yml b/spec/support/sdam_monitoring/replica_set_with_removal.yml index 465d7e2c33..3ccaf09644 100644 --- a/spec/support/sdam_monitoring/replica_set_with_removal.yml +++ b/spec/support/sdam_monitoring/replica_set_with_removal.yml @@ -57,7 +57,7 @@ phases: previousDescription: topologyType: "Unknown" setName: null - servers: [ "a:27017" ] + servers: [ "a:27017", "b:27017" ] newDescription: topologyType: "ReplicaSetWithPrimary" setName: "rs" From 108b4eda5434b6b03b4c53223ac92b2bd163ca70 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Sun, 6 Mar 2016 21:25:24 +0100 Subject: [PATCH 13/23] SPEC-222: Fixing specs --- lib/mongo/cluster/topology/sharded.rb | 3 +++ spec/mongo/auth/cr_spec.rb | 12 ++++++++-- spec/mongo/auth/ldap_spec.rb | 12 ++++++++-- spec/mongo/auth/scram_spec.rb | 12 ++++++++-- spec/mongo/auth/x509_spec.rb | 12 ++++++++-- .../cluster/topology/replica_set_spec.rb | 22 +++++++++++-------- spec/mongo/cluster/topology/sharded_spec.rb | 12 ++++++---- spec/mongo/cluster/topology/single_spec.rb | 14 +++++++----- spec/mongo/cluster/topology/unknown_spec.rb | 2 +- spec/mongo/cluster/topology_spec.rb | 2 +- spec/mongo/cluster_spec.rb | 6 ++--- spec/mongo/monitoring_spec.rb | 4 ++-- spec/mongo/server/connection_pool_spec.rb | 8 +++++-- spec/mongo/server/connection_spec.rb | 8 +++++-- spec/mongo/server/description_spec.rb | 16 ++++++++++---- spec/mongo/server_selection_spec.rb | 6 ++--- spec/mongo/server_spec.rb | 8 +++++-- 17 files changed, 113 insertions(+), 46 deletions(-) diff --git a/lib/mongo/cluster/topology/sharded.rb b/lib/mongo/cluster/topology/sharded.rb index 0b5f346890..0d3f48e700 100644 --- a/lib/mongo/cluster/topology/sharded.rb +++ b/lib/mongo/cluster/topology/sharded.rb @@ -27,6 +27,9 @@ class Sharded # @since 2.0.0 NAME = 'Sharded'.freeze + # @return [ Hash ] options The options. + attr_reader :options + # @return [ Monitoring ] monitoring The monitoring. attr_reader :monitoring diff --git a/spec/mongo/auth/cr_spec.rb b/spec/mongo/auth/cr_spec.rb index 426735f828..d3e6980912 100644 --- a/spec/mongo/auth/cr_spec.rb +++ b/spec/mongo/auth/cr_spec.rb @@ -7,15 +7,23 @@ end let(:monitoring) do - Mongo::Monitoring.new + Mongo::Monitoring.new(monitoring: false) end let(:listeners) do Mongo::Event::Listeners.new end + let(:topology) do + double('topology') + end + + let(:cluster) do + double('cluster', topology: topology) + end + let(:server) do - Mongo::Server.new(address, double('cluster'), monitoring, listeners, TEST_OPTIONS) + Mongo::Server.new(address, cluster, monitoring, listeners, TEST_OPTIONS) end let(:connection) do diff --git a/spec/mongo/auth/ldap_spec.rb b/spec/mongo/auth/ldap_spec.rb index 6d99e7dce4..4d3fc75037 100644 --- a/spec/mongo/auth/ldap_spec.rb +++ b/spec/mongo/auth/ldap_spec.rb @@ -7,15 +7,23 @@ end let(:monitoring) do - Mongo::Monitoring.new + Mongo::Monitoring.new(monitoring: false) end let(:listeners) do Mongo::Event::Listeners.new end + let(:topology) do + double('topology') + end + + let(:cluster) do + double('cluster', topology: topology) + end + let(:server) do - Mongo::Server.new(address, double('cluster'), monitoring, listeners, TEST_OPTIONS) + Mongo::Server.new(address, cluster, monitoring, listeners, TEST_OPTIONS) end let(:connection) do diff --git a/spec/mongo/auth/scram_spec.rb b/spec/mongo/auth/scram_spec.rb index eeb94d45cd..f1afc60ad6 100644 --- a/spec/mongo/auth/scram_spec.rb +++ b/spec/mongo/auth/scram_spec.rb @@ -7,15 +7,23 @@ end let(:monitoring) do - Mongo::Monitoring.new + Mongo::Monitoring.new(monitoring: false) end let(:listeners) do Mongo::Event::Listeners.new end + let(:topology) do + double('topology') + end + + let(:cluster) do + double('cluster', topology: topology) + end + let(:server) do - Mongo::Server.new(address, double('cluster'), monitoring, listeners, TEST_OPTIONS) + Mongo::Server.new(address, cluster, monitoring, listeners, TEST_OPTIONS) end let(:connection) do diff --git a/spec/mongo/auth/x509_spec.rb b/spec/mongo/auth/x509_spec.rb index 7c83413e55..21a51e2805 100644 --- a/spec/mongo/auth/x509_spec.rb +++ b/spec/mongo/auth/x509_spec.rb @@ -7,15 +7,23 @@ end let(:monitoring) do - Mongo::Monitoring.new + Mongo::Monitoring.new(monitoring: false) end let(:listeners) do Mongo::Event::Listeners.new end + let(:topology) do + double('topology') + end + + let(:cluster) do + double('cluster', topology: topology) + end + let(:server) do - Mongo::Server.new(address, double('cluster'), monitoring, listeners, TEST_OPTIONS) + Mongo::Server.new(address, cluster, monitoring, listeners, TEST_OPTIONS) end let(:connection) do diff --git a/spec/mongo/cluster/topology/replica_set_spec.rb b/spec/mongo/cluster/topology/replica_set_spec.rb index 46a20c5291..a573344e8e 100644 --- a/spec/mongo/cluster/topology/replica_set_spec.rb +++ b/spec/mongo/cluster/topology/replica_set_spec.rb @@ -11,25 +11,29 @@ end let(:monitoring) do - Mongo::Monitoring.new + Mongo::Monitoring.new(monitoring: false) + end + + let(:cluster) do + double('cluster', topology: topology) end describe '#servers' do let(:mongos) do - Mongo::Server.new(address, double('cluster'), monitoring, listeners, TEST_OPTIONS) + Mongo::Server.new(address, cluster, monitoring, listeners, TEST_OPTIONS) end let(:standalone) do - Mongo::Server.new(address, double('cluster'), monitoring, listeners, TEST_OPTIONS) + Mongo::Server.new(address, cluster, monitoring, listeners, TEST_OPTIONS) end let(:replica_set) do - Mongo::Server.new(address, double('cluster'), monitoring, listeners, TEST_OPTIONS) + Mongo::Server.new(address, cluster, monitoring, listeners, TEST_OPTIONS) end let(:replica_set_two) do - Mongo::Server.new(address, double('cluster'), monitoring, listeners, TEST_OPTIONS) + Mongo::Server.new(address, cluster, monitoring, listeners, TEST_OPTIONS) end let(:mongos_description) do @@ -110,11 +114,11 @@ describe '#add_hosts?' do let(:primary) do - Mongo::Server.new(address, double('cluster'), monitoring, listeners, TEST_OPTIONS) + Mongo::Server.new(address, cluster, monitoring, listeners, TEST_OPTIONS) end let(:secondary) do - Mongo::Server.new(address, double('cluster'), monitoring, listeners, TEST_OPTIONS) + Mongo::Server.new(address, cluster, monitoring, listeners, TEST_OPTIONS) end let(:primary_description) do @@ -193,7 +197,7 @@ describe '#remove_hosts?' do let(:primary) do - Mongo::Server.new(address, double('cluster'), monitoring, listeners, TEST_OPTIONS) + Mongo::Server.new(address, cluster, monitoring, listeners, TEST_OPTIONS) end let(:primary_description) do @@ -274,7 +278,7 @@ describe '#remove_server?' do let(:secondary) do - Mongo::Server.new(address, double('cluster'), monitoring, listeners, TEST_OPTIONS) + Mongo::Server.new(address, cluster, monitoring, listeners, TEST_OPTIONS) end let(:secondary_description) do diff --git a/spec/mongo/cluster/topology/sharded_spec.rb b/spec/mongo/cluster/topology/sharded_spec.rb index ff318bb48b..c8d8af1486 100644 --- a/spec/mongo/cluster/topology/sharded_spec.rb +++ b/spec/mongo/cluster/topology/sharded_spec.rb @@ -11,23 +11,27 @@ end let(:monitoring) do - Mongo::Monitoring.new + Mongo::Monitoring.new(monitoring: false) end let(:listeners) do Mongo::Event::Listeners.new end + let(:cluster) do + double('cluster', topology: topology) + end + let(:mongos) do - Mongo::Server.new(address, double('cluster'), monitoring, listeners, TEST_OPTIONS) + Mongo::Server.new(address, cluster, monitoring, listeners, TEST_OPTIONS) end let(:standalone) do - Mongo::Server.new(address, double('cluster'), monitoring, listeners, TEST_OPTIONS) + Mongo::Server.new(address, cluster, monitoring, listeners, TEST_OPTIONS) end let(:replica_set) do - Mongo::Server.new(address, double('cluster'), monitoring, listeners, TEST_OPTIONS) + Mongo::Server.new(address, cluster, monitoring, listeners, TEST_OPTIONS) end let(:mongos_description) do diff --git a/spec/mongo/cluster/topology/single_spec.rb b/spec/mongo/cluster/topology/single_spec.rb index c22f74106e..62b37cfa66 100644 --- a/spec/mongo/cluster/topology/single_spec.rb +++ b/spec/mongo/cluster/topology/single_spec.rb @@ -7,7 +7,7 @@ end let(:monitoring) do - Mongo::Monitoring.new + Mongo::Monitoring.new(monitoring: false) end let(:topology) do @@ -18,22 +18,26 @@ Mongo::Event::Listeners.new end + let(:cluster) do + double('cluster', topology: topology) + end + describe '.servers' do let(:mongos) do - Mongo::Server.new(address, double('cluster'), monitoring, listeners, TEST_OPTIONS) + Mongo::Server.new(address, cluster, monitoring, listeners, TEST_OPTIONS) end let(:standalone) do - Mongo::Server.new(address, double('cluster'), monitoring, listeners, TEST_OPTIONS) + Mongo::Server.new(address, cluster, monitoring, listeners, TEST_OPTIONS) end let(:standalone_two) do - Mongo::Server.new(address, double('cluster'), monitoring, listeners, TEST_OPTIONS) + Mongo::Server.new(address, cluster, monitoring, listeners, TEST_OPTIONS) end let(:replica_set) do - Mongo::Server.new(address, double('cluster'), monitoring, listeners, TEST_OPTIONS) + Mongo::Server.new(address, cluster, monitoring, listeners, TEST_OPTIONS) end let(:mongos_description) do diff --git a/spec/mongo/cluster/topology/unknown_spec.rb b/spec/mongo/cluster/topology/unknown_spec.rb index b95d3aca0a..7bf44befdd 100644 --- a/spec/mongo/cluster/topology/unknown_spec.rb +++ b/spec/mongo/cluster/topology/unknown_spec.rb @@ -3,7 +3,7 @@ describe Mongo::Cluster::Topology::Unknown do let(:monitoring) do - Mongo::Monitoring.new + Mongo::Monitoring.new(monitoring: false) end let(:topology) do diff --git a/spec/mongo/cluster/topology_spec.rb b/spec/mongo/cluster/topology_spec.rb index 9054c17419..ac909cb9b9 100644 --- a/spec/mongo/cluster/topology_spec.rb +++ b/spec/mongo/cluster/topology_spec.rb @@ -3,7 +3,7 @@ describe Mongo::Cluster::Topology do let(:monitoring) do - Mongo::Monitoring.new + Mongo::Monitoring.new(monitoring: false) end describe '.initial' do diff --git a/spec/mongo/cluster_spec.rb b/spec/mongo/cluster_spec.rb index bd4e4738e8..26591d2d7e 100644 --- a/spec/mongo/cluster_spec.rb +++ b/spec/mongo/cluster_spec.rb @@ -3,7 +3,7 @@ describe Mongo::Cluster do let(:monitoring) do - Mongo::Monitoring.new + Mongo::Monitoring.new(monitoring: false) end let(:cluster) do @@ -258,7 +258,7 @@ end let(:monitoring) do - Mongo::Monitoring.new + Mongo::Monitoring.new(monitoring: false) end let(:server_a) do @@ -351,7 +351,7 @@ end let(:monitoring) do - Mongo::Monitoring.new + Mongo::Monitoring.new(monitoring: false) end let(:server) do diff --git a/spec/mongo/monitoring_spec.rb b/spec/mongo/monitoring_spec.rb index fac06d9d4f..e4bb1f9812 100644 --- a/spec/mongo/monitoring_spec.rb +++ b/spec/mongo/monitoring_spec.rb @@ -45,7 +45,7 @@ end it 'includes the global subscribers' do - expect(monitoring.subscribers.size).to eq(1) + expect(monitoring.subscribers.size).to eq(6) end end @@ -58,7 +58,7 @@ end it 'includes the global subscribers' do - expect(monitoring.subscribers.size).to eq(1) + expect(monitoring.subscribers.size).to eq(6) end end diff --git a/spec/mongo/server/connection_pool_spec.rb b/spec/mongo/server/connection_pool_spec.rb index ffa79360a2..ce26d24c7a 100644 --- a/spec/mongo/server/connection_pool_spec.rb +++ b/spec/mongo/server/connection_pool_spec.rb @@ -11,15 +11,19 @@ end let(:monitoring) do - Mongo::Monitoring.new + Mongo::Monitoring.new(monitoring: false) end let(:listeners) do Mongo::Event::Listeners.new end + let(:topology) do + double('topology') + end + let(:cluster) do - double('cluster') + double('cluster', topology: topology) end describe '#checkin' do diff --git a/spec/mongo/server/connection_spec.rb b/spec/mongo/server/connection_spec.rb index 4ee10bf6eb..da75639d3e 100644 --- a/spec/mongo/server/connection_spec.rb +++ b/spec/mongo/server/connection_spec.rb @@ -7,15 +7,19 @@ end let(:monitoring) do - Mongo::Monitoring.new + Mongo::Monitoring.new(monitoring: false) end let(:listeners) do Mongo::Event::Listeners.new end + let(:topology) do + double('topology') + end + let(:cluster) do - double('cluster') + double('cluster', topology: topology) end let(:server) do diff --git a/spec/mongo/server/description_spec.rb b/spec/mongo/server/description_spec.rb index 1793f67975..9ad6715fde 100644 --- a/spec/mongo/server/description_spec.rb +++ b/spec/mongo/server/description_spec.rb @@ -32,7 +32,15 @@ end let(:monitoring) do - Mongo::Monitoring.new + Mongo::Monitoring.new(monitoring: false) + end + + let(:topology) do + double('topology') + end + + let(:cluster) do + double('cluster', topology: topology) end describe '#arbiter?' do @@ -671,7 +679,7 @@ end let(:server) do - Mongo::Server.new(address, double('cluster'), monitoring, listeners) + Mongo::Server.new(address, cluster, monitoring, listeners) end let(:description) do @@ -692,7 +700,7 @@ end let(:server) do - Mongo::Server.new(other_address, double('cluster'), monitoring, listeners) + Mongo::Server.new(other_address, cluster, monitoring, listeners) end it 'returns false' do @@ -758,7 +766,7 @@ end let(:server) do - Mongo::Server.new(server_address, double('cluster'), monitoring, listeners) + Mongo::Server.new(server_address, cluster, monitoring, listeners) end context 'when the server is included in the description hosts list' do diff --git a/spec/mongo/server_selection_spec.rb b/spec/mongo/server_selection_spec.rb index 90a15ebf85..069b35c9bf 100644 --- a/spec/mongo/server_selection_spec.rb +++ b/spec/mongo/server_selection_spec.rb @@ -11,7 +11,7 @@ context(spec.description) do let(:monitoring) do - Mongo::Monitoring.new + Mongo::Monitoring.new(monitoring: false) end let(:topology) do @@ -34,7 +34,7 @@ let(:candidate_servers) do spec.candidate_servers.collect do |server| address = Mongo::Address.new(server['address']) - Mongo::Server.new(address, double('cluster'), monitoring, listeners, TEST_OPTIONS).tap do |s| + Mongo::Server.new(address, cluster, monitoring, listeners, TEST_OPTIONS).tap do |s| allow(s).to receive(:average_round_trip_time).and_return(server['avg_rtt_ms']) allow(s).to receive(:tags).and_return(server['tags']) allow(s).to receive(:secondary?).and_return(server['type'] == 'RSSecondary') @@ -47,7 +47,7 @@ let(:in_latency_window) do spec.in_latency_window.collect do |server| address = Mongo::Address.new(server['address']) - Mongo::Server.new(address, double('cluster'), monitoring, listeners, TEST_OPTIONS).tap do |s| + Mongo::Server.new(address, cluster, monitoring, listeners, TEST_OPTIONS).tap do |s| allow(s).to receive(:average_round_trip_time).and_return(server['avg_rtt_ms']) allow(s).to receive(:tags).and_return(server['tags']) allow(s).to receive(:connectable?).and_return(true) diff --git a/spec/mongo/server_spec.rb b/spec/mongo/server_spec.rb index 392b40175a..ff296afa29 100644 --- a/spec/mongo/server_spec.rb +++ b/spec/mongo/server_spec.rb @@ -2,8 +2,12 @@ describe Mongo::Server do + let(:topology) do + double('topology') + end + let(:cluster) do - double('cluster') + double('cluster', topology: topology) end let(:listeners) do @@ -11,7 +15,7 @@ end let(:monitoring) do - Mongo::Monitoring.new + Mongo::Monitoring.new(monitoring: false) end let(:address) do From 0cb07dfa2cf1757f5e239a96bdbb87d26d3c4f30 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Thu, 10 Mar 2016 21:47:48 +0100 Subject: [PATCH 14/23] SPEC-222: Ensure events are generated --- spec/mongo/sdam_monitoring_spec.rb | 14 ++++ spec/spec_helper.rb | 1 + spec/support/sdam_monitoring.rb | 57 ++++++++++++++ .../replica_set_with_removal.yml | 78 ++++++++++++++----- spec/support/sdam_monitoring/standalone.yml | 31 ++++---- 5 files changed, 150 insertions(+), 31 deletions(-) diff --git a/spec/mongo/sdam_monitoring_spec.rb b/spec/mongo/sdam_monitoring_spec.rb index d9b2aaaade..ec19063565 100644 --- a/spec/mongo/sdam_monitoring_spec.rb +++ b/spec/mongo/sdam_monitoring_spec.rb @@ -59,6 +59,15 @@ class Server context("Phase: #{index + 1}") do + before(:all) do + @subscriber = Mongo::SDAMMonitoring::TestSubscriber.new + @client.subscribe(Mongo::Monitoring::SERVER_OPENING, @subscriber) + @client.subscribe(Mongo::Monitoring::SERVER_CLOSED, @subscriber) + @client.subscribe(Mongo::Monitoring::SERVER_DESCRIPTION_CHANGED, @subscriber) + @client.subscribe(Mongo::Monitoring::TOPOLOGY_OPENING, @subscriber) + @client.subscribe(Mongo::Monitoring::TOPOLOGY_CHANGED, @subscriber) + end + phase.responses.each do |response| before do @@ -80,7 +89,12 @@ class Server phase.outcome.events.each do |event| + let(:fired_event) do + @subscriber.first_event(event.name) + end + it "expects a #{event.name} to be fired" do + expect(fired_event).to_not be_nil end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 21f496b492..d32cfa3f64 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -27,6 +27,7 @@ require 'support/server_discovery_and_monitoring' require 'support/server_selection_rtt' require 'support/server_selection' +require 'support/sdam_monitoring' require 'support/crud' require 'support/command_monitoring' require 'support/connection_string' diff --git a/spec/support/sdam_monitoring.rb b/spec/support/sdam_monitoring.rb index 75c799ab7b..e97d40473c 100644 --- a/spec/support/sdam_monitoring.rb +++ b/spec/support/sdam_monitoring.rb @@ -1,5 +1,62 @@ +# Copyright (C) 2014-2015 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + module Mongo module SDAMMonitoring + # Test subscriber for SDAM monitoring. + # + # @since 2.3.0 + class TestSubscriber + + # The mappings of event names to types. + # + # @since 2.3.0 + MAPPINGS = { + 'topology_opening_event' => Mongo::Monitoring::Event::TopologyOpening, + 'topology_description_changed_event' => Mongo::Monitoring::Event::TopologyChanged, + 'server_opening_event' => Mongo::Monitoring::Event::ServerOpening, + 'server_description_changed_event' => Mongo::Monitoring::Event::ServerDescriptionChanged, + 'server_closed_event' => Mongo::Monitoring::Event::ServerClosed + }.freeze + + # Implement the succeeded event. + # + # @param [ Event ] event The event. + # + # @since 2.3.0 + def succeeded(event) + events.push(event) + end + + # Get the first event fired for the name, and then delete it. + # + # @param [ String ] name The event name. + # + # @return [ Event ] The matching event. + def first_event(name) + matching = events.find{ |event| event.is_a?(MAPPINGS[name]) } + events.delete(events.find_index(matching)) + matching + end + + private + + def events + @events ||= [] + end + end end end diff --git a/spec/support/sdam_monitoring/replica_set_with_removal.yml b/spec/support/sdam_monitoring/replica_set_with_removal.yml index 3ccaf09644..e156ec7100 100644 --- a/spec/support/sdam_monitoring/replica_set_with_removal.yml +++ b/spec/support/sdam_monitoring/replica_set_with_removal.yml @@ -1,24 +1,49 @@ description: "Monitoring a replica set with non member" -uri: "mongodb://a,b/?replicaSet=rs" +uri: "mongodb://a,b/" phases: - responses: - "a:27017": - ok: 1 - ismaster: true - setName: "rs" - setVersion: 1.0 - hosts: [ "a:27017" ] - minWireVersion: 0 - maxWireVersion: 4 - "b:27017": - ok: 1 - ismaster: true + - + - "a:27017" + - { + ok: 1, + ismaster: true, + setName: "rs", + setVersion: 1.0, + primary: "a:27017", + hosts: [ "a:27017" ], + minWireVersion: 0, + maxWireVersion: 4 + } + - + - "b:27017" + - { ok: 1, ismaster: true } outcome: events: - topology_opening_event: topologyId: "42" + - + topology_description_changed_event: + topologyId: "42" + previousDescription: + topologyType: "Unknown" + servers: [] + newDescription: + topologyType: "Unknown" + servers: + - + address: "a:27017" + arbiters: [] + hosts: [] + passives: [] + type: "Unknown" + - + address: "b:27017" + arbiters: [] + hosts: [] + passives: [] + type: "Unknown" - server_opening_event: topologyId: "42" @@ -36,15 +61,13 @@ phases: arbiters: [] hosts: [] passives: [] - primary: null - setName: null type: "Unknown" newDescription: address: "a:27017" arbiters: [] hosts: [ "a:27017" ] passives: [] - primary: true + primary: "a:27017" setName: "rs" type: "RSPrimary" - @@ -56,9 +79,28 @@ phases: topologyId: "42" previousDescription: topologyType: "Unknown" - setName: null - servers: [ "a:27017", "b:27017" ] + servers: + - + address: "a:27017" + arbiters: [] + hosts: [] + passives: [] + type: "Unknown" + - + address: "b:27017" + arbiters: [] + hosts: [] + passives: [] + type: "Unknown" newDescription: topologyType: "ReplicaSetWithPrimary" setName: "rs" - servers: [ "a:27017" ] + servers: + - + address: "a:27017" + arbiters: [] + hosts: [ "a:27017" ] + passives: [] + primary: "a:27017" + setName: "rs" + type: "RSPrimary" diff --git a/spec/support/sdam_monitoring/standalone.yml b/spec/support/sdam_monitoring/standalone.yml index 0aaee6dedb..a9bd9d2f68 100644 --- a/spec/support/sdam_monitoring/standalone.yml +++ b/spec/support/sdam_monitoring/standalone.yml @@ -3,11 +3,10 @@ uri: "mongodb://a:27017" phases: - responses: - "a:27017": - ok: 1 - ismaster: true - minWireVersion: 0 - maxWireVersion: 4 + - + - "a:27017" + - { ok: 1, ismaster: true, minWireVersion: 0, maxWireVersion: 4 } + outcome: events: - @@ -26,25 +25,31 @@ phases: arbiters: [] hosts: [] passives: [] - primary: null - setName: null type: "Unknown" newDescription: address: "a:27017" arbiters: [] hosts: [] passives: [] - primary: null - setName: null type: "Standalone" - topology_description_changed_event: topologyId: "42" previousDescription: topologyType: "Unknown" - setName: null - servers: [ "a:27017" ] + servers: + - + address: "a:27017" + arbiters: [] + hosts: [] + passives: [] + type: "Unknown" newDescription: topologyType: "Single" - setName: null - servers: [ "a:27017" ] + servers: + - + address: "a:27017" + arbiters: [] + hosts: [] + passives: [] + type: "Standalone" From c0c7f6cc46e8b7223521a2e2fada545a15211b5f Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Thu, 10 Mar 2016 22:25:35 +0100 Subject: [PATCH 15/23] SPEC-222: Starting work on matchers --- spec/mongo/sdam_monitoring_spec.rb | 11 +++---- spec/support/sdam_monitoring.rb | 49 +++++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/spec/mongo/sdam_monitoring_spec.rb b/spec/mongo/sdam_monitoring_spec.rb index ec19063565..e8f6134e27 100644 --- a/spec/mongo/sdam_monitoring_spec.rb +++ b/spec/mongo/sdam_monitoring_spec.rb @@ -87,14 +87,11 @@ class Server end end - phase.outcome.events.each do |event| + phase.outcome.events.each do |expectation| - let(:fired_event) do - @subscriber.first_event(event.name) - end - - it "expects a #{event.name} to be fired" do - expect(fired_event).to_not be_nil + it "expects a #{expectation.name} to be fired" do + fired_event = @subscriber.first_event(expectation.name) + expect(fired_event).to match_sdam_monitoring_event(expectation) end end end diff --git a/spec/support/sdam_monitoring.rb b/spec/support/sdam_monitoring.rb index e97d40473c..19efb92648 100644 --- a/spec/support/sdam_monitoring.rb +++ b/spec/support/sdam_monitoring.rb @@ -13,6 +13,51 @@ # limitations under the License. # +RSpec::Matchers.define :match_topology_opening_event do |expectation| + + match do |event| + event.topology != nil + end +end + +RSpec::Matchers.define :match_topology_description_changed_event do |expectation| + + match do |event| + event.previous_topology != nil + event.new_topology != nil + end +end + +RSpec::Matchers.define :match_server_opening_event do |expectation| + + match do |event| + true + # event.address != nil + end +end + +RSpec::Matchers.define :match_server_description_changed_event do |expectation| + + match do |event| + event.previous_description != nil + event.new_description != nil + end +end + +RSpec::Matchers.define :match_server_closed_event do |expectation| + + match do |event| + event.address != nil + end +end + +RSpec::Matchers.define :match_sdam_monitoring_event do |expectation| + + match do |event| + expect(event).to send("match_#{expectation.name}", expectation) + end +end + module Mongo module SDAMMonitoring @@ -47,7 +92,9 @@ def succeeded(event) # # @return [ Event ] The matching event. def first_event(name) - matching = events.find{ |event| event.is_a?(MAPPINGS[name]) } + matching = events.find do |event| + event.class == MAPPINGS[name] + end events.delete(events.find_index(matching)) matching end From 1b8f53910369220c02f59d8281781d13e6085f50 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Fri, 11 Mar 2016 11:59:45 +0100 Subject: [PATCH 16/23] SPEC-222: Adding topology & description matchers --- spec/support/sdam_monitoring.rb | 47 ++++++++++++++++--- .../replica_set_with_removal.yml | 21 --------- 2 files changed, 41 insertions(+), 27 deletions(-) diff --git a/spec/support/sdam_monitoring.rb b/spec/support/sdam_monitoring.rb index 19efb92648..27fa739973 100644 --- a/spec/support/sdam_monitoring.rb +++ b/spec/support/sdam_monitoring.rb @@ -21,10 +21,10 @@ end RSpec::Matchers.define :match_topology_description_changed_event do |expectation| + include Mongo::SDAMMonitoring::Matchable match do |event| - event.previous_topology != nil - event.new_topology != nil + topologies_match?(event, expectation) end end @@ -32,22 +32,21 @@ match do |event| true - # event.address != nil end end RSpec::Matchers.define :match_server_description_changed_event do |expectation| + include Mongo::SDAMMonitoring::Matchable match do |event| - event.previous_description != nil - event.new_description != nil + descriptions_match?(event, expectation) end end RSpec::Matchers.define :match_server_closed_event do |expectation| match do |event| - event.address != nil + event.address.to_s == expectation.data['address'] end end @@ -60,6 +59,42 @@ module Mongo module SDAMMonitoring + module Matchable + + def descriptions_match?(event, expectation) + description_matches?(event.previous_description, expectation.data['previousDescription']) && + description_matches?(event.new_description, expectation.data['newDescription']) + end + + def topologies_match?(event, expectation) + topology_matches?(event.previous_topology, expectation.data['previousDescription']) && + topology_matches?(event.new_topology, expectation.data['newDescription']) + end + + def description_matches?(actual, expected) + case expected['type'] + when 'Standalone' then actual.standalone? + when 'RSPrimary' then actual.primary? + when 'RSSecondary' then actual.secondary? + when 'RSArbiter' then actual.arbiter? + when 'Mongos' then actual.mongos? + when 'Unknown' then actual.unknown? + when 'PossiblePrimary' then actual.unknown? + when 'RSGhost' then actual.ghost? + when 'RSOther' then actual.other? + end + end + + def topology_matches?(actual, expected) + case expected['topologyType'] + when 'ReplicaSetWithPrimary' then actual.replica_set? + when 'ReplicaSetNoPrimary' then actual.replica_set? + when 'Sharded' then actual.sharded? + when 'Single' then actual.single? + when 'Unknown' then actual.unknown? + end + end + end # Test subscriber for SDAM monitoring. # diff --git a/spec/support/sdam_monitoring/replica_set_with_removal.yml b/spec/support/sdam_monitoring/replica_set_with_removal.yml index e156ec7100..203902c4ea 100644 --- a/spec/support/sdam_monitoring/replica_set_with_removal.yml +++ b/spec/support/sdam_monitoring/replica_set_with_removal.yml @@ -23,27 +23,6 @@ phases: - topology_opening_event: topologyId: "42" - - - topology_description_changed_event: - topologyId: "42" - previousDescription: - topologyType: "Unknown" - servers: [] - newDescription: - topologyType: "Unknown" - servers: - - - address: "a:27017" - arbiters: [] - hosts: [] - passives: [] - type: "Unknown" - - - address: "b:27017" - arbiters: [] - hosts: [] - passives: [] - type: "Unknown" - server_opening_event: topologyId: "42" From d17ebb67fe1b6dcfa303b5bd2d51f218ffc02a6c Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Tue, 15 Mar 2016 14:53:52 +0100 Subject: [PATCH 17/23] SPEC-222: Remove old logging --- lib/mongo/cluster/topology/replica_set.rb | 8 ++++++++ lib/mongo/cluster/topology/unknown.rb | 7 ------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/mongo/cluster/topology/replica_set.rb b/lib/mongo/cluster/topology/replica_set.rb index 424913c556..661830945e 100644 --- a/lib/mongo/cluster/topology/replica_set.rb +++ b/lib/mongo/cluster/topology/replica_set.rb @@ -83,6 +83,14 @@ def elect_primary(description, servers) self end + def has_readable_server?(server_selector) + + end + + def has_writable_server? + + end + # Initialize the topology with the options. # # @example Initialize the topology. diff --git a/lib/mongo/cluster/topology/unknown.rb b/lib/mongo/cluster/topology/unknown.rb index 08446429d6..309a6280de 100644 --- a/lib/mongo/cluster/topology/unknown.rb +++ b/lib/mongo/cluster/topology/unknown.rb @@ -59,8 +59,6 @@ def display_name # @return [ Sharded, ReplicaSet ] The new topology. def elect_primary(description, servers) if description.mongos? - # @todo: Remove. - log_debug("Mongos #{description.address.to_s} discovered.") sharded = Sharded.new(options, monitoring) topology_changed(sharded) sharded @@ -220,11 +218,6 @@ def standalone_discovered private def initialize_replica_set(description, servers) - # @todo: Remove. - log_debug( - "Server #{description.address.to_s} discovered as primary in replica set: " + - "'#{description.replica_set_name}'. Changing topology to replica set." - ) servers.each do |server| if server.standalone? && server.address != description.address server.description.unknown! From e3928b27fd0a6ccf431ef10f3b65dea821fd1357 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Sun, 27 Mar 2016 17:31:49 +0200 Subject: [PATCH 18/23] SPEC-222: Unknown topology read/write check --- lib/mongo/cluster/topology/unknown.rb | 28 +++++++++++++++++++++ spec/mongo/cluster/topology/unknown_spec.rb | 14 +++++++++++ 2 files changed, 42 insertions(+) diff --git a/lib/mongo/cluster/topology/unknown.rb b/lib/mongo/cluster/topology/unknown.rb index 309a6280de..a9d94caab2 100644 --- a/lib/mongo/cluster/topology/unknown.rb +++ b/lib/mongo/cluster/topology/unknown.rb @@ -67,6 +67,34 @@ def elect_primary(description, servers) end end + # Determine if the topology would select a readable server for the + # provided candidates and read preference. + # + # @example Is a readable server present? + # topology.has_readable_server?(servers, read_preference) + # + # @param [ Array ] servers The server candidates. + # @param [ ServerSelector, Symbol ] read_preference The read + # preference. + # + # @return [ true, false ] If a readable server is present. + # + # @since 2.3.0 + def has_readable_server?(servers, read_preference); false; end + + # Determine if the topology would select a writable server for the + # provided candidates. + # + # @example Is a writable server present? + # topology.has_writable_server?(servers) + # + # @param [ Array ] servers The server candidates. + # + # @return [ true, false ] If a writable server is present. + # + # @since 2.3.0 + def has_writable_server?(servers); false; end + # Initialize the topology with the options. # # @example Initialize the topology. diff --git a/spec/mongo/cluster/topology/unknown_spec.rb b/spec/mongo/cluster/topology/unknown_spec.rb index 7bf44befdd..f3de452b46 100644 --- a/spec/mongo/cluster/topology/unknown_spec.rb +++ b/spec/mongo/cluster/topology/unknown_spec.rb @@ -49,6 +49,20 @@ end end + describe '#has_readable_servers?' do + + it 'returns false' do + expect(topology).to_not have_readable_server(nil, nil) + end + end + + describe '#has_writable_servers?' do + + it 'returns false' do + expect(topology).to_not have_writable_server(nil) + end + end + describe '#add_hosts?' do context 'when the description is from an unknown server' do From 851cf43426af58ad182a5eb8b68d4d0cb37a60f7 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Sun, 27 Mar 2016 17:36:16 +0200 Subject: [PATCH 19/23] SPEC-222: Single topology read/write check --- lib/mongo/cluster/topology/single.rb | 30 ++++++++++++++++++++ spec/mongo/cluster/topology/single_spec.rb | 32 ++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/lib/mongo/cluster/topology/single.rb b/lib/mongo/cluster/topology/single.rb index 48ce0cc30b..f36dfcc87d 100644 --- a/lib/mongo/cluster/topology/single.rb +++ b/lib/mongo/cluster/topology/single.rb @@ -61,6 +61,36 @@ def display_name # @return [ Single ] The topology. def elect_primary(description, servers); self; end + # Determine if the topology would select a readable server for the + # provided candidates and read preference. + # + # @example Is a readable server present? + # topology.has_readable_server?(servers, read_preference) + # + # @param [ Array ] servers The server candidates. + # @param [ ServerSelector, Symbol ] read_preference The read + # preference. + # + # @return [ true, false ] If a readable server is present. + # + # @since 2.3.0 + def has_readable_server?(servers, read_preference); true; end + + # Determine if the topology would select a writable server for the + # provided candidates. + # + # @example Is a writable server present? + # topology.has_writable_server?(servers) + # + # @param [ Array ] servers The server candidates. + # + # @return [ true, false ] If a writable server is present. + # + # @since 2.3.0 + def has_writable_server?(servers) + servers.any?{ |server| server.primary? } + end + # Initialize the topology with the options. # # @example Initialize the topology. diff --git a/spec/mongo/cluster/topology/single_spec.rb b/spec/mongo/cluster/topology/single_spec.rb index 62b37cfa66..74d57ab9e6 100644 --- a/spec/mongo/cluster/topology/single_spec.rb +++ b/spec/mongo/cluster/topology/single_spec.rb @@ -89,6 +89,38 @@ end end + describe '#has_readable_servers?' do + + it 'returns true' do + expect(topology).to have_readable_server(nil, nil) + end + end + + describe '#has_writable_servers?' do + + context 'when the server is a primary' do + + let(:server) do + double('server', :primary? => true) + end + + it 'returns true' do + expect(topology).to have_writable_server([ server ]) + end + end + + context 'when the server is not a primary (e.g. direct connect to secondary)' do + + let(:server) do + double('server', :primary? => false) + end + + it 'returns false' do + expect(topology).to_not have_writable_server([ server ]) + end + end + end + describe '#add_hosts?' do it 'returns false' do From bc6feb25bfdd015a153397b0b614c2438357b2bf Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Sun, 27 Mar 2016 17:38:40 +0200 Subject: [PATCH 20/23] SPEC-222: Sharded topology read/write check --- lib/mongo/cluster/topology/sharded.rb | 28 +++++++++++++++++++++ spec/mongo/cluster/topology/sharded_spec.rb | 14 +++++++++++ 2 files changed, 42 insertions(+) diff --git a/lib/mongo/cluster/topology/sharded.rb b/lib/mongo/cluster/topology/sharded.rb index 0d3f48e700..f8e6a246f9 100644 --- a/lib/mongo/cluster/topology/sharded.rb +++ b/lib/mongo/cluster/topology/sharded.rb @@ -58,6 +58,34 @@ def display_name # @return [ Sharded ] The topology. def elect_primary(description, servers); self; end + # Determine if the topology would select a readable server for the + # provided candidates and read preference. + # + # @example Is a readable server present? + # topology.has_readable_server?(servers, read_preference) + # + # @param [ Array ] servers The server candidates. + # @param [ ServerSelector, Symbol ] read_preference The read + # preference. + # + # @return [ true, false ] If a readable server is present. + # + # @since 2.3.0 + def has_readable_server?(servers, read_preference); true; end + + # Determine if the topology would select a writable server for the + # provided candidates. + # + # @example Is a writable server present? + # topology.has_writable_server?(servers) + # + # @param [ Array ] servers The server candidates. + # + # @return [ true, false ] If a writable server is present. + # + # @since 2.3.0 + def has_writable_server?(servers); true; end + # Initialize the topology with the options. # # @example Initialize the topology. diff --git a/spec/mongo/cluster/topology/sharded_spec.rb b/spec/mongo/cluster/topology/sharded_spec.rb index c8d8af1486..5926188f0e 100644 --- a/spec/mongo/cluster/topology/sharded_spec.rb +++ b/spec/mongo/cluster/topology/sharded_spec.rb @@ -84,6 +84,20 @@ end end + describe '#has_readable_servers?' do + + it 'returns true' do + expect(topology).to have_readable_server(nil, nil) + end + end + + describe '#has_writable_servers?' do + + it 'returns true' do + expect(topology).to have_writable_server(nil) + end + end + describe '#add_hosts?' do it 'returns false' do From 86cbd47bf4f20fa61a8ee4f9e45b6fc1c7c2e2d2 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Sun, 27 Mar 2016 17:47:16 +0200 Subject: [PATCH 21/23] SPEC-222: Replica set topology read/write check --- lib/mongo/cluster/topology/replica_set.rb | 32 +++++++++++--- .../cluster/topology/replica_set_spec.rb | 42 +++++++++++++++++++ spec/mongo/cluster/topology/single_spec.rb | 2 + 3 files changed, 71 insertions(+), 5 deletions(-) diff --git a/lib/mongo/cluster/topology/replica_set.rb b/lib/mongo/cluster/topology/replica_set.rb index 661830945e..0c55d7f0dc 100644 --- a/lib/mongo/cluster/topology/replica_set.rb +++ b/lib/mongo/cluster/topology/replica_set.rb @@ -83,12 +83,34 @@ def elect_primary(description, servers) self end - def has_readable_server?(server_selector) - - end - - def has_writable_server? + # Determine if the topology would select a readable server for the + # provided candidates and read preference. + # + # @example Is a readable server present? + # topology.has_readable_server?(servers, read_preference) + # + # @param [ Array ] servers The server candidates. + # @param [ ServerSelector, Symbol ] read_preference The read + # preference. + # + # @return [ true, false ] If a readable server is present. + # + # @since 2.3.0 + def has_readable_server?(servers, read_preference); false; end + # Determine if the topology would select a writable server for the + # provided candidates. + # + # @example Is a writable server present? + # topology.has_writable_server?(servers) + # + # @param [ Array ] servers The server candidates. + # + # @return [ true, false ] If a writable server is present. + # + # @since 2.3.0 + def has_writable_server?(servers) + servers.any?{ |server| server.primary? } end # Initialize the topology with the options. diff --git a/spec/mongo/cluster/topology/replica_set_spec.rb b/spec/mongo/cluster/topology/replica_set_spec.rb index a573344e8e..ff4c7250ec 100644 --- a/spec/mongo/cluster/topology/replica_set_spec.rb +++ b/spec/mongo/cluster/topology/replica_set_spec.rb @@ -111,6 +111,48 @@ end end + describe '#has_readable_servers?' do + + let(:topology) do + described_class.new({}, monitoring, []) + end + + it 'test read preference' + end + + describe '#has_writable_servers?' do + + let(:topology) do + described_class.new({}, monitoring, []) + end + + context 'when a primary server exists' do + + let(:primary) do + double('server', :primary? => true) + end + + let(:secondary) do + double('server', :primary? => false) + end + + it 'returns true' do + expect(topology).to have_writable_server([ primary, secondary ]) + end + end + + context 'when no primary server exists' do + + let(:server) do + double('server', :primary? => false) + end + + it 'returns false' do + expect(topology).to_not have_writable_server([ server ]) + end + end + end + describe '#add_hosts?' do let(:primary) do diff --git a/spec/mongo/cluster/topology/single_spec.rb b/spec/mongo/cluster/topology/single_spec.rb index 74d57ab9e6..eaebb7931d 100644 --- a/spec/mongo/cluster/topology/single_spec.rb +++ b/spec/mongo/cluster/topology/single_spec.rb @@ -91,6 +91,8 @@ describe '#has_readable_servers?' do + it 'test read preference primary with direct connection to secondary' + it 'returns true' do expect(topology).to have_readable_server(nil, nil) end From be8a12efde86fcfc8b64052e14c4b1093d449588 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Sun, 27 Mar 2016 18:21:56 +0200 Subject: [PATCH 22/23] SPEC-222: Replica set check uses server selector --- lib/mongo/cluster/topology/replica_set.rb | 18 ++- lib/mongo/cluster/topology/sharded.rb | 14 +- lib/mongo/cluster/topology/single.rb | 16 +- lib/mongo/cluster/topology/unknown.rb | 14 +- lib/mongo/server_selector.rb | 10 +- lib/mongo/server_selector/selectable.rb | 30 ++-- spec/mongo/address/unix_spec.rb | 2 +- .../cluster/topology/replica_set_spec.rb | 147 +++++++++++++++++- spec/mongo/cluster/topology/single_spec.rb | 14 +- spec/mongo/socket/unix_spec.rb | 6 +- 10 files changed, 215 insertions(+), 56 deletions(-) diff --git a/lib/mongo/cluster/topology/replica_set.rb b/lib/mongo/cluster/topology/replica_set.rb index 0c55d7f0dc..993602c212 100644 --- a/lib/mongo/cluster/topology/replica_set.rb +++ b/lib/mongo/cluster/topology/replica_set.rb @@ -87,16 +87,18 @@ def elect_primary(description, servers) # provided candidates and read preference. # # @example Is a readable server present? - # topology.has_readable_server?(servers, read_preference) + # topology.has_readable_server?(cluster, server_selector) # - # @param [ Array ] servers The server candidates. - # @param [ ServerSelector, Symbol ] read_preference The read - # preference. + # @param [ Cluster ] cluster The cluster. + # @param [ ServerSelector, Symbol ] server_selector The server + # selector. # # @return [ true, false ] If a readable server is present. # # @since 2.3.0 - def has_readable_server?(servers, read_preference); false; end + def has_readable_server?(cluster, server_selector) + server_selector.candidates(cluster).any? + end # Determine if the topology would select a writable server for the # provided candidates. @@ -104,13 +106,13 @@ def has_readable_server?(servers, read_preference); false; end # @example Is a writable server present? # topology.has_writable_server?(servers) # - # @param [ Array ] servers The server candidates. + # @param [ Cluster ] cluster The cluster. # # @return [ true, false ] If a writable server is present. # # @since 2.3.0 - def has_writable_server?(servers) - servers.any?{ |server| server.primary? } + def has_writable_server?(cluster) + cluster.servers.any?{ |server| server.primary? } end # Initialize the topology with the options. diff --git a/lib/mongo/cluster/topology/sharded.rb b/lib/mongo/cluster/topology/sharded.rb index f8e6a246f9..d5a4689cf2 100644 --- a/lib/mongo/cluster/topology/sharded.rb +++ b/lib/mongo/cluster/topology/sharded.rb @@ -62,16 +62,16 @@ def elect_primary(description, servers); self; end # provided candidates and read preference. # # @example Is a readable server present? - # topology.has_readable_server?(servers, read_preference) + # topology.has_readable_server?(cluster, server_selector) # - # @param [ Array ] servers The server candidates. - # @param [ ServerSelector, Symbol ] read_preference The read - # preference. + # @param [ Cluster ] cluster The cluster. + # @param [ ServerSelector, Symbol ] server_selector The server + # selector. # # @return [ true, false ] If a readable server is present. # # @since 2.3.0 - def has_readable_server?(servers, read_preference); true; end + def has_readable_server?(cluster, server_selector); true; end # Determine if the topology would select a writable server for the # provided candidates. @@ -79,12 +79,12 @@ def has_readable_server?(servers, read_preference); true; end # @example Is a writable server present? # topology.has_writable_server?(servers) # - # @param [ Array ] servers The server candidates. + # @param [ Cluster ] cluster The cluster. # # @return [ true, false ] If a writable server is present. # # @since 2.3.0 - def has_writable_server?(servers); true; end + def has_writable_server?(cluster); true; end # Initialize the topology with the options. # diff --git a/lib/mongo/cluster/topology/single.rb b/lib/mongo/cluster/topology/single.rb index f36dfcc87d..eda33c59a8 100644 --- a/lib/mongo/cluster/topology/single.rb +++ b/lib/mongo/cluster/topology/single.rb @@ -65,16 +65,16 @@ def elect_primary(description, servers); self; end # provided candidates and read preference. # # @example Is a readable server present? - # topology.has_readable_server?(servers, read_preference) + # topology.has_readable_server?(cluster, server_selector) # - # @param [ Array ] servers The server candidates. - # @param [ ServerSelector, Symbol ] read_preference The read - # preference. + # @param [ Cluster ] cluster The cluster. + # @param [ ServerSelector, Symbol ] server_selector The server + # selector. # # @return [ true, false ] If a readable server is present. # # @since 2.3.0 - def has_readable_server?(servers, read_preference); true; end + def has_readable_server?(cluster, server_selector); true; end # Determine if the topology would select a writable server for the # provided candidates. @@ -82,13 +82,13 @@ def has_readable_server?(servers, read_preference); true; end # @example Is a writable server present? # topology.has_writable_server?(servers) # - # @param [ Array ] servers The server candidates. + # @param [ Cluster ] cluster The cluster. # # @return [ true, false ] If a writable server is present. # # @since 2.3.0 - def has_writable_server?(servers) - servers.any?{ |server| server.primary? } + def has_writable_server?(cluster) + cluster.servers.any?{ |server| server.primary? } end # Initialize the topology with the options. diff --git a/lib/mongo/cluster/topology/unknown.rb b/lib/mongo/cluster/topology/unknown.rb index a9d94caab2..1a657c5114 100644 --- a/lib/mongo/cluster/topology/unknown.rb +++ b/lib/mongo/cluster/topology/unknown.rb @@ -71,16 +71,16 @@ def elect_primary(description, servers) # provided candidates and read preference. # # @example Is a readable server present? - # topology.has_readable_server?(servers, read_preference) + # topology.has_readable_server?(cluster, server_selector) # - # @param [ Array ] servers The server candidates. - # @param [ ServerSelector, Symbol ] read_preference The read - # preference. + # @param [ Cluster ] cluster The cluster. + # @param [ ServerSelector, Symbol ] server_selector The server + # selector. # # @return [ true, false ] If a readable server is present. # # @since 2.3.0 - def has_readable_server?(servers, read_preference); false; end + def has_readable_server?(cluster, server_selector); false; end # Determine if the topology would select a writable server for the # provided candidates. @@ -88,12 +88,12 @@ def has_readable_server?(servers, read_preference); false; end # @example Is a writable server present? # topology.has_writable_server?(servers) # - # @param [ Array ] servers The server candidates. + # @param [ Cluster ] cluster The cluster. # # @return [ true, false ] If a writable server is present. # # @since 2.3.0 - def has_writable_server?(servers); false; end + def has_writable_server?(cluster); false; end # Initialize the topology with the options. # diff --git a/lib/mongo/server_selector.rb b/lib/mongo/server_selector.rb index 921af8f221..359fd0b3fb 100644 --- a/lib/mongo/server_selector.rb +++ b/lib/mongo/server_selector.rb @@ -48,11 +48,11 @@ module ServerSelector # # @since 2.0.0 PREFERENCES = { - nearest: Nearest, - primary: Primary, - primary_preferred: PrimaryPreferred, - secondary: Secondary, - secondary_preferred: SecondaryPreferred + nearest: Nearest, + primary: Primary, + primary_preferred: PrimaryPreferred, + secondary: Secondary, + secondary_preferred: SecondaryPreferred }.freeze # Create a server selector object. diff --git a/lib/mongo/server_selector/selectable.rb b/lib/mongo/server_selector/selectable.rb index 6c35b3182e..e7851a10ac 100644 --- a/lib/mongo/server_selector/selectable.rb +++ b/lib/mongo/server_selector/selectable.rb @@ -40,6 +40,26 @@ def ==(other) name == other.name && tag_sets == other.tag_sets end + # Get the potential candidates to selecto from the cluster. + # + # @example Get the server candidates. + # selectable.candidates(cluster) + # + # @param [ Cluster ] cluster The cluster. + # + # @return [ Array ] The candidate servers. + # + # @since 2.3.0 + def candidates(cluster) + if cluster.single? + cluster.servers + elsif cluster.sharded? + near_servers(cluster.servers) + else + select(cluster.servers) + end + end + # Initialize the server selector. # # @example Initialize the selector. @@ -136,16 +156,6 @@ def local_threshold private - def candidates(cluster) - if cluster.single? - cluster.servers - elsif cluster.sharded? - near_servers(cluster.servers) - else - select(cluster.servers) - end - end - # Select the primary from a list of provided candidates. # # @param [ Array ] candidates List of candidate servers to select the diff --git a/spec/mongo/address/unix_spec.rb b/spec/mongo/address/unix_spec.rb index b033989519..ef9404dacc 100644 --- a/spec/mongo/address/unix_spec.rb +++ b/spec/mongo/address/unix_spec.rb @@ -24,7 +24,7 @@ end end - describe '#socket' do + pending '#socket' do let(:address) do '/tmp/mongodb-27017.sock' diff --git a/spec/mongo/cluster/topology/replica_set_spec.rb b/spec/mongo/cluster/topology/replica_set_spec.rb index ff4c7250ec..6f5a52a84b 100644 --- a/spec/mongo/cluster/topology/replica_set_spec.rb +++ b/spec/mongo/cluster/topology/replica_set_spec.rb @@ -117,7 +117,140 @@ described_class.new({}, monitoring, []) end - it 'test read preference' + let(:cluster) do + double('cluster', servers: servers, single?: false, sharded?: false) + end + + context 'when the read preference is primary' do + + let(:selector) do + Mongo::ServerSelector.get(:mode => :primary) + end + + context 'when a primary exists' do + + let(:servers) do + [ double('server', primary?: true) ] + end + + it 'returns true' do + expect(topology).to have_readable_server(cluster, selector) + end + end + + context 'when a primary does not exist' do + + let(:servers) do + [ double('server', primary?: false) ] + end + + it 'returns false' do + expect(topology).to_not have_readable_server(cluster, selector) + end + end + end + + context 'when the read preference is primary preferred' do + + let(:selector) do + Mongo::ServerSelector.get(:mode => :primary_preferred) + end + + context 'when a primary exists' do + + let(:servers) do + [ double('server', primary?: true) ] + end + + it 'returns true' do + expect(topology).to have_readable_server(cluster, selector) + end + end + + context 'when a primary does not exist' do + + let(:servers) do + [ double('server', primary?: false, secondary?: true, average_round_trip_time: 0.01) ] + end + + it 'returns true' do + expect(topology).to have_readable_server(cluster, selector) + end + end + end + + context 'when the read preference is secondary' do + + let(:selector) do + Mongo::ServerSelector.get(:mode => :secondary) + end + + context 'when a secondary exists' do + + let(:servers) do + [ double('server', secondary?: true, average_round_trip_time: 0.01) ] + end + + it 'returns true' do + expect(topology).to have_readable_server(cluster, selector) + end + end + + context 'when a secondary does not exist' do + + let(:servers) do + [ double('server', secondary?: false) ] + end + + it 'returns false' do + expect(topology).to_not have_readable_server(cluster, selector) + end + end + end + + context 'when the read preference is secondary preferred' do + + let(:selector) do + Mongo::ServerSelector.get(:mode => :secondary_preferred) + end + + context 'when a secondary exists' do + + let(:servers) do + [ double('server', primary?: false, secondary?: true, average_round_trip_time: 0.01) ] + end + + it 'returns true' do + expect(topology).to have_readable_server(cluster, selector) + end + end + + context 'when a secondary does not exist' do + + let(:servers) do + [ double('server', secondary?: false, primary?: true) ] + end + + it 'returns true' do + expect(topology).to have_readable_server(cluster, selector) + end + end + end + + context 'when the read preference is nearest' do + + let(:selector) do + Mongo::ServerSelector.get(:mode => :nearest) + end + + let(:servers) do + [ double('server', primary?: false, secondary?: true, average_round_trip_time: 0.01) ] + end + + it 'returns true' do + expect(topology).to have_readable_server(cluster, selector) + end + end end describe '#has_writable_servers?' do @@ -136,8 +269,12 @@ double('server', :primary? => false) end + let(:cluster) do + double('cluster', servers: [ primary, secondary ]) + end + it 'returns true' do - expect(topology).to have_writable_server([ primary, secondary ]) + expect(topology).to have_writable_server(cluster) end end @@ -147,8 +284,12 @@ double('server', :primary? => false) end + let(:cluster) do + double('cluster', servers: [ server ]) + end + it 'returns false' do - expect(topology).to_not have_writable_server([ server ]) + expect(topology).to_not have_writable_server(cluster) end end end diff --git a/spec/mongo/cluster/topology/single_spec.rb b/spec/mongo/cluster/topology/single_spec.rb index eaebb7931d..90c128a181 100644 --- a/spec/mongo/cluster/topology/single_spec.rb +++ b/spec/mongo/cluster/topology/single_spec.rb @@ -91,8 +91,6 @@ describe '#has_readable_servers?' do - it 'test read preference primary with direct connection to secondary' - it 'returns true' do expect(topology).to have_readable_server(nil, nil) end @@ -106,8 +104,12 @@ double('server', :primary? => true) end + let(:cluster) do + double('cluster', servers: [ server ]) + end + it 'returns true' do - expect(topology).to have_writable_server([ server ]) + expect(topology).to have_writable_server(cluster) end end @@ -117,8 +119,12 @@ double('server', :primary? => false) end + let(:cluster) do + double('cluster', servers: [ server ]) + end + it 'returns false' do - expect(topology).to_not have_writable_server([ server ]) + expect(topology).to_not have_writable_server(cluster) end end end diff --git a/spec/mongo/socket/unix_spec.rb b/spec/mongo/socket/unix_spec.rb index d3b72e49c0..8d9b511f20 100644 --- a/spec/mongo/socket/unix_spec.rb +++ b/spec/mongo/socket/unix_spec.rb @@ -16,7 +16,7 @@ socket.close end - it 'connects to the server' do + pending 'connects to the server' do expect(socket).to be_alive end end @@ -33,7 +33,7 @@ socket.close end - it 'returns true' do + pending 'returns true' do expect(socket).to be_alive end end @@ -44,7 +44,7 @@ socket.close end - it 'raises error' do + pending 'raises error' do expect { socket.alive? }.to raise_error(IOError) end end From c130941d5f03b03c18289bddbddd231e6a5c27af Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Sun, 27 Mar 2016 19:00:29 +0200 Subject: [PATCH 23/23] SPEC-222: Delegate to topology from cluster --- lib/mongo/cluster.rb | 28 ++++++++++++ lib/mongo/cluster/topology/replica_set.rb | 2 +- lib/mongo/cluster/topology/sharded.rb | 2 +- lib/mongo/cluster/topology/single.rb | 6 ++- lib/mongo/cluster/topology/unknown.rb | 2 +- spec/mongo/cluster/topology/single_spec.rb | 52 +++++++++++++++++++++- spec/mongo/cluster_spec.rb | 18 ++++++++ 7 files changed, 103 insertions(+), 7 deletions(-) diff --git a/lib/mongo/cluster.rb b/lib/mongo/cluster.rb index 27f0d62893..415bfe267a 100644 --- a/lib/mongo/cluster.rb +++ b/lib/mongo/cluster.rb @@ -87,6 +87,34 @@ def add(host) end end + # Determine if the cluster would select a readable server for the + # provided read preference. + # + # @example Is a readable server present? + # topology.has_readable_server?(server_selector) + # + # @param [ ServerSelector ] server_selector The server + # selector. + # + # @return [ true, false ] If a readable server is present. + # + # @since 2.3.0 + def has_readable_server?(server_selector) + topology.has_readable_server?(self, server_selector) + end + + # Determine if the cluster would select a writable server. + # + # @example Is a writable server present? + # topology.has_writable_server? + # + # @return [ true, false ] If a writable server is present. + # + # @since 2.3.0 + def has_writable_server? + topology.has_writable_server?(self) + end + # Instantiate the new cluster. # # @api private diff --git a/lib/mongo/cluster/topology/replica_set.rb b/lib/mongo/cluster/topology/replica_set.rb index 993602c212..d3ceba9cfb 100644 --- a/lib/mongo/cluster/topology/replica_set.rb +++ b/lib/mongo/cluster/topology/replica_set.rb @@ -90,7 +90,7 @@ def elect_primary(description, servers) # topology.has_readable_server?(cluster, server_selector) # # @param [ Cluster ] cluster The cluster. - # @param [ ServerSelector, Symbol ] server_selector The server + # @param [ ServerSelector ] server_selector The server # selector. # # @return [ true, false ] If a readable server is present. diff --git a/lib/mongo/cluster/topology/sharded.rb b/lib/mongo/cluster/topology/sharded.rb index d5a4689cf2..a8f191395e 100644 --- a/lib/mongo/cluster/topology/sharded.rb +++ b/lib/mongo/cluster/topology/sharded.rb @@ -65,7 +65,7 @@ def elect_primary(description, servers); self; end # topology.has_readable_server?(cluster, server_selector) # # @param [ Cluster ] cluster The cluster. - # @param [ ServerSelector, Symbol ] server_selector The server + # @param [ ServerSelector ] server_selector The server # selector. # # @return [ true, false ] If a readable server is present. diff --git a/lib/mongo/cluster/topology/single.rb b/lib/mongo/cluster/topology/single.rb index eda33c59a8..56221349ef 100644 --- a/lib/mongo/cluster/topology/single.rb +++ b/lib/mongo/cluster/topology/single.rb @@ -68,13 +68,15 @@ def elect_primary(description, servers); self; end # topology.has_readable_server?(cluster, server_selector) # # @param [ Cluster ] cluster The cluster. - # @param [ ServerSelector, Symbol ] server_selector The server + # @param [ ServerSelector ] server_selector The server # selector. # # @return [ true, false ] If a readable server is present. # # @since 2.3.0 - def has_readable_server?(cluster, server_selector); true; end + def has_readable_server?(cluster, server_selector) + server_selector.candidates(cluster).any? + end # Determine if the topology would select a writable server for the # provided candidates. diff --git a/lib/mongo/cluster/topology/unknown.rb b/lib/mongo/cluster/topology/unknown.rb index 1a657c5114..38d13ef2ff 100644 --- a/lib/mongo/cluster/topology/unknown.rb +++ b/lib/mongo/cluster/topology/unknown.rb @@ -74,7 +74,7 @@ def elect_primary(description, servers) # topology.has_readable_server?(cluster, server_selector) # # @param [ Cluster ] cluster The cluster. - # @param [ ServerSelector, Symbol ] server_selector The server + # @param [ ServerSelector ] server_selector The server # selector. # # @return [ true, false ] If a readable server is present. diff --git a/spec/mongo/cluster/topology/single_spec.rb b/spec/mongo/cluster/topology/single_spec.rb index 90c128a181..bbf89290fa 100644 --- a/spec/mongo/cluster/topology/single_spec.rb +++ b/spec/mongo/cluster/topology/single_spec.rb @@ -91,8 +91,56 @@ describe '#has_readable_servers?' do - it 'returns true' do - expect(topology).to have_readable_server(nil, nil) + let(:cluster) do + double('cluster', servers: servers, single?: true) + end + + let(:selector) do + Mongo::ServerSelector.get(mode: :primary) + end + + context 'when using a direct connection to a primary' do + + let(:servers) do + [ double('server', primary?: true) ] + end + + it 'returns true' do + expect(topology).to have_readable_server(cluster, selector) + end + end + + context 'when using a direct connection to a secondary' do + + let(:servers) do + [ double('server', secondary?: true) ] + end + + it 'returns true' do + expect(topology).to have_readable_server(cluster, selector) + end + end + + context 'when using a direct connection to an arbiter' do + + let(:servers) do + [ double('server', secondary?: true) ] + end + + it 'returns true' do + expect(topology).to have_readable_server(cluster, selector) + end + end + + context 'when no servers have been scanned' do + + let(:servers) do + [] + end + + it 'returns false' do + expect(topology).to_not have_readable_server(cluster, selector) + end end end diff --git a/spec/mongo/cluster_spec.rb b/spec/mongo/cluster_spec.rb index 26591d2d7e..41306fa0f1 100644 --- a/spec/mongo/cluster_spec.rb +++ b/spec/mongo/cluster_spec.rb @@ -59,6 +59,24 @@ end end + describe '#has_readable_server?' do + + let(:selector) do + Mongo::ServerSelector.get(mode: :primary) + end + + it 'delegates to the topology' do + expect(cluster).to have_readable_server(selector) + end + end + + describe '#has_writable_server?' do + + it 'delegates to the topology' do + expect(cluster).to_not have_writable_server + end + end + describe '#inspect' do let(:preference) do