From 6f5fd4d66cbd59f2f847ebe028143e880967f151 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Fri, 22 Jan 2016 18:51:58 +0100 Subject: [PATCH 01/34] 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 d92bd132c7..c93c93ff0e 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 be5f89ae2689e0e2e14f7b367627099001e3b47c Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Sun, 24 Jan 2016 19:15:52 +0100 Subject: [PATCH 02/34] 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 9d7b95b6da335c1f951d07f1bf7174c0658780ba Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Sun, 24 Jan 2016 19:18:14 +0100 Subject: [PATCH 03/34] 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 9c8774b3dd..a63dcc0e06 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 53c1c8285a920f9a4f71ec037e073724b68cb7e9 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Sun, 24 Jan 2016 20:14:53 +0100 Subject: [PATCH 04/34] 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 2134d5ec8c..f8ee087bc6 100644 --- a/lib/mongo/cluster.rb +++ b/lib/mongo/cluster.rb @@ -119,9 +119,9 @@ def initialize(seeds, monitoring, options = Options::Redacted.new) @event_listeners = Event::Listeners.new @options = options.freeze @app_metadata ||= AppMetadata.new(self) - @topology = Topology.initial(seeds, options) @update_lock = Mutex.new @pool_lock = Mutex.new + @topology = Topology.initial(seeds, monitoring, options) subscribe_to(Event::STANDALONE_DISCOVERED, Event::StandaloneDiscovered.new(self)) subscribe_to(Event::DESCRIPTION_CHANGED, Event::DescriptionChanged.new(self)) diff --git a/lib/mongo/cluster/topology.rb b/lib/mongo/cluster/topology.rb index c93c93ff0e..684fb0a417 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 8ac208f0d9..36cf66a105 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 1188e7bfe4..a68830cb71 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 b487563681..24755b309a 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 d0f6c56f64..aea47889d8 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 69e7428cec..008bf14af0 100644 --- a/spec/mongo/cluster/topology/replica_set_spec.rb +++ b/spec/mongo/cluster/topology/replica_set_spec.rb @@ -64,7 +64,7 @@ context 'when no replica set name is provided' do let(:topology) do - described_class.new({}) + described_class.new({}, monitoring, []) end let(:servers) do @@ -79,7 +79,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 @@ -95,21 +95,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 @@ -133,7 +133,7 @@ end let(:topology) do - described_class.new(:replica_set => 'testing') + described_class.new({ :replica_set => 'testing' }, monitoring) end before do @@ -207,7 +207,7 @@ end let(:topology) do - described_class.new(:replica_set => 'testing') + described_class.new({ :replica_set => 'testing' }, monitoring) end before do @@ -289,7 +289,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 d67775849b..ebec7c6b27 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 b4eb0bdc3c..d789654f4e 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 2c290e0675..34a34f2508 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 50b4e55a28..804f80b8a7 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 39bff45c2171bbcbdcf93ae2115ab6b9c833f57f Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Sun, 24 Jan 2016 20:31:58 +0100 Subject: [PATCH 05/34] 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 aea47889d8..8602860efe 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 0011d59e8891ba52b1562c2396338a5d79a375d6 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Mon, 25 Jan 2016 16:13:09 +0100 Subject: [PATCH 06/34] 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 a63dcc0e06..53bf6f8267 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 fc77c396c5133ebb6fc1d0baf231e5af1dbcf85d Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Thu, 4 Feb 2016 17:18:38 +0100 Subject: [PATCH 07/34] 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 53bf6f8267..d93bef91f0 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 3c56e74e91b0c136c0d308dd666a0a2f1f7e07e2 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Mon, 8 Feb 2016 14:22:46 -0500 Subject: [PATCH 08/34] 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 d93bef91f0..d7d2f00a41 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 0628f7617c..20a5d99bbe 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") MAX_STALENESS_TESTS = Dir.glob("#{CURRENT_PATH}/support/max_staleness/**/*.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 aeac6094646a46e52f8b49e7252efad0f911673e Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Tue, 9 Feb 2016 09:37:22 -0500 Subject: [PATCH 09/34] 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 f8ee087bc6..66c025a704 100644 --- a/lib/mongo/cluster.rb +++ b/lib/mongo/cluster.rb @@ -45,6 +45,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 @@ -89,7 +92,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 684fb0a417..27a40e4e3e 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 36cf66a105..687a388284 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 a68830cb71..438323edb2 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 24755b309a..a7c4c57905 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 8602860efe..461d5da178 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 1b43f6fa63..bd79e0ee79 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 d7d2f00a41..661268af23 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 4bfe1f341e..3bb6a79906 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 @@ -164,6 +165,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.merge(app_metadata: cluster.app_metadata)) 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 71df7f6620..3f81da42bd 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 20a5d99bbe..5c2237d639 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -24,7 +24,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 ca7bed7c52bce32d7c754c9f9eda6dddf66c2a3d Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Tue, 9 Feb 2016 10:10:46 -0500 Subject: [PATCH 10/34] 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 66c025a704..947492fed6 100644 --- a/lib/mongo/cluster.rb +++ b/lib/mongo/cluster.rb @@ -24,6 +24,7 @@ module Mongo # @since 2.0.0 class Cluster extend Forwardable + include Monitoring::Publishable include Event::Subscriber include Loggable @@ -266,11 +267,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 461d5da178..f3434d669c 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 661268af23..75eb408b5d 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 fb575efe0d..031f0d2757 100644 --- a/lib/mongo/server/description.rb +++ b/lib/mongo/server/description.rb @@ -480,6 +480,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 c01c84c5e2..5530829f94 100644 --- a/spec/mongo/server/description_spec.rb +++ b/spec/mongo/server/description_spec.rb @@ -546,6 +546,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 b458c99dfbfd401184c1422c5141444ede5d64ba Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Thu, 18 Feb 2016 15:22:58 +0100 Subject: [PATCH 11/34] 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 5c2237d639..20a5d99bbe 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -24,7 +24,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 b0317870214b95dedc4f7e7a6be10f4ca48c0b5f Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Fri, 19 Feb 2016 17:52:01 +0100 Subject: [PATCH 12/34] 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 2a4931a4547c08e7c83f06776374a1bce7358a02 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Sun, 6 Mar 2016 21:25:24 +0100 Subject: [PATCH 13/34] SPEC-222: Fixing specs --- lib/mongo/cluster/topology/sharded.rb | 3 +++ spec/mongo/auth/cr_spec.rb | 9 ++++++--- spec/mongo/auth/ldap_spec.rb | 7 +++++-- spec/mongo/auth/scram_spec.rb | 4 ++-- spec/mongo/auth/x509_spec.rb | 7 +++++-- spec/mongo/cluster/topology/replica_set_spec.rb | 6 +++++- spec/mongo/cluster/topology/sharded_spec.rb | 4 ++-- spec/mongo/cluster/topology/single_spec.rb | 4 ++-- 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 | 7 +++++-- spec/mongo/server/description_spec.rb | 10 +++++++++- spec/mongo/server_selection_spec.rb | 2 +- spec/mongo/server_spec.rb | 8 ++++++-- 17 files changed, 64 insertions(+), 29 deletions(-) diff --git a/lib/mongo/cluster/topology/sharded.rb b/lib/mongo/cluster/topology/sharded.rb index 438323edb2..2aa9b4a8d4 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 422ff76cde..93c977a445 100644 --- a/spec/mongo/auth/cr_spec.rb +++ b/spec/mongo/auth/cr_spec.rb @@ -7,7 +7,7 @@ end let(:monitoring) do - Mongo::Monitoring.new + Mongo::Monitoring.new(monitoring: false) end let(:listeners) do @@ -15,11 +15,14 @@ end let(:cluster) do - double('cluster').tap do |cl| + double('cluster', topology: topology).tap do |cl| allow(cl).to receive(:app_metadata).and_return(app_metadata) end - end + let(:topology) do + double('topology') + end + let(:server) do Mongo::Server.new(address, cluster, monitoring, listeners, TEST_OPTIONS) end diff --git a/spec/mongo/auth/ldap_spec.rb b/spec/mongo/auth/ldap_spec.rb index 8d9d8ef5f5..402e885a99 100644 --- a/spec/mongo/auth/ldap_spec.rb +++ b/spec/mongo/auth/ldap_spec.rb @@ -7,7 +7,7 @@ end let(:monitoring) do - Mongo::Monitoring.new + Mongo::Monitoring.new(monitoring: false) end let(:listeners) do @@ -15,9 +15,12 @@ end let(:cluster) do - double('cluster').tap do |cl| + double('cluster', topology: topology).tap do |cl| allow(cl).to receive(:app_metadata).and_return(app_metadata) end + + let(:topology) do + double('topology') end let(:server) do diff --git a/spec/mongo/auth/scram_spec.rb b/spec/mongo/auth/scram_spec.rb index bb6ec1dc9c..d5c346ad21 100644 --- a/spec/mongo/auth/scram_spec.rb +++ b/spec/mongo/auth/scram_spec.rb @@ -7,7 +7,7 @@ end let(:monitoring) do - Mongo::Monitoring.new + Mongo::Monitoring.new(monitoring: false) end let(:listeners) do @@ -15,7 +15,7 @@ end let(:cluster) do - double('cluster').tap do |cl| + double('cluster', topology: topology).tap do |cl| allow(cl).to receive(:app_metadata).and_return(app_metadata) end end diff --git a/spec/mongo/auth/x509_spec.rb b/spec/mongo/auth/x509_spec.rb index f3dc00a679..3c09cd4e3b 100644 --- a/spec/mongo/auth/x509_spec.rb +++ b/spec/mongo/auth/x509_spec.rb @@ -7,7 +7,7 @@ end let(:monitoring) do - Mongo::Monitoring.new + Mongo::Monitoring.new(monitoring: false) end let(:listeners) do @@ -15,9 +15,12 @@ end let(:cluster) do - double('cluster').tap do |cl| + double('cluster', topology: topology).tap do |cl| allow(cl).to receive(:app_metadata).and_return(app_metadata) end + + let(:topology) do + double('topology') end let(:server) do diff --git a/spec/mongo/cluster/topology/replica_set_spec.rb b/spec/mongo/cluster/topology/replica_set_spec.rb index 008bf14af0..e1a0fc132d 100644 --- a/spec/mongo/cluster/topology/replica_set_spec.rb +++ b/spec/mongo/cluster/topology/replica_set_spec.rb @@ -11,7 +11,11 @@ end let(:monitoring) do - Mongo::Monitoring.new + Mongo::Monitoring.new(monitoring: false) + end + + let(:cluster) do + double('cluster', topology: topology) end let(:cluster) do diff --git a/spec/mongo/cluster/topology/sharded_spec.rb b/spec/mongo/cluster/topology/sharded_spec.rb index ebec7c6b27..a40941fb6c 100644 --- a/spec/mongo/cluster/topology/sharded_spec.rb +++ b/spec/mongo/cluster/topology/sharded_spec.rb @@ -11,7 +11,7 @@ end let(:monitoring) do - Mongo::Monitoring.new + Mongo::Monitoring.new(monitoring: false) end let(:listeners) do @@ -19,7 +19,7 @@ end let(:cluster) do - double('cluster').tap do |cl| + double('cluster', topology: topology).tap do |cl| allow(cl).to receive(:app_metadata).and_return(app_metadata) end end diff --git a/spec/mongo/cluster/topology/single_spec.rb b/spec/mongo/cluster/topology/single_spec.rb index d789654f4e..30ab4e0197 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 @@ -19,7 +19,7 @@ end let(:cluster) do - double('cluster').tap do |cl| + double('cluster', topology: topology).tap do |cl| allow(cl).to receive(:app_metadata).and_return(app_metadata) end end 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 34a34f2508..45dc86b742 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 @@ -268,7 +268,7 @@ end let(:monitoring) do - Mongo::Monitoring.new + Mongo::Monitoring.new(monitoring: false) end let(:server_a) do @@ -361,7 +361,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 440ac5dca0..ca86f195dd 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').tap do |cl| + double('cluster', topology: topology).tap do |cl| allow(cl).to receive(:app_metadata).and_return(app_metadata) end end diff --git a/spec/mongo/server/connection_spec.rb b/spec/mongo/server/connection_spec.rb index 3d4ff5aed1..8f210cc029 100644 --- a/spec/mongo/server/connection_spec.rb +++ b/spec/mongo/server/connection_spec.rb @@ -7,7 +7,7 @@ end let(:monitoring) do - Mongo::Monitoring.new + Mongo::Monitoring.new(monitoring: false) end let(:listeners) do @@ -19,9 +19,12 @@ end let(:cluster) do - double('cluster').tap do |cl| + double('cluster', topology: topology).tap do |cl| allow(cl).to receive(:app_metadata).and_return(app_metadata) end + + let(:topology) do + double('topology') end let(:server) do diff --git a/spec/mongo/server/description_spec.rb b/spec/mongo/server/description_spec.rb index 5530829f94..431e5fc932 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 let(:cluster) do diff --git a/spec/mongo/server_selection_spec.rb b/spec/mongo/server_selection_spec.rb index 804f80b8a7..b52503a7a2 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 diff --git a/spec/mongo/server_spec.rb b/spec/mongo/server_spec.rb index 9492066248..701a5ffc0f 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').tap do |cl| + double('cluster', topology: topology).tap do |cl| allow(cl).to receive(:app_metadata).and_return(app_metadata) end end @@ -13,7 +17,7 @@ end let(:monitoring) do - Mongo::Monitoring.new + Mongo::Monitoring.new(monitoring: false) end let(:address) do From 5fe745cfe202efc9bbcd20505e822aab2993242f Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Thu, 10 Mar 2016 21:47:48 +0100 Subject: [PATCH 14/34] 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 20a5d99bbe..8a8f6f48fd 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -32,6 +32,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 49545aac8650255bb8b79a01845b64c37d4c0fd3 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Thu, 10 Mar 2016 22:25:35 +0100 Subject: [PATCH 15/34] 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 6bc6684f6ed73fac5d5d1cabdb76dd83bf526dec Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Fri, 11 Mar 2016 11:59:45 +0100 Subject: [PATCH 16/34] 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 60ee5c702fb6594050500fc89bd10789e3fed490 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Tue, 15 Mar 2016 14:53:52 +0100 Subject: [PATCH 17/34] 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 687a388284..3a74ac92f8 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 f3434d669c..89e60ab576 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 77d8ee8732ca3f826ad0eb12ea2d2ebf0c8280e7 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Sun, 27 Mar 2016 17:31:49 +0200 Subject: [PATCH 18/34] 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 89e60ab576..ad1d33b355 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 b229284bf8b94f9586f9c72a9e5a898144b0d1e1 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Sun, 27 Mar 2016 17:36:16 +0200 Subject: [PATCH 19/34] 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 a7c4c57905..bafa9b5b95 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 30ab4e0197..f96973c863 100644 --- a/spec/mongo/cluster/topology/single_spec.rb +++ b/spec/mongo/cluster/topology/single_spec.rb @@ -91,6 +91,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 9a726e84ff87ec88bf791c57915af2896f33c3c1 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Sun, 27 Mar 2016 17:38:40 +0200 Subject: [PATCH 20/34] 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 2aa9b4a8d4..ec4725f75c 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 a40941fb6c..3ca34c169f 100644 --- a/spec/mongo/cluster/topology/sharded_spec.rb +++ b/spec/mongo/cluster/topology/sharded_spec.rb @@ -86,6 +86,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 c8e0d0acec39383b54f2c61ad05e582d06fff344 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Sun, 27 Mar 2016 17:47:16 +0200 Subject: [PATCH 21/34] 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 3a74ac92f8..5092de1eaa 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 e1a0fc132d..d9cb0a318f 100644 --- a/spec/mongo/cluster/topology/replica_set_spec.rb +++ b/spec/mongo/cluster/topology/replica_set_spec.rb @@ -117,6 +117,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 f96973c863..9cafeeec33 100644 --- a/spec/mongo/cluster/topology/single_spec.rb +++ b/spec/mongo/cluster/topology/single_spec.rb @@ -93,6 +93,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 d89240a73df0d111c1a0d2a8e8e933d150601770 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Sun, 27 Mar 2016 18:21:56 +0200 Subject: [PATCH 22/34] 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 | 20 +++ spec/mongo/address/unix_spec.rb | 2 +- spec/mongo/auth/cr_spec.rb | 3 +- .../cluster/topology/replica_set_spec.rb | 147 +++++++++++++++++- spec/mongo/cluster/topology/single_spec.rb | 14 +- spec/mongo/socket/unix_spec.rb | 6 +- 11 files changed, 217 insertions(+), 47 deletions(-) diff --git a/lib/mongo/cluster/topology/replica_set.rb b/lib/mongo/cluster/topology/replica_set.rb index 5092de1eaa..392a1d41ff 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 ec4725f75c..ae1fb7d52c 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 bafa9b5b95..90babacbbd 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 ad1d33b355..0b456ae077 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 54eeef09da..bb33b349fa 100644 --- a/lib/mongo/server_selector.rb +++ b/lib/mongo/server_selector.rb @@ -53,11 +53,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 7df7276368..7a67ce5159 100644 --- a/lib/mongo/server_selector/selectable.rb +++ b/lib/mongo/server_selector/selectable.rb @@ -48,6 +48,26 @@ def ==(other) max_staleness == other.max_staleness 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. 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/auth/cr_spec.rb b/spec/mongo/auth/cr_spec.rb index 93c977a445..5ce95ca9c5 100644 --- a/spec/mongo/auth/cr_spec.rb +++ b/spec/mongo/auth/cr_spec.rb @@ -18,11 +18,12 @@ double('cluster', topology: topology).tap do |cl| allow(cl).to receive(:app_metadata).and_return(app_metadata) end + end let(:topology) do double('topology') end - + let(:server) do Mongo::Server.new(address, cluster, monitoring, listeners, TEST_OPTIONS) end diff --git a/spec/mongo/cluster/topology/replica_set_spec.rb b/spec/mongo/cluster/topology/replica_set_spec.rb index d9cb0a318f..aef1b25481 100644 --- a/spec/mongo/cluster/topology/replica_set_spec.rb +++ b/spec/mongo/cluster/topology/replica_set_spec.rb @@ -123,7 +123,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 @@ -142,8 +275,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 @@ -153,8 +290,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 9cafeeec33..0ad783a88a 100644 --- a/spec/mongo/cluster/topology/single_spec.rb +++ b/spec/mongo/cluster/topology/single_spec.rb @@ -93,8 +93,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 @@ -108,8 +106,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 @@ -119,8 +121,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 e96240227ee6bfe93fe54183d954d612eacdf064 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Sun, 27 Mar 2016 19:00:29 +0200 Subject: [PATCH 23/34] 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 947492fed6..19857050ab 100644 --- a/lib/mongo/cluster.rb +++ b/lib/mongo/cluster.rb @@ -101,6 +101,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 392a1d41ff..10cec67b39 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 ae1fb7d52c..8cec2197e6 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 90babacbbd..edd0dc8a3d 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 0b456ae077..81fb05fa6f 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 0ad783a88a..9d5c07a50f 100644 --- a/spec/mongo/cluster/topology/single_spec.rb +++ b/spec/mongo/cluster/topology/single_spec.rb @@ -93,8 +93,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 45dc86b742..987ec5f1b1 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 From cbd0f8222a186fa4c1525d5cd7558fb3042ec885 Mon Sep 17 00:00:00 2001 From: Emily Date: Mon, 31 Oct 2016 17:46:15 +0100 Subject: [PATCH 24/34] RUBY-1096 Add MemberDiscovered event and updated SDAM Monitoring implementation --- lib/mongo/cluster.rb | 19 ++- lib/mongo/cluster/topology.rb | 5 +- lib/mongo/cluster/topology/replica_set.rb | 17 +-- lib/mongo/cluster/topology/sharded.rb | 16 ++- lib/mongo/cluster/topology/single.rb | 21 +++- lib/mongo/cluster/topology/unknown.rb | 21 +++- lib/mongo/event.rb | 6 + lib/mongo/event/member_discovered.rb | 65 ++++++++++ lib/mongo/monitoring.rb | 12 +- lib/mongo/monitoring/event/server_closed.rb | 4 +- .../event/server_description_changed.rb | 4 +- lib/mongo/monitoring/event/server_opening.rb | 4 +- .../monitoring/event/topology_changed.rb | 4 +- lib/mongo/monitoring/event/topology_closed.rb | 4 +- .../monitoring/event/topology_opening.rb | 4 +- lib/mongo/monitoring/sdam_log_subscriber.rb | 6 +- .../server_closed_log_subscriber.rb | 2 +- ...rver_description_changed_log_subscriber.rb | 2 +- .../server_opening_log_subscriber.rb | 2 +- .../topology_changed_log_subscriber.rb | 2 +- .../topology_opening_log_subscriber.rb | 2 +- lib/mongo/server/description.rb | 2 +- lib/mongo/server/description/inspector.rb | 3 +- .../inspector/description_changed.rb | 2 +- .../inspector/member_discovered.rb | 59 +++++++++ lib/mongo/server_selector/selectable.rb | 14 ++- spec/mongo/address/unix_spec.rb | 2 +- spec/mongo/auth/cr_spec.rb | 3 +- spec/mongo/auth/ldap_spec.rb | 4 +- spec/mongo/auth/scram_spec.rb | 7 +- spec/mongo/auth/x509_spec.rb | 4 +- .../cluster/topology/replica_set_spec.rb | 13 +- spec/mongo/cluster/topology/sharded_spec.rb | 3 +- spec/mongo/cluster/topology/single_spec.rb | 3 +- spec/mongo/cluster_spec.rb | 5 + spec/mongo/max_staleness_spec.rb | 6 +- spec/mongo/sdam_monitoring_spec.rb | 62 ++-------- ...ry_and_monitoring_spec.rb => sdam_spec.rb} | 61 ++-------- spec/mongo/server/connection_pool_spec.rb | 3 +- spec/mongo/server/connection_spec.rb | 4 +- spec/mongo/server/description_spec.rb | 7 +- spec/mongo/server_selector_spec.rb | 4 + spec/mongo/server_spec.rb | 3 +- spec/mongo/socket/unix_spec.rb | 6 +- spec/spec_helper.rb | 2 +- spec/support/sdam_monitoring.rb | 10 +- .../replica_set_with_no_primary.yml | 112 ++++++++++++++++++ .../replica_set_with_primary.yml | 111 +++++++++++++++++ .../replica_set_with_removal.yml | 21 ++++ .../sdam_monitoring/required_replica_set.yml | 84 +++++++++++++ spec/support/sdam_monitoring/standalone.yml | 17 ++- spec/support/shared/server_selector.rb | 6 + 52 files changed, 669 insertions(+), 196 deletions(-) create mode 100644 lib/mongo/event/member_discovered.rb create mode 100644 lib/mongo/server/description/inspector/member_discovered.rb rename spec/mongo/{server_discovery_and_monitoring_spec.rb => sdam_spec.rb} (50%) create mode 100644 spec/support/sdam_monitoring/replica_set_with_no_primary.yml create mode 100644 spec/support/sdam_monitoring/replica_set_with_primary.yml create mode 100644 spec/support/sdam_monitoring/required_replica_set.yml diff --git a/lib/mongo/cluster.rb b/lib/mongo/cluster.rb index 19857050ab..5781ea4cb7 100644 --- a/lib/mongo/cluster.rb +++ b/lib/mongo/cluster.rb @@ -58,7 +58,8 @@ class Cluster # @since 2.4.0 attr_reader :app_metadata - def_delegators :topology, :replica_set?, :replica_set_name, :sharded?, :single?, :unknown? + def_delegators :topology, :replica_set?, :replica_set_name, :sharded?, + :single?, :unknown?, :member_discovered def_delegators :@cursor_reaper, :register_cursor, :schedule_kill_cursor, :unregister_cursor # Determine if this cluster of servers is equal to another object. Checks the @@ -112,7 +113,7 @@ def add(host) # # @return [ true, false ] If a readable server is present. # - # @since 2.3.0 + # @since 2.4.0 def has_readable_server?(server_selector) topology.has_readable_server?(self, server_selector) end @@ -124,7 +125,7 @@ def has_readable_server?(server_selector) # # @return [ true, false ] If a writable server is present. # - # @since 2.3.0 + # @since 2.4.0 def has_writable_server? topology.has_writable_server?(self) end @@ -154,12 +155,22 @@ def initialize(seeds, monitoring, options = Options::Redacted.new) @pool_lock = Mutex.new @topology = Topology.initial(seeds, monitoring, options) + publish_sdam_event( + Monitoring::TOPOLOGY_OPENING, + Monitoring::Event::TopologyOpening.new(@topology) + ) + subscribe_to(Event::STANDALONE_DISCOVERED, Event::StandaloneDiscovered.new(self)) subscribe_to(Event::DESCRIPTION_CHANGED, Event::DescriptionChanged.new(self)) - subscribe_to(Event::PRIMARY_ELECTED, Event::PrimaryElected.new(self)) + subscribe_to(Event::MEMBER_DISCOVERED, Event::MemberDiscovered.new(self)) seeds.each{ |seed| add(seed) } + publish_sdam_event( + Monitoring::TOPOLOGY_CHANGED, + Monitoring::Event::TopologyChanged.new(@topology, @topology) + ) if @servers.size > 1 + @cursor_reaper = CursorReaper.new @cursor_reaper.run! diff --git a/lib/mongo/cluster/topology.rb b/lib/mongo/cluster/topology.rb index 27a40e4e3e..04b1ca8e4f 100644 --- a/lib/mongo/cluster/topology.rb +++ b/lib/mongo/cluster/topology.rb @@ -26,7 +26,7 @@ class Cluster module Topology extend self - # The 2 various topologies for server selection. + # The various topologies for server selection. # # @since 2.0.0 OPTIONS = { @@ -48,14 +48,13 @@ module Topology # # @since 2.0.0 def initial(seeds, monitoring, options) - topology = if options.has_key?(:connect) + if options.has_key?(:connect) OPTIONS.fetch(options[:connect]).new(options, monitoring, seeds) elsif options.has_key?(:replica_set) ReplicaSet.new(options, monitoring, options) else Unknown.new(options, monitoring, seeds) end - topology end end end diff --git a/lib/mongo/cluster/topology/replica_set.rb b/lib/mongo/cluster/topology/replica_set.rb index 10cec67b39..0cb6f34dfe 100644 --- a/lib/mongo/cluster/topology/replica_set.rb +++ b/lib/mongo/cluster/topology/replica_set.rb @@ -65,7 +65,6 @@ def display_name def elect_primary(description, servers) if description.replica_set_name == replica_set_name unless detect_stale_primary!(description) - log_debug("Server #{description.address.to_s} elected as primary in #{replica_set_name}.") servers.each do |server| if server.primary? && server.address != description.address server.description.unknown! @@ -95,7 +94,7 @@ def elect_primary(description, servers) # # @return [ true, false ] If a readable server is present. # - # @since 2.3.0 + # @since 2.4.0 def has_readable_server?(cluster, server_selector) server_selector.candidates(cluster).any? end @@ -110,7 +109,7 @@ def has_readable_server?(cluster, server_selector) # # @return [ true, false ] If a writable server is present. # - # @since 2.3.0 + # @since 2.4.0 def has_writable_server?(cluster) cluster.servers.any?{ |server| server.primary? } end @@ -130,10 +129,6 @@ 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. @@ -265,6 +260,14 @@ def unknown?; false; end # @since 2.0.6 def standalone_discovered; self; end + # Notify the topology that a member was discovered. + # + # @example Notify the topology that a member was discovered. + # topology.member_discovered + # + # @since 2.4.0 + def member_discovered; end; + private def update_max_election_id(description) diff --git a/lib/mongo/cluster/topology/sharded.rb b/lib/mongo/cluster/topology/sharded.rb index 8cec2197e6..31dad10b93 100644 --- a/lib/mongo/cluster/topology/sharded.rb +++ b/lib/mongo/cluster/topology/sharded.rb @@ -70,7 +70,7 @@ def elect_primary(description, servers); self; end # # @return [ true, false ] If a readable server is present. # - # @since 2.3.0 + # @since 2.4.0 def has_readable_server?(cluster, server_selector); true; end # Determine if the topology would select a writable server for the @@ -83,7 +83,7 @@ def has_readable_server?(cluster, server_selector); true; end # # @return [ true, false ] If a writable server is present. # - # @since 2.3.0 + # @since 2.4.0 def has_writable_server?(cluster); true; end # Initialize the topology with the options. @@ -99,10 +99,6 @@ def has_writable_server?(cluster); true; 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. @@ -223,6 +219,14 @@ def unknown?; false; end # @since 2.0.6 def standalone_discovered; self; end + # Notify the topology that a member was discovered. + # + # @example Notify the cluster that a member was discovered. + # topology.member_discovered + # + # @since 2.4.0 + def member_discovered; end; + private def remove_self?(description, server) diff --git a/lib/mongo/cluster/topology/single.rb b/lib/mongo/cluster/topology/single.rb index edd0dc8a3d..cf7fa1d9a1 100644 --- a/lib/mongo/cluster/topology/single.rb +++ b/lib/mongo/cluster/topology/single.rb @@ -73,7 +73,7 @@ def elect_primary(description, servers); self; end # # @return [ true, false ] If a readable server is present. # - # @since 2.3.0 + # @since 2.4.0 def has_readable_server?(cluster, server_selector) server_selector.candidates(cluster).any? end @@ -88,7 +88,7 @@ def has_readable_server?(cluster, server_selector) # # @return [ true, false ] If a writable server is present. # - # @since 2.3.0 + # @since 2.4.0 def has_writable_server?(cluster) cluster.servers.any?{ |server| server.primary? } end @@ -107,10 +107,6 @@ 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. @@ -227,6 +223,19 @@ def unknown?; false; end # # @since 2.0.6 def standalone_discovered; self; end + + # Publish that a member of this topology was discovered. + # + # @example Publish that a member was discovered. + # topology.member_discovered + # + # @since 2.4.0 + def member_discovered + publish_sdam_event( + Monitoring::TOPOLOGY_CHANGED, + Monitoring::Event::TopologyChanged.new(self, self) + ) + end end end end diff --git a/lib/mongo/cluster/topology/unknown.rb b/lib/mongo/cluster/topology/unknown.rb index 81fb05fa6f..3c29466b83 100644 --- a/lib/mongo/cluster/topology/unknown.rb +++ b/lib/mongo/cluster/topology/unknown.rb @@ -79,7 +79,7 @@ def elect_primary(description, servers) # # @return [ true, false ] If a readable server is present. # - # @since 2.3.0 + # @since 2.4.0 def has_readable_server?(cluster, server_selector); false; end # Determine if the topology would select a writable server for the @@ -92,7 +92,7 @@ def has_readable_server?(cluster, server_selector); false; end # # @return [ true, false ] If a writable server is present. # - # @since 2.3.0 + # @since 2.4.0 def has_writable_server?(cluster); false; end # Initialize the topology with the options. @@ -109,10 +109,6 @@ 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. @@ -243,6 +239,19 @@ def standalone_discovered end end + # Notify the topology that a member was discovered. + # + # @example Notify the topology that a member was discovered. + # topology.member_discovered + # + # @since 2.4.0 + def member_discovered + publish_sdam_event( + Monitoring::TOPOLOGY_CHANGED, + Monitoring::Event::TopologyChanged.new(self, self) + ) + end + private def initialize_replica_set(description, servers) diff --git a/lib/mongo/event.rb b/lib/mongo/event.rb index 20b60c3dfc..d916bd9de1 100644 --- a/lib/mongo/event.rb +++ b/lib/mongo/event.rb @@ -16,6 +16,7 @@ require 'mongo/event/publisher' require 'mongo/event/subscriber' require 'mongo/event/primary_elected' +require 'mongo/event/member_discovered' require 'mongo/event/description_changed' require 'mongo/event/standalone_discovered' @@ -32,6 +33,11 @@ module Event # @since 2.0.0 PRIMARY_ELECTED = 'primary_elected'.freeze + # When a server is discovered to be a member of a topology. + # + # @since 2.4.0 + MEMBER_DISCOVERED = 'member_discovered'.freeze + # When a server is to be removed from a cluster. # # @since 2.0.6 diff --git a/lib/mongo/event/member_discovered.rb b/lib/mongo/event/member_discovered.rb new file mode 100644 index 0000000000..03a294e7f1 --- /dev/null +++ b/lib/mongo/event/member_discovered.rb @@ -0,0 +1,65 @@ +# Copyright (C) 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 Event + + # This handles member discovered events for server descriptions. + # + # @since 2.4.0 + class MemberDiscovered + 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 member discovered event handler. + # + # @example Create the new handler. + # MemberDiscovered.new(cluster) + # + # @param [ Mongo::Cluster ] cluster The cluster to publish from. + # + # @since 2.0.0 + def initialize(cluster) + @cluster = cluster + @options = cluster.options + @monitoring = cluster.monitoring + end + + # This event tells the cluster that a member of a known topology is discovered. + # + # @example Handle the event. + # member_discovered.handle(previous_description, description) + # + # @param [ Server::Description ] previous The previous description of the server. + # @param [ Server::Description ] updated The updated description of the server. + # + # @since 2.4.0 + def handle(previous, updated) + if updated.primary? || updated.mongos? + cluster.elect_primary!(updated) + else + cluster.member_discovered + end + end + end + end +end diff --git a/lib/mongo/monitoring.rb b/lib/mongo/monitoring.rb index 75eb408b5d..b6f198d327 100644 --- a/lib/mongo/monitoring.rb +++ b/lib/mongo/monitoring.rb @@ -36,32 +36,32 @@ class Monitoring # Server closed topic. # - # @since 2.3.0 + # @since 2.4.0 SERVER_CLOSED = 'ServerClosed'.freeze # Server description changed topic. # - # @since 2.3.0 + # @since 2.4.0 SERVER_DESCRIPTION_CHANGED = 'ServerDescriptionChanged'.freeze # Server opening topic. # - # @since 2.3.0 + # @since 2.4.0 SERVER_OPENING = 'ServerOpening'.freeze # Topology changed topic. # - # @since 2.3.0 + # @since 2.4.0 TOPOLOGY_CHANGED = 'TopologyChanged'.freeze # Topology closed topic. # - # @since 2.3.0 + # @since 2.4.0 TOPOLOGY_CLOSED = 'TopologyClosed'.freeze # Topology opening topic. # - # @since 2.3.0 + # @since 2.4.0 TOPOLOGY_OPENING = 'TopologyOpening'.freeze @@operation_id = 0 diff --git a/lib/mongo/monitoring/event/server_closed.rb b/lib/mongo/monitoring/event/server_closed.rb index 6313bec99a..3433ee3884 100644 --- a/lib/mongo/monitoring/event/server_closed.rb +++ b/lib/mongo/monitoring/event/server_closed.rb @@ -18,7 +18,7 @@ module Event # Event fired when the server is closed. # - # @since 2.3.0 + # @since 2.4.0 class ServerClosed # @return [ Address ] address The server address. @@ -35,7 +35,7 @@ class ServerClosed # @param [ Address ] address The server address. # @param [ Integer ] topology The topology. # - # @since 2.3.0 + # @since 2.4.0 def initialize(address, topology) @address = address @topology = topology diff --git a/lib/mongo/monitoring/event/server_description_changed.rb b/lib/mongo/monitoring/event/server_description_changed.rb index 99f4945c2d..e2a0fe891b 100644 --- a/lib/mongo/monitoring/event/server_description_changed.rb +++ b/lib/mongo/monitoring/event/server_description_changed.rb @@ -18,7 +18,7 @@ module Event # Event fired when a server's description changes. # - # @since 2.3.0 + # @since 2.4.0 class ServerDescriptionChanged # @return [ Address ] address The server address. @@ -45,7 +45,7 @@ class ServerDescriptionChanged # @param [ Server::Description ] previous_description The previous description. # @param [ Server::Description ] new_description The new description. # - # @since 2.3.0 + # @since 2.4.0 def initialize(address, topology, previous_description, new_description) @address = address @topology = topology diff --git a/lib/mongo/monitoring/event/server_opening.rb b/lib/mongo/monitoring/event/server_opening.rb index 721f139d82..061905c824 100644 --- a/lib/mongo/monitoring/event/server_opening.rb +++ b/lib/mongo/monitoring/event/server_opening.rb @@ -18,7 +18,7 @@ module Event # Event fired when the server is opening. # - # @since 2.3.0 + # @since 2.4.0 class ServerOpening # @return [ Address ] address The server address. @@ -35,7 +35,7 @@ class ServerOpening # @param [ Address ] address The server address. # @param [ Integer ] topology The topology. # - # @since 2.3.0 + # @since 2.4.0 def initialize(address, topology) @address = address @topology = topology diff --git a/lib/mongo/monitoring/event/topology_changed.rb b/lib/mongo/monitoring/event/topology_changed.rb index e415718db7..8f8154b4a7 100644 --- a/lib/mongo/monitoring/event/topology_changed.rb +++ b/lib/mongo/monitoring/event/topology_changed.rb @@ -18,7 +18,7 @@ module Event # Event fired when the topology changes. # - # @since 2.3.0 + # @since 2.4.0 class TopologyChanged # @return [ Cluster::Topology ] previous_topology The previous topology. @@ -35,7 +35,7 @@ class TopologyChanged # @param [ Cluster::Topology ] previous_topology The previous topology. # @param [ Cluster::Topology ] new_topology The new topology. # - # @since 2.3.0 + # @since 2.4.0 def initialize(previous_topology, new_topology) @previous_topology = previous_topology @new_topology = new_topology diff --git a/lib/mongo/monitoring/event/topology_closed.rb b/lib/mongo/monitoring/event/topology_closed.rb index 4c5970af8e..4cd0483e9a 100644 --- a/lib/mongo/monitoring/event/topology_closed.rb +++ b/lib/mongo/monitoring/event/topology_closed.rb @@ -18,7 +18,7 @@ module Event # Event fired when the topology closes. # - # @since 2.3.0 + # @since 2.4.0 class TopologyClosed # @return [ Topology ] topology The topology. @@ -31,7 +31,7 @@ class TopologyClosed # # @param [ Integer ] topology The topology. # - # @since 2.3.0 + # @since 2.4.0 def initialize(topology) @topology = topology end diff --git a/lib/mongo/monitoring/event/topology_opening.rb b/lib/mongo/monitoring/event/topology_opening.rb index 643bd70a54..f16fdbec77 100644 --- a/lib/mongo/monitoring/event/topology_opening.rb +++ b/lib/mongo/monitoring/event/topology_opening.rb @@ -18,7 +18,7 @@ module Event # Event fired when the topology is opening. # - # @since 2.3.0 + # @since 2.4.0 class TopologyOpening # @return [ Topology ] topology The topology. @@ -31,7 +31,7 @@ class TopologyOpening # # @param [ Integer ] topology The topology. # - # @since 2.3.0 + # @since 2.4.0 def initialize(topology) @topology = topology end diff --git a/lib/mongo/monitoring/sdam_log_subscriber.rb b/lib/mongo/monitoring/sdam_log_subscriber.rb index 494ed35adc..f37282801c 100644 --- a/lib/mongo/monitoring/sdam_log_subscriber.rb +++ b/lib/mongo/monitoring/sdam_log_subscriber.rb @@ -17,7 +17,7 @@ class Monitoring # Subscribes to SDAM events and logs them. # - # @since 2.3.0 + # @since 2.4.0 class SDAMLogSubscriber include Loggable @@ -33,7 +33,7 @@ class SDAMLogSubscriber # # @option options [ Logger ] :logger An optional custom logger. # - # @since 2.1.0 + # @since 2.4.0 def initialize(options = {}) @options = options end @@ -45,7 +45,7 @@ def initialize(options = {}) # # @param [ Event ] event The event. # - # @since 2.3.0 + # @since 2.4.0 def succeeded(event) log_event(event) if logger.debug? end diff --git a/lib/mongo/monitoring/server_closed_log_subscriber.rb b/lib/mongo/monitoring/server_closed_log_subscriber.rb index 350d5030d0..b45657c94f 100644 --- a/lib/mongo/monitoring/server_closed_log_subscriber.rb +++ b/lib/mongo/monitoring/server_closed_log_subscriber.rb @@ -17,7 +17,7 @@ class Monitoring # Subscribes to Server Closed events and logs them. # - # @since 2.3.0 + # @since 2.4.0 class ServerClosedLogSubscriber < SDAMLogSubscriber private diff --git a/lib/mongo/monitoring/server_description_changed_log_subscriber.rb b/lib/mongo/monitoring/server_description_changed_log_subscriber.rb index e31bac0a26..b9c9548583 100644 --- a/lib/mongo/monitoring/server_description_changed_log_subscriber.rb +++ b/lib/mongo/monitoring/server_description_changed_log_subscriber.rb @@ -17,7 +17,7 @@ class Monitoring # Subscribes to Server Description Changed events and logs them. # - # @since 2.3.0 + # @since 2.4.0 class ServerDescriptionChangedLogSubscriber < SDAMLogSubscriber private diff --git a/lib/mongo/monitoring/server_opening_log_subscriber.rb b/lib/mongo/monitoring/server_opening_log_subscriber.rb index 9c4849da73..871adb2604 100644 --- a/lib/mongo/monitoring/server_opening_log_subscriber.rb +++ b/lib/mongo/monitoring/server_opening_log_subscriber.rb @@ -17,7 +17,7 @@ class Monitoring # Subscribes to Server Opening events and logs them. # - # @since 2.3.0 + # @since 2.4.0 class ServerOpeningLogSubscriber < SDAMLogSubscriber private diff --git a/lib/mongo/monitoring/topology_changed_log_subscriber.rb b/lib/mongo/monitoring/topology_changed_log_subscriber.rb index 48ca30a93e..71b545ae0b 100644 --- a/lib/mongo/monitoring/topology_changed_log_subscriber.rb +++ b/lib/mongo/monitoring/topology_changed_log_subscriber.rb @@ -17,7 +17,7 @@ class Monitoring # Subscribes to Topology Changed events and logs them. # - # @since 2.3.0 + # @since 2.4.0 class TopologyChangedLogSubscriber < SDAMLogSubscriber private diff --git a/lib/mongo/monitoring/topology_opening_log_subscriber.rb b/lib/mongo/monitoring/topology_opening_log_subscriber.rb index 29bb8e1871..3514bd6668 100644 --- a/lib/mongo/monitoring/topology_opening_log_subscriber.rb +++ b/lib/mongo/monitoring/topology_opening_log_subscriber.rb @@ -17,7 +17,7 @@ class Monitoring # Subscribes to Topology Openeing events and logs them. # - # @since 2.3.0 + # @since 2.4.0 class TopologyOpeningLogSubscriber < SDAMLogSubscriber private diff --git a/lib/mongo/server/description.rb b/lib/mongo/server/description.rb index 031f0d2757..f87110b9cf 100644 --- a/lib/mongo/server/description.rb +++ b/lib/mongo/server/description.rb @@ -487,7 +487,7 @@ def secondary? # # @return [ Symbol ] The server type. # - # @since 2.3.0 + # @since 2.4.0 def server_type return :arbiter if arbiter? return :ghost if ghost? diff --git a/lib/mongo/server/description/inspector.rb b/lib/mongo/server/description/inspector.rb index 068dc3b843..aa6803e3aa 100644 --- a/lib/mongo/server/description/inspector.rb +++ b/lib/mongo/server/description/inspector.rb @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +require 'mongo/server/description/inspector/member_discovered' require 'mongo/server/description/inspector/primary_elected' require 'mongo/server/description/inspector/description_changed' require 'mongo/server/description/inspector/standalone_discovered' @@ -34,7 +35,7 @@ class Inspector INSPECTORS = [ Inspector::StandaloneDiscovered, Inspector::DescriptionChanged, - Inspector::PrimaryElected + Inspector::MemberDiscovered ].freeze # @return [ Array ] inspectors The description inspectors. diff --git a/lib/mongo/server/description/inspector/description_changed.rb b/lib/mongo/server/description/inspector/description_changed.rb index 3f81da42bd..8af3f8a2e5 100644 --- a/lib/mongo/server/description/inspector/description_changed.rb +++ b/lib/mongo/server/description/inspector/description_changed.rb @@ -46,7 +46,7 @@ def initialize(event_listeners) # # @since 2.0.0 def run(description, updated) - unless description == updated + unless (description.config.empty? && updated.config.empty?) || (description == updated) publish(Event::DESCRIPTION_CHANGED, description, updated) end end diff --git a/lib/mongo/server/description/inspector/member_discovered.rb b/lib/mongo/server/description/inspector/member_discovered.rb new file mode 100644 index 0000000000..d1a63546af --- /dev/null +++ b/lib/mongo/server/description/inspector/member_discovered.rb @@ -0,0 +1,59 @@ +# Copyright (C) 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 + class Server + class Description + class Inspector + + # Handles inspecting the result of an ismaster command to check if this + # a server is a member of a known topology. + # + # @since 2.4.0 + class MemberDiscovered + include Event::Publisher + + # Instantiate the member discovered inspection. + # + # @example Instantiate the inspection. + # MemberDiscovered.new(listeners) + # + # @param [ Event::Listeners ] event_listeners The event listeners. + # + # @since 2.4.0 + def initialize(event_listeners) + @event_listeners = event_listeners + end + + # Run the member discovered inspection. + # + # @example Run the inspection. + # MemberDiscovered.run(description, {}) + # + # @param [ Description ] description The server description. + # @param [ Description ] updated The updated description. + # + # @since 2.4.0 + def run(description, updated) + if (!description.primary? && updated.primary?) || + (!description.mongos? && updated.mongos?) || + (description.unknown? && !updated.unknown?) + publish(Event::MEMBER_DISCOVERED, description, updated) + end + end + end + end + end + end +end diff --git a/lib/mongo/server_selector/selectable.rb b/lib/mongo/server_selector/selectable.rb index 7a67ce5159..d421faee53 100644 --- a/lib/mongo/server_selector/selectable.rb +++ b/lib/mongo/server_selector/selectable.rb @@ -166,8 +166,16 @@ def local_threshold @local_threshold ||= (options[:local_threshold] || ServerSelector::LOCAL_THRESHOLD) end - private - + # 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.4.0 def candidates(cluster) if cluster.single? cluster.servers.each { |server| validate_max_staleness_support!(server) } @@ -179,6 +187,8 @@ def candidates(cluster) end end + private + # 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 ef9404dacc..b033989519 100644 --- a/spec/mongo/address/unix_spec.rb +++ b/spec/mongo/address/unix_spec.rb @@ -24,7 +24,7 @@ end end - pending '#socket' do + describe '#socket' do let(:address) do '/tmp/mongodb-27017.sock' diff --git a/spec/mongo/auth/cr_spec.rb b/spec/mongo/auth/cr_spec.rb index 5ce95ca9c5..4c79c8fa7c 100644 --- a/spec/mongo/auth/cr_spec.rb +++ b/spec/mongo/auth/cr_spec.rb @@ -15,7 +15,8 @@ end let(:cluster) do - double('cluster', topology: topology).tap do |cl| + double('cluster').tap do |cl| + allow(cl).to receive(:topology).and_return(topology) allow(cl).to receive(:app_metadata).and_return(app_metadata) end end diff --git a/spec/mongo/auth/ldap_spec.rb b/spec/mongo/auth/ldap_spec.rb index 402e885a99..885fcfa424 100644 --- a/spec/mongo/auth/ldap_spec.rb +++ b/spec/mongo/auth/ldap_spec.rb @@ -15,9 +15,11 @@ end let(:cluster) do - double('cluster', topology: topology).tap do |cl| + double('cluster').tap do |cl| + allow(cl).to receive(:topology).and_return(topology) allow(cl).to receive(:app_metadata).and_return(app_metadata) end + end let(:topology) do double('topology') diff --git a/spec/mongo/auth/scram_spec.rb b/spec/mongo/auth/scram_spec.rb index d5c346ad21..22a522e717 100644 --- a/spec/mongo/auth/scram_spec.rb +++ b/spec/mongo/auth/scram_spec.rb @@ -15,11 +15,16 @@ end let(:cluster) do - double('cluster', topology: topology).tap do |cl| + double('cluster').tap do |cl| + allow(cl).to receive(:topology).and_return(topology) allow(cl).to receive(:app_metadata).and_return(app_metadata) end end + let(:topology) do + double('topology') + end + let(:server) do Mongo::Server.new(address, cluster, monitoring, listeners, TEST_OPTIONS) end diff --git a/spec/mongo/auth/x509_spec.rb b/spec/mongo/auth/x509_spec.rb index 3c09cd4e3b..1852da0c36 100644 --- a/spec/mongo/auth/x509_spec.rb +++ b/spec/mongo/auth/x509_spec.rb @@ -15,9 +15,11 @@ end let(:cluster) do - double('cluster', topology: topology).tap do |cl| + double('cluster').tap do |cl| + allow(cl).to receive(:topology).and_return(topology) allow(cl).to receive(:app_metadata).and_return(app_metadata) end + end let(:topology) do double('topology') diff --git a/spec/mongo/cluster/topology/replica_set_spec.rb b/spec/mongo/cluster/topology/replica_set_spec.rb index aef1b25481..c80b83e5d0 100644 --- a/spec/mongo/cluster/topology/replica_set_spec.rb +++ b/spec/mongo/cluster/topology/replica_set_spec.rb @@ -14,12 +14,9 @@ Mongo::Monitoring.new(monitoring: false) end - let(:cluster) do - double('cluster', topology: topology) - end - let(:cluster) do double('cluster').tap do |cl| + allow(cl).to receive(:topology).and_return(topology) allow(cl).to receive(:app_metadata).and_return(app_metadata) end end @@ -124,7 +121,7 @@ end let(:cluster) do - double('cluster', servers: servers, single?: false, sharded?: false) + double('cluster', servers: servers, single?: false, sharded?: false, unknown?: false) end context 'when the read preference is primary' do @@ -165,7 +162,7 @@ context 'when a primary exists' do let(:servers) do - [ double('server', primary?: true) ] + [ double('server', primary?: true, secondary?: false) ] end it 'returns true' do @@ -194,7 +191,7 @@ context 'when a secondary exists' do let(:servers) do - [ double('server', secondary?: true, average_round_trip_time: 0.01) ] + [ double('server', primary?: false, secondary?: true, average_round_trip_time: 0.01) ] end it 'returns true' do @@ -205,7 +202,7 @@ context 'when a secondary does not exist' do let(:servers) do - [ double('server', secondary?: false) ] + [ double('server', primary?: true, secondary?: false) ] end it 'returns false' do diff --git a/spec/mongo/cluster/topology/sharded_spec.rb b/spec/mongo/cluster/topology/sharded_spec.rb index 3ca34c169f..fd1995d9ac 100644 --- a/spec/mongo/cluster/topology/sharded_spec.rb +++ b/spec/mongo/cluster/topology/sharded_spec.rb @@ -19,7 +19,8 @@ end let(:cluster) do - double('cluster', topology: topology).tap do |cl| + double('cluster').tap do |cl| + allow(cl).to receive(:topology).and_return(topology) allow(cl).to receive(:app_metadata).and_return(app_metadata) end end diff --git a/spec/mongo/cluster/topology/single_spec.rb b/spec/mongo/cluster/topology/single_spec.rb index 9d5c07a50f..1f10a0cc94 100644 --- a/spec/mongo/cluster/topology/single_spec.rb +++ b/spec/mongo/cluster/topology/single_spec.rb @@ -19,8 +19,9 @@ end let(:cluster) do - double('cluster', topology: topology).tap do |cl| + double('cluster').tap do |cl| allow(cl).to receive(:app_metadata).and_return(app_metadata) + allow(cl).to receive(:topology).and_return(topology) end end diff --git a/spec/mongo/cluster_spec.rb b/spec/mongo/cluster_spec.rb index 987ec5f1b1..be69ed2347 100644 --- a/spec/mongo/cluster_spec.rb +++ b/spec/mongo/cluster_spec.rb @@ -344,6 +344,7 @@ let(:topology) do double('topology').tap do |t| allow(t).to receive(:add_hosts?).and_return(true) + allow(t).to receive(:changed!).and_return(true) end end @@ -358,6 +359,7 @@ let(:topology) do double('topology').tap do |t| allow(t).to receive(:add_hosts?).and_return(false) + allow(t).to receive(:changed!).and_return(true) end end @@ -406,6 +408,7 @@ double('topology').tap do |t| allow(t).to receive(:remove_hosts?).and_return(true) allow(t).to receive(:remove_server?).and_return(true) + allow(t).to receive(:changed!).and_return(true) end end @@ -426,6 +429,7 @@ double('topology').tap do |t| allow(t).to receive(:remove_hosts?).and_return(true) allow(t).to receive(:remove_server?).and_return(false) + allow(t).to receive(:changed!).and_return(true) end end @@ -446,6 +450,7 @@ let(:topology) do double('topology').tap do |t| allow(t).to receive(:remove_hosts?).and_return(false) + allow(t).to receive(:changed!).and_return(true) end end diff --git a/spec/mongo/max_staleness_spec.rb b/spec/mongo/max_staleness_spec.rb index 78fd012faa..71ebf5ef05 100644 --- a/spec/mongo/max_staleness_spec.rb +++ b/spec/mongo/max_staleness_spec.rb @@ -11,7 +11,11 @@ context(spec.description) do let(:topology) do - spec.type.new({}) + spec.type.new({}, monitoring, []) + end + + let(:monitoring) do + Mongo::Monitoring.new(monitoring: false) end let(:monitoring) do diff --git a/spec/mongo/sdam_monitoring_spec.rb b/spec/mongo/sdam_monitoring_spec.rb index e8f6134e27..060015a434 100644 --- a/spec/mongo/sdam_monitoring_spec.rb +++ b/spec/mongo/sdam_monitoring_spec.rb @@ -10,49 +10,20 @@ 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) + cl = Mongo::Client.new([]) + @client = cl.with(heartbeat_frequency: 100, connect_timeout: 0.1) + cl.close + @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) + @client.send(:create_from_uri, 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| @@ -60,17 +31,7 @@ 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 + phase.responses.each do |response| # For each response in the phase, we need to change that server's # description. server = find_server(@client, response.address) @@ -91,6 +52,7 @@ class Server it "expects a #{expectation.name} to be fired" do fired_event = @subscriber.first_event(expectation.name) + expect(fired_event).not_to be_nil expect(fired_event).to match_sdam_monitoring_event(expectation) end end diff --git a/spec/mongo/server_discovery_and_monitoring_spec.rb b/spec/mongo/sdam_spec.rb similarity index 50% rename from spec/mongo/server_discovery_and_monitoring_spec.rb rename to spec/mongo/sdam_spec.rb index 0aaca3fd44..cfb6a2b36e 100644 --- a/spec/mongo/server_discovery_and_monitoring_spec.rb +++ b/spec/mongo/sdam_spec.rb @@ -10,67 +10,30 @@ 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) + c = Mongo::Client.new(spec.uri_string) + @client = c.with(connect_timeout: 0.1) + c.close 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. + before(:all) do + phase.responses.each do |response| 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 + 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) @@ -79,7 +42,7 @@ class Server let(:cluster_addresses) do @client.cluster.instance_variable_get(:@servers). - collect(&:address).collect(&:to_s).uniq.sort + collect(&:address).collect(&:to_s).uniq.sort end let(:phase_addresses) do diff --git a/spec/mongo/server/connection_pool_spec.rb b/spec/mongo/server/connection_pool_spec.rb index ca86f195dd..03b27406d1 100644 --- a/spec/mongo/server/connection_pool_spec.rb +++ b/spec/mongo/server/connection_pool_spec.rb @@ -23,7 +23,8 @@ end let(:cluster) do - double('cluster', topology: topology).tap do |cl| + double('cluster').tap do |cl| + allow(cl).to receive(:topology).and_return(topology) allow(cl).to receive(:app_metadata).and_return(app_metadata) end end diff --git a/spec/mongo/server/connection_spec.rb b/spec/mongo/server/connection_spec.rb index 8f210cc029..35c0a214c8 100644 --- a/spec/mongo/server/connection_spec.rb +++ b/spec/mongo/server/connection_spec.rb @@ -19,9 +19,11 @@ end let(:cluster) do - double('cluster', topology: topology).tap do |cl| + double('cluster').tap do |cl| + allow(cl).to receive(:topology).and_return(topology) allow(cl).to receive(:app_metadata).and_return(app_metadata) end + end let(:topology) do double('topology') diff --git a/spec/mongo/server/description_spec.rb b/spec/mongo/server/description_spec.rb index 431e5fc932..7857b17802 100644 --- a/spec/mongo/server/description_spec.rb +++ b/spec/mongo/server/description_spec.rb @@ -38,13 +38,10 @@ let(:topology) do double('topology') end - - let(:cluster) do - double('cluster', topology: topology) - end - + let(:cluster) do double('cluster').tap do |cl| + allow(cl).to receive(:topology).and_return(topology) allow(cl).to receive(:app_metadata).and_return(app_metadata) end end diff --git a/spec/mongo/server_selector_spec.rb b/spec/mongo/server_selector_spec.rb index 711e467515..d79efa997e 100644 --- a/spec/mongo/server_selector_spec.rb +++ b/spec/mongo/server_selector_spec.rb @@ -150,6 +150,7 @@ let(:cluster) do double('cluster').tap do |c| + allow(c).to receive(:topology).and_return(topology) allow(c).to receive(:servers).and_return(servers) allow(c).to receive(:single?).and_return(false) allow(c).to receive(:sharded?).and_return(false) @@ -180,6 +181,7 @@ let(:cluster) do double('cluster').tap do |c| + allow(c).to receive(:topology).and_return(topology) allow(c).to receive(:servers).and_return(servers) allow(c).to receive(:single?).and_return(false) allow(c).to receive(:sharded?).and_return(false) @@ -222,6 +224,7 @@ let(:cluster) do double('cluster').tap do |c| + allow(c).to receive(:topology).and_return(topology) allow(c).to receive(:servers).and_return(servers) allow(c).to receive(:single?).and_return(false) allow(c).to receive(:sharded?).and_return(false) @@ -251,6 +254,7 @@ let(:cluster) do double('cluster').tap do |c| + allow(c).to receive(:topology).and_return(topology) allow(c).to receive(:servers).and_return(servers) allow(c).to receive(:single?).and_return(single) allow(c).to receive(:sharded?).and_return(sharded) diff --git a/spec/mongo/server_spec.rb b/spec/mongo/server_spec.rb index 701a5ffc0f..c172bfaa49 100644 --- a/spec/mongo/server_spec.rb +++ b/spec/mongo/server_spec.rb @@ -7,7 +7,8 @@ end let(:cluster) do - double('cluster', topology: topology).tap do |cl| + double('cluster').tap do |cl| + allow(cl).to receive(:topology).and_return(topology) allow(cl).to receive(:app_metadata).and_return(app_metadata) end end diff --git a/spec/mongo/socket/unix_spec.rb b/spec/mongo/socket/unix_spec.rb index 8d9b511f20..d3b72e49c0 100644 --- a/spec/mongo/socket/unix_spec.rb +++ b/spec/mongo/socket/unix_spec.rb @@ -16,7 +16,7 @@ socket.close end - pending 'connects to the server' do + it 'connects to the server' do expect(socket).to be_alive end end @@ -33,7 +33,7 @@ socket.close end - pending 'returns true' do + it 'returns true' do expect(socket).to be_alive end end @@ -44,7 +44,7 @@ socket.close end - pending 'raises error' do + it 'raises error' do expect { socket.alive? }.to raise_error(IOError) end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 8a8f6f48fd..55e0bfa158 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,7 +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") +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") MAX_STALENESS_TESTS = Dir.glob("#{CURRENT_PATH}/support/max_staleness/**/*.yml") diff --git a/spec/support/sdam_monitoring.rb b/spec/support/sdam_monitoring.rb index 27fa739973..5614da8716 100644 --- a/spec/support/sdam_monitoring.rb +++ b/spec/support/sdam_monitoring.rb @@ -88,7 +88,7 @@ def description_matches?(actual, expected) def topology_matches?(actual, expected) case expected['topologyType'] when 'ReplicaSetWithPrimary' then actual.replica_set? - when 'ReplicaSetNoPrimary' then actual.replica_set? + when 'ReplicaSetNoPrimary' then (actual.replica_set? || actual.unknown?) when 'Sharded' then actual.sharded? when 'Single' then actual.single? when 'Unknown' then actual.unknown? @@ -98,12 +98,12 @@ def topology_matches?(actual, expected) # Test subscriber for SDAM monitoring. # - # @since 2.3.0 + # @since 2.4.0 class TestSubscriber # The mappings of event names to types. # - # @since 2.3.0 + # @since 2.4.0 MAPPINGS = { 'topology_opening_event' => Mongo::Monitoring::Event::TopologyOpening, 'topology_description_changed_event' => Mongo::Monitoring::Event::TopologyChanged, @@ -116,7 +116,7 @@ class TestSubscriber # # @param [ Event ] event The event. # - # @since 2.3.0 + # @since 2.4.0 def succeeded(event) events.push(event) end @@ -130,7 +130,7 @@ def first_event(name) matching = events.find do |event| event.class == MAPPINGS[name] end - events.delete(events.find_index(matching)) + events.delete(matching) matching end diff --git a/spec/support/sdam_monitoring/replica_set_with_no_primary.yml b/spec/support/sdam_monitoring/replica_set_with_no_primary.yml new file mode 100644 index 0000000000..ed992f849c --- /dev/null +++ b/spec/support/sdam_monitoring/replica_set_with_no_primary.yml @@ -0,0 +1,112 @@ +description: "Monitoring a topology that is a replica set with no primary connected" +uri: "mongodb://a,b" +phases: + - + responses: + - + - "a:27017" + - + ok: 1 + ismaster: false + secondary: true + setName: "rs" + setVersion: 1 + primary: "b:27017" + hosts: + - "a:27017" + - "b:27017" + minWireVersion: 0 + maxWireVersion: 4 + 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" + address: "a:27017" + - + server_opening_event: + topologyId: "42" + address: "b:27017" + - + server_description_changed_event: + topologyId: "42" + address: "a:27017" + previousDescription: + address: "a:27017" + arbiters: [] + hosts: [] + passives: [] + type: "Unknown" + newDescription: + address: "a:27017" + arbiters: [] + hosts: + - "a:27017" + - "b:27017" + passives: [] + primary: "b:27017" + setName: "rs" + type: "RSSecondary" + - + topology_description_changed_event: + topologyId: "42" + previousDescription: + topologyType: "Unknown" + servers: + - + address: "a:27017" + arbiters: [] + hosts: [] + passives: [] + type: "Unknown" + - + address: "b:27017" + arbiters: [] + hosts: [] + passives: [] + type: "Unknown" + newDescription: + topologyType: "ReplicaSetNoPrimary" + setName: "rs" + servers: + - + address: "a:27017" + arbiters: [] + hosts: + - "a:27017" + - "b:27017" + passives: [] + primary: "b:27017" + setName: "rs" + type: "RSSecondary" + - + address: "b:27017" + arbiters: [] + hosts: [] + passives: [] + type: "PossiblePrimary" + diff --git a/spec/support/sdam_monitoring/replica_set_with_primary.yml b/spec/support/sdam_monitoring/replica_set_with_primary.yml new file mode 100644 index 0000000000..e42735d41c --- /dev/null +++ b/spec/support/sdam_monitoring/replica_set_with_primary.yml @@ -0,0 +1,111 @@ +description: "Monitoring a topology that is a replica set with a primary connected" +uri: "mongodb://a,b" +phases: + - + responses: + - + - "a:27017" + - + ok: 1 + ismaster: true + setName: "rs" + setVersion: 1 + primary: "a:27017" + hosts: + - "a:27017" + - "b:27017" + minWireVersion: 0 + maxWireVersion: 4 + 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" + address: "a:27017" + - + server_opening_event: + topologyId: "42" + address: "b:27017" + - + server_description_changed_event: + topologyId: "42" + address: "a:27017" + previousDescription: + address: "a:27017" + arbiters: [] + hosts: [] + passives: [] + type: "Unknown" + newDescription: + address: "a:27017" + arbiters: [] + hosts: + - "a:27017" + - "b:27017" + passives: [] + primary: "a:27017" + setName: "rs" + type: "RSPrimary" + - + topology_description_changed_event: + topologyId: "42" + previousDescription: + topologyType: "Unknown" + servers: + - + address: "a:27017" + arbiters: [] + hosts: [] + passives: [] + type: "Unknown" + - + address: "b:27017" + arbiters: [] + hosts: [] + passives: [] + type: "Unknown" + newDescription: + topologyType: "ReplicaSetWithPrimary" + setName: "rs" + servers: + - + address: "a:27017" + arbiters: [] + hosts: + - "a:27017" + - "b:27017" + passives: [] + primary: "a:27017" + setName: "rs" + type: "RSPrimary" + - + address: "b:27017" + arbiters: [] + hosts: [] + passives: [] + type: "Unknown" + diff --git a/spec/support/sdam_monitoring/replica_set_with_removal.yml b/spec/support/sdam_monitoring/replica_set_with_removal.yml index 203902c4ea..e156ec7100 100644 --- a/spec/support/sdam_monitoring/replica_set_with_removal.yml +++ b/spec/support/sdam_monitoring/replica_set_with_removal.yml @@ -23,6 +23,27 @@ 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" diff --git a/spec/support/sdam_monitoring/required_replica_set.yml b/spec/support/sdam_monitoring/required_replica_set.yml new file mode 100644 index 0000000000..d809d80327 --- /dev/null +++ b/spec/support/sdam_monitoring/required_replica_set.yml @@ -0,0 +1,84 @@ +description: "Monitoring a topology that is required to be a replica set" +uri: "mongodb://a,b/?replicaSet=rs" +phases: + - + responses: + - + - "a:27017" + - { + ok: 1, + ismaster: true, + setName: "rs", + setVersion: 1.0, + primary: "a:27017", + hosts: [ "a:27017", "b:27017" ], + minWireVersion: 0, + maxWireVersion: 4 + } + 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: "a:27017" + arbiters: [] + hosts: [] + passives: [] + type: "Unknown" + newDescription: + address: "a:27017" + arbiters: [] + hosts: [ "a:27017", "b:27017" ] + passives: [] + primary: "a:27017" + setName: "rs" + type: "RSPrimary" + - + topology_description_changed_event: + topologyId: "42" + previousDescription: + topologyType: "ReplicaSetNoPrimary" + servers: + - + address: "a:27017" + arbiters: [] + hosts: [] + passives: [] + type: "Unknown" + - + address: "b:27017" + arbiters: [] + hosts: [] + passives: [] + type: "Unknown" + newDescription: + topologyType: "ReplicaSetWithPrimary" + setName: "rs" + servers: + - + address: "a:27017" + arbiters: [] + hosts: [ "a:27017", "b:27017" ] + passives: [] + primary: "a:27017" + setName: "rs" + type: "RSPrimary" + - + address: "b:27017" + arbiters: [] + hosts: [] + passives: [] + type: "Unknown" diff --git a/spec/support/sdam_monitoring/standalone.yml b/spec/support/sdam_monitoring/standalone.yml index a9bd9d2f68..aff3f7322c 100644 --- a/spec/support/sdam_monitoring/standalone.yml +++ b/spec/support/sdam_monitoring/standalone.yml @@ -12,6 +12,21 @@ phases: - topology_opening_event: topologyId: "42" + - + topology_description_changed_event: + topologyId: "42" + previousDescription: + topologyType: "Unknown" + servers: [] + newDescription: + topologyType: "Single" + servers: + - + address: "a:27017" + arbiters: [] + hosts: [] + passives: [] + type: "Unknown" - server_opening_event: topologyId: "42" @@ -36,7 +51,7 @@ phases: topology_description_changed_event: topologyId: "42" previousDescription: - topologyType: "Unknown" + topologyType: "Single" servers: - address: "a:27017" diff --git a/spec/support/shared/server_selector.rb b/spec/support/shared/server_selector.rb index 6b5d063ee7..ade4f5f90f 100644 --- a/spec/support/shared/server_selector.rb +++ b/spec/support/shared/server_selector.rb @@ -35,6 +35,12 @@ def make_server(mode, options = {}) let(:secondary) { make_server(:secondary) } let(:options) { { :mode => name, :tag_sets => tag_sets, max_staleness: max_staleness } } let(:selector) { described_class.new(options) } + let(:monitoring) do + Mongo::Monitoring.new(monitoring: false) + end + let(:topology) do + double('topology') + end before(:all) do module Mongo From 6a7e360707554fcb5a2ce036d38d4e25d935d0ba Mon Sep 17 00:00:00 2001 From: Emily Date: Fri, 25 Nov 2016 17:38:44 +0100 Subject: [PATCH 25/34] RUBY-1096 Update #has_readable_server? and #has_writable_server? implementations --- lib/mongo/cluster.rb | 2 +- lib/mongo/cluster/topology/replica_set.rb | 4 +- lib/mongo/cluster/topology/sharded.rb | 2 +- lib/mongo/cluster/topology/single.rb | 8 +- lib/mongo/cluster/topology/unknown.rb | 2 +- .../cluster/topology/replica_set_spec.rb | 25 ++++++ spec/mongo/cluster/topology/single_spec.rb | 84 +++---------------- spec/mongo/cluster_spec.rb | 4 +- 8 files changed, 47 insertions(+), 84 deletions(-) diff --git a/lib/mongo/cluster.rb b/lib/mongo/cluster.rb index 5781ea4cb7..0b4ec86aa7 100644 --- a/lib/mongo/cluster.rb +++ b/lib/mongo/cluster.rb @@ -114,7 +114,7 @@ def add(host) # @return [ true, false ] If a readable server is present. # # @since 2.4.0 - def has_readable_server?(server_selector) + def has_readable_server?(server_selector = nil) topology.has_readable_server?(self, server_selector) end diff --git a/lib/mongo/cluster/topology/replica_set.rb b/lib/mongo/cluster/topology/replica_set.rb index 0cb6f34dfe..3d66d96218 100644 --- a/lib/mongo/cluster/topology/replica_set.rb +++ b/lib/mongo/cluster/topology/replica_set.rb @@ -95,8 +95,8 @@ def elect_primary(description, servers) # @return [ true, false ] If a readable server is present. # # @since 2.4.0 - def has_readable_server?(cluster, server_selector) - server_selector.candidates(cluster).any? + def has_readable_server?(cluster, server_selector = nil) + (server_selector || ServerSelector.get(mode: :primary)).candidates(cluster).any? end # Determine if the topology would select a writable server for the diff --git a/lib/mongo/cluster/topology/sharded.rb b/lib/mongo/cluster/topology/sharded.rb index 31dad10b93..c95cfedff8 100644 --- a/lib/mongo/cluster/topology/sharded.rb +++ b/lib/mongo/cluster/topology/sharded.rb @@ -71,7 +71,7 @@ def elect_primary(description, servers); self; end # @return [ true, false ] If a readable server is present. # # @since 2.4.0 - def has_readable_server?(cluster, server_selector); true; end + def has_readable_server?(cluster, server_selector = nil); true; end # Determine if the topology would select a writable server for the # provided candidates. diff --git a/lib/mongo/cluster/topology/single.rb b/lib/mongo/cluster/topology/single.rb index cf7fa1d9a1..4995cd54b4 100644 --- a/lib/mongo/cluster/topology/single.rb +++ b/lib/mongo/cluster/topology/single.rb @@ -74,9 +74,7 @@ def elect_primary(description, servers); self; end # @return [ true, false ] If a readable server is present. # # @since 2.4.0 - def has_readable_server?(cluster, server_selector) - server_selector.candidates(cluster).any? - end + def has_readable_server?(cluster, server_selector = nil); true; end # Determine if the topology would select a writable server for the # provided candidates. @@ -89,9 +87,7 @@ def has_readable_server?(cluster, server_selector) # @return [ true, false ] If a writable server is present. # # @since 2.4.0 - def has_writable_server?(cluster) - cluster.servers.any?{ |server| server.primary? } - end + def has_writable_server?(cluster); true; end # Initialize the topology with the options. # diff --git a/lib/mongo/cluster/topology/unknown.rb b/lib/mongo/cluster/topology/unknown.rb index 3c29466b83..6189977da6 100644 --- a/lib/mongo/cluster/topology/unknown.rb +++ b/lib/mongo/cluster/topology/unknown.rb @@ -80,7 +80,7 @@ def elect_primary(description, servers) # @return [ true, false ] If a readable server is present. # # @since 2.4.0 - def has_readable_server?(cluster, server_selector); false; end + def has_readable_server?(cluster, server_selector = nil); false; end # Determine if the topology would select a writable server for the # provided candidates. diff --git a/spec/mongo/cluster/topology/replica_set_spec.rb b/spec/mongo/cluster/topology/replica_set_spec.rb index c80b83e5d0..48b84d6044 100644 --- a/spec/mongo/cluster/topology/replica_set_spec.rb +++ b/spec/mongo/cluster/topology/replica_set_spec.rb @@ -254,6 +254,31 @@ expect(topology).to have_readable_server(cluster, selector) end end + + context 'when the read preference is not provided' do + + context 'when a primary exists' do + + let(:servers) do + [ double('server', primary?: true, secondary?: false) ] + end + + it 'returns true' do + expect(topology).to have_readable_server(cluster) + 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 false' do + expect(topology).to_not have_readable_server(cluster) + end + end + end end describe '#has_writable_servers?' do diff --git a/spec/mongo/cluster/topology/single_spec.rb b/spec/mongo/cluster/topology/single_spec.rb index 1f10a0cc94..42d02f89e1 100644 --- a/spec/mongo/cluster/topology/single_spec.rb +++ b/spec/mongo/cluster/topology/single_spec.rb @@ -94,89 +94,31 @@ describe '#has_readable_servers?' do - 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 + let(:server) do + double('server', :primary? => true) 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 + let(:cluster) do + double('cluster', servers: [server], single?: true) 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 + it 'returns true' do + expect(topology).to have_readable_server(cluster) end end describe '#has_writable_servers?' do - context 'when the server is a primary' do - - let(:server) do - double('server', :primary? => true) - end - - let(:cluster) do - double('cluster', servers: [ server ]) - end - - it 'returns true' do - expect(topology).to have_writable_server(cluster) - end + let(:server) do + double('server', :primary? => true) end - context 'when the server is not a primary (e.g. direct connect to secondary)' do - - let(:server) do - double('server', :primary? => false) - end - - let(:cluster) do - double('cluster', servers: [ server ]) - end + let(:cluster) do + double('cluster', servers: [server], single?: true) + end - it 'returns false' do - expect(topology).to_not have_writable_server(cluster) - end + it 'returns true' do + expect(topology).to have_writable_server(cluster) end end diff --git a/spec/mongo/cluster_spec.rb b/spec/mongo/cluster_spec.rb index be69ed2347..3365c42f86 100644 --- a/spec/mongo/cluster_spec.rb +++ b/spec/mongo/cluster_spec.rb @@ -66,14 +66,14 @@ end it 'delegates to the topology' do - expect(cluster).to have_readable_server(selector) + expect(cluster.has_readable_server?).to eq(cluster.topology.has_readable_server?(cluster)) end end describe '#has_writable_server?' do it 'delegates to the topology' do - expect(cluster).to_not have_writable_server + expect(cluster.has_writable_server?).to eq(cluster.topology.has_writable_server?(cluster)) end end From 1344ba6a94d384d49b99abc8f1742a2f544e40cb Mon Sep 17 00:00:00 2001 From: Emily Date: Mon, 28 Nov 2016 11:48:05 +0100 Subject: [PATCH 26/34] RUBY-1096 Change montioring message to say members changed in topology --- .../monitoring/topology_changed_log_subscriber.rb | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/mongo/monitoring/topology_changed_log_subscriber.rb b/lib/mongo/monitoring/topology_changed_log_subscriber.rb index 71b545ae0b..7de49413ff 100644 --- a/lib/mongo/monitoring/topology_changed_log_subscriber.rb +++ b/lib/mongo/monitoring/topology_changed_log_subscriber.rb @@ -23,10 +23,17 @@ class TopologyChangedLogSubscriber < SDAMLogSubscriber private def log_event(event) - log_debug( - "Topology type '#{event.previous_topology.display_name.downcase}' changed to " + - "type '#{event.new_topology.display_name.downcase}'." - ) + if event.previous_topology != event.new_topology + log_debug( + "Topology type '#{event.previous_topology.display_name.downcase}' changed to " + + "type '#{event.new_topology.display_name.downcase}'." + ) + else + log_debug( + "There was a change in the members of the '#{event.new_topology.display_name.downcase}' " + + "topology." + ) + end end end end From 8b673c42d632b7d2961acd5a9af3f136cadddb03 Mon Sep 17 00:00:00 2001 From: Emily Date: Mon, 28 Nov 2016 12:02:20 +0100 Subject: [PATCH 27/34] RUBY-1096 Remove extra candidates method definition --- lib/mongo/server_selector/selectable.rb | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/lib/mongo/server_selector/selectable.rb b/lib/mongo/server_selector/selectable.rb index d421faee53..0baa35463e 100644 --- a/lib/mongo/server_selector/selectable.rb +++ b/lib/mongo/server_selector/selectable.rb @@ -48,26 +48,6 @@ def ==(other) max_staleness == other.max_staleness 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. @@ -166,7 +146,7 @@ def local_threshold @local_threshold ||= (options[:local_threshold] || ServerSelector::LOCAL_THRESHOLD) end - # Get the potential candidates to selecto from the cluster. + # Get the potential candidates to select from the cluster. # # @example Get the server candidates. # selectable.candidates(cluster) From 7b89da3fae7691df73acfa4fedcfd7bd95e0386f Mon Sep 17 00:00:00 2001 From: Emily Date: Mon, 28 Nov 2016 12:07:28 +0100 Subject: [PATCH 28/34] RUBY-1096 Single always returns true for has_readable_server? and has_writable_server? --- spec/mongo/cluster/topology/single_spec.rb | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/spec/mongo/cluster/topology/single_spec.rb b/spec/mongo/cluster/topology/single_spec.rb index 42d02f89e1..701d0e76c9 100644 --- a/spec/mongo/cluster/topology/single_spec.rb +++ b/spec/mongo/cluster/topology/single_spec.rb @@ -94,31 +94,15 @@ describe '#has_readable_servers?' do - let(:server) do - double('server', :primary? => true) - end - - let(:cluster) do - double('cluster', servers: [server], single?: true) - end - it 'returns true' do - expect(topology).to have_readable_server(cluster) + expect(topology).to have_readable_server(nil, nil) end end describe '#has_writable_servers?' do - let(:server) do - double('server', :primary? => true) - end - - let(:cluster) do - double('cluster', servers: [server], single?: true) - end - it 'returns true' do - expect(topology).to have_writable_server(cluster) + expect(topology).to have_writable_server(nil) end end From 11ee31edbbde53ae7bdd5772c6e5510609d2a8b0 Mon Sep 17 00:00:00 2001 From: Emily Date: Mon, 28 Nov 2016 12:35:25 +0100 Subject: [PATCH 29/34] RUBY-1096 Clean up spec --- spec/mongo/sdam_monitoring_spec.rb | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/spec/mongo/sdam_monitoring_spec.rb b/spec/mongo/sdam_monitoring_spec.rb index 060015a434..5ea58d99f4 100644 --- a/spec/mongo/sdam_monitoring_spec.rb +++ b/spec/mongo/sdam_monitoring_spec.rb @@ -10,9 +10,7 @@ context(spec.description) do before(:all) do - cl = Mongo::Client.new([]) - @client = cl.with(heartbeat_frequency: 100, connect_timeout: 0.1) - cl.close + @client = Mongo::Client.new([], heartbeat_frequency: 100, connect_timeout: 0.1) @subscriber = Mongo::SDAMMonitoring::TestSubscriber.new @client.subscribe(Mongo::Monitoring::SERVER_OPENING, @subscriber) @client.subscribe(Mongo::Monitoring::SERVER_CLOSED, @subscriber) @@ -32,16 +30,15 @@ before(:all) do phase.responses.each do |response| - # For each response in the phase, we need to change that server's - # description. + # 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 + server ||= Mongo::Server.new( + Mongo::Address.new(response.address), + @client.cluster, + @client.instance_variable_get(:@monitoring), + @client.cluster.send(:event_listeners), + @client.cluster.options + ) monitor = server.instance_variable_get(:@monitor) description = monitor.inspector.run(server.description, response.ismaster, 0.5) monitor.instance_variable_set(:@description, description) From 9fe5a5ae439595ee9e804c648deb7317b63f90e0 Mon Sep 17 00:00:00 2001 From: Emily Date: Mon, 28 Nov 2016 12:38:24 +0100 Subject: [PATCH 30/34] RUBY-1096 Clean up SDAM spec --- spec/mongo/sdam_spec.rb | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/spec/mongo/sdam_spec.rb b/spec/mongo/sdam_spec.rb index cfb6a2b36e..30ff357a58 100644 --- a/spec/mongo/sdam_spec.rb +++ b/spec/mongo/sdam_spec.rb @@ -10,9 +10,9 @@ context(spec.description) do before(:all) do - c = Mongo::Client.new(spec.uri_string) - @client = c.with(connect_timeout: 0.1) - c.close + Mongo::Client.new(spec.uri_string).tap do |client| + @client = client.with(connect_timeout: 0.1) + end.close end after(:all) do @@ -26,14 +26,13 @@ before(:all) do phase.responses.each do |response| server = find_server(@client, response.address) - server = Mongo::Server.new( + 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) From a7fa87d7043eb2b91b8cecade1bcc7e07376ae59 Mon Sep 17 00:00:00 2001 From: Emily Date: Mon, 28 Nov 2016 12:45:56 +0100 Subject: [PATCH 31/34] RUBY-1096 Small typo fixes and spec cleanup --- lib/mongo/cluster/topology/sharded.rb | 4 ++-- lib/mongo/cluster/topology/single.rb | 4 ++-- lib/mongo/cluster/topology/unknown.rb | 4 ++-- lib/mongo/event/description_changed.rb | 2 +- lib/mongo/event/member_discovered.rb | 4 ++-- lib/mongo/event/primary_elected.rb | 2 +- lib/mongo/event/standalone_discovered.rb | 2 +- lib/mongo/server/description.rb | 6 ++++-- .../server/description/inspector/description_changed.rb | 2 +- spec/mongo/cluster_spec.rb | 5 ----- 10 files changed, 16 insertions(+), 19 deletions(-) diff --git a/lib/mongo/cluster/topology/sharded.rb b/lib/mongo/cluster/topology/sharded.rb index c95cfedff8..d607f5f4a1 100644 --- a/lib/mongo/cluster/topology/sharded.rb +++ b/lib/mongo/cluster/topology/sharded.rb @@ -68,7 +68,7 @@ def elect_primary(description, servers); self; end # @param [ ServerSelector ] server_selector The server # selector. # - # @return [ true, false ] If a readable server is present. + # @return [ true ] A Sharded cluster always has a readable server. # # @since 2.4.0 def has_readable_server?(cluster, server_selector = nil); true; end @@ -81,7 +81,7 @@ def has_readable_server?(cluster, server_selector = nil); true; end # # @param [ Cluster ] cluster The cluster. # - # @return [ true, false ] If a writable server is present. + # @return [ true ] A Sharded cluster always has a writable server. # # @since 2.4.0 def has_writable_server?(cluster); true; end diff --git a/lib/mongo/cluster/topology/single.rb b/lib/mongo/cluster/topology/single.rb index 4995cd54b4..a26576d16c 100644 --- a/lib/mongo/cluster/topology/single.rb +++ b/lib/mongo/cluster/topology/single.rb @@ -71,7 +71,7 @@ def elect_primary(description, servers); self; end # @param [ ServerSelector ] server_selector The server # selector. # - # @return [ true, false ] If a readable server is present. + # @return [ true ] A standalone always has a readable server. # # @since 2.4.0 def has_readable_server?(cluster, server_selector = nil); true; end @@ -84,7 +84,7 @@ def has_readable_server?(cluster, server_selector = nil); true; end # # @param [ Cluster ] cluster The cluster. # - # @return [ true, false ] If a writable server is present. + # @return [ true ] A standalone always has a writable server. # # @since 2.4.0 def has_writable_server?(cluster); true; end diff --git a/lib/mongo/cluster/topology/unknown.rb b/lib/mongo/cluster/topology/unknown.rb index 6189977da6..3d7dfc50ef 100644 --- a/lib/mongo/cluster/topology/unknown.rb +++ b/lib/mongo/cluster/topology/unknown.rb @@ -77,7 +77,7 @@ def elect_primary(description, servers) # @param [ ServerSelector ] server_selector The server # selector. # - # @return [ true, false ] If a readable server is present. + # @return [ false ] An Unknown topology will never have a readable server. # # @since 2.4.0 def has_readable_server?(cluster, server_selector = nil); false; end @@ -90,7 +90,7 @@ def has_readable_server?(cluster, server_selector = nil); false; end # # @param [ Cluster ] cluster The cluster. # - # @return [ true, false ] If a writable server is present. + # @return [ false ] An Unknown topology will never have a writable server. # # @since 2.4.0 def has_writable_server?(cluster); false; end diff --git a/lib/mongo/event/description_changed.rb b/lib/mongo/event/description_changed.rb index bd79e0ee79..005f4c1cbf 100644 --- a/lib/mongo/event/description_changed.rb +++ b/lib/mongo/event/description_changed.rb @@ -22,7 +22,7 @@ module Event class DescriptionChanged include Monitoring::Publishable - # @return [ Mongo::Cluster ] cluster The event publisher. + # @return [ Mongo::Cluster ] cluster The cluster. attr_reader :cluster # @return [ Hash ] options The options. diff --git a/lib/mongo/event/member_discovered.rb b/lib/mongo/event/member_discovered.rb index 03a294e7f1..800907bef8 100644 --- a/lib/mongo/event/member_discovered.rb +++ b/lib/mongo/event/member_discovered.rb @@ -21,7 +21,7 @@ module Event class MemberDiscovered include Monitoring::Publishable - # @return [ Mongo::Cluster ] cluster The event publisher. + # @return [ Mongo::Cluster ] cluster The cluster. attr_reader :cluster # @return [ Hash ] options The options. @@ -44,7 +44,7 @@ def initialize(cluster) @monitoring = cluster.monitoring end - # This event tells the cluster that a member of a known topology is discovered. + # This event tells the cluster that a member of a topology is discovered. # # @example Handle the event. # member_discovered.handle(previous_description, description) diff --git a/lib/mongo/event/primary_elected.rb b/lib/mongo/event/primary_elected.rb index f1141205c2..9fd7825973 100644 --- a/lib/mongo/event/primary_elected.rb +++ b/lib/mongo/event/primary_elected.rb @@ -20,7 +20,7 @@ module Event # @since 2.0.0 class PrimaryElected - # @return [ Mongo::Cluster ] cluster The event publisher. + # @return [ Mongo::Cluster ] cluster The cluster. attr_reader :cluster # Initialize the new primary elected event handler. diff --git a/lib/mongo/event/standalone_discovered.rb b/lib/mongo/event/standalone_discovered.rb index 9b87ffd349..fc6057d22e 100644 --- a/lib/mongo/event/standalone_discovered.rb +++ b/lib/mongo/event/standalone_discovered.rb @@ -20,7 +20,7 @@ module Event # @since 2.0.6 class StandaloneDiscovered - # @return [ Mongo::Cluster ] cluster The event publisher. + # @return [ Mongo::Cluster ] cluster The cluster. attr_reader :cluster # Initialize the new standalone discovered event handler. diff --git a/lib/mongo/server/description.rb b/lib/mongo/server/description.rb index f87110b9cf..3d4305ae32 100644 --- a/lib/mongo/server/description.rb +++ b/lib/mongo/server/description.rb @@ -519,7 +519,8 @@ def standalone? # # @since 2.0.0 def unknown? - config.empty? || config[Operation::Result::OK] != 1 + config.empty? || (config[Operation::Result::OK] && + config[Operation::Result::OK] != 1) end # A result from another server's ismaster command before this server has @@ -610,6 +611,7 @@ def me_mismatch? # @since 2.0.6 def ==(other) return false if self.class != other.class + return false if unknown? || other.unknown? compare_config(other) end alias_method :eql?, :== @@ -617,7 +619,7 @@ def ==(other) private def compare_config(other) - !config.keys.empty? && config.keys.all? do |k| + config.keys.all? do |k| config[k] == other.config[k] || EXCLUDE_FOR_COMPARISON.include?(k) end end diff --git a/lib/mongo/server/description/inspector/description_changed.rb b/lib/mongo/server/description/inspector/description_changed.rb index 8af3f8a2e5..5a78f31548 100644 --- a/lib/mongo/server/description/inspector/description_changed.rb +++ b/lib/mongo/server/description/inspector/description_changed.rb @@ -46,7 +46,7 @@ def initialize(event_listeners) # # @since 2.0.0 def run(description, updated) - unless (description.config.empty? && updated.config.empty?) || (description == updated) + unless (description.unknown? && updated.unknown?) || (description == updated) publish(Event::DESCRIPTION_CHANGED, description, updated) end end diff --git a/spec/mongo/cluster_spec.rb b/spec/mongo/cluster_spec.rb index 3365c42f86..30743cf4f6 100644 --- a/spec/mongo/cluster_spec.rb +++ b/spec/mongo/cluster_spec.rb @@ -344,7 +344,6 @@ let(:topology) do double('topology').tap do |t| allow(t).to receive(:add_hosts?).and_return(true) - allow(t).to receive(:changed!).and_return(true) end end @@ -359,7 +358,6 @@ let(:topology) do double('topology').tap do |t| allow(t).to receive(:add_hosts?).and_return(false) - allow(t).to receive(:changed!).and_return(true) end end @@ -408,7 +406,6 @@ double('topology').tap do |t| allow(t).to receive(:remove_hosts?).and_return(true) allow(t).to receive(:remove_server?).and_return(true) - allow(t).to receive(:changed!).and_return(true) end end @@ -429,7 +426,6 @@ double('topology').tap do |t| allow(t).to receive(:remove_hosts?).and_return(true) allow(t).to receive(:remove_server?).and_return(false) - allow(t).to receive(:changed!).and_return(true) end end @@ -450,7 +446,6 @@ let(:topology) do double('topology').tap do |t| allow(t).to receive(:remove_hosts?).and_return(false) - allow(t).to receive(:changed!).and_return(true) end end From d6bf77ffe7520d0fb3c198d5d340dda12c50ce30 Mon Sep 17 00:00:00 2001 From: Emily Date: Mon, 28 Nov 2016 12:51:04 +0100 Subject: [PATCH 32/34] RUBY-1096 Deprecate PrimaryElected event in favor of MemberDiscovered event --- lib/mongo/event.rb | 2 ++ lib/mongo/event/primary_elected.rb | 2 ++ lib/mongo/server/description/inspector.rb | 1 + lib/mongo/server/description/inspector/primary_elected.rb | 2 ++ 4 files changed, 7 insertions(+) diff --git a/lib/mongo/event.rb b/lib/mongo/event.rb index d916bd9de1..5648b5380c 100644 --- a/lib/mongo/event.rb +++ b/lib/mongo/event.rb @@ -31,6 +31,8 @@ module Event # When a server is elected primary. # # @since 2.0.0 + # + # @deprecated. Will be removed in 3.0 PRIMARY_ELECTED = 'primary_elected'.freeze # When a server is discovered to be a member of a topology. diff --git a/lib/mongo/event/primary_elected.rb b/lib/mongo/event/primary_elected.rb index 9fd7825973..cd2eb93ce4 100644 --- a/lib/mongo/event/primary_elected.rb +++ b/lib/mongo/event/primary_elected.rb @@ -18,6 +18,8 @@ module Event # This handles primary elected events for server descriptions. # # @since 2.0.0 + # + # @deprecated. Will be removed in 3.0 class PrimaryElected # @return [ Mongo::Cluster ] cluster The cluster. diff --git a/lib/mongo/server/description/inspector.rb b/lib/mongo/server/description/inspector.rb index aa6803e3aa..58fa9e6d6e 100644 --- a/lib/mongo/server/description/inspector.rb +++ b/lib/mongo/server/description/inspector.rb @@ -13,6 +13,7 @@ # limitations under the License. require 'mongo/server/description/inspector/member_discovered' +# @deprecated. Will be removed in 3.0 require 'mongo/server/description/inspector/primary_elected' require 'mongo/server/description/inspector/description_changed' require 'mongo/server/description/inspector/standalone_discovered' diff --git a/lib/mongo/server/description/inspector/primary_elected.rb b/lib/mongo/server/description/inspector/primary_elected.rb index 6d77085e43..4ef6cf5a8f 100644 --- a/lib/mongo/server/description/inspector/primary_elected.rb +++ b/lib/mongo/server/description/inspector/primary_elected.rb @@ -21,6 +21,8 @@ class Inspector # server was elected primary. # # @since 2.0.0 + # + # @deprecated. Will be removed in 3.0 class PrimaryElected include Event::Publisher From dc53a0798280aa4b9933b0ab19c736348a818d43 Mon Sep 17 00:00:00 2001 From: Emily Date: Tue, 29 Nov 2016 14:03:19 +0100 Subject: [PATCH 33/34] RUBY-1096 Increase heartbeat_frequency on client used in sdam test --- spec/mongo/sdam_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/mongo/sdam_spec.rb b/spec/mongo/sdam_spec.rb index 30ff357a58..b068585711 100644 --- a/spec/mongo/sdam_spec.rb +++ b/spec/mongo/sdam_spec.rb @@ -11,7 +11,7 @@ before(:all) do Mongo::Client.new(spec.uri_string).tap do |client| - @client = client.with(connect_timeout: 0.1) + @client = client.with(connect_timeout: 0.1, heartbeat_frequency: 100) end.close end From 004c871899ad79c0833bedb983d82b4c21fc0058 Mon Sep 17 00:00:00 2001 From: Emily Date: Tue, 29 Nov 2016 16:24:13 +0100 Subject: [PATCH 34/34] RUBY-1096 Don't refer to localhost:27017 in SDAM test, as there probably is a server running on that host:port --- spec/support/sdam/rs/primary_mismatched_me.yml | 4 ++-- spec/support/sdam/rs/secondary_mismatched_me.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/support/sdam/rs/primary_mismatched_me.yml b/spec/support/sdam/rs/primary_mismatched_me.yml index 71773c9c20..9b99fbd645 100644 --- a/spec/support/sdam/rs/primary_mismatched_me.yml +++ b/spec/support/sdam/rs/primary_mismatched_me.yml @@ -18,7 +18,7 @@ }, "responses": [ [ - "localhost:27017", + "lhost:27017", { "me": "a:27017", "hosts": [ @@ -33,5 +33,5 @@ ] } ], - "uri": "mongodb://localhost:27017/?replicaSet=rs" + "uri": "mongodb://lhost:27017/?replicaSet=rs" } diff --git a/spec/support/sdam/rs/secondary_mismatched_me.yml b/spec/support/sdam/rs/secondary_mismatched_me.yml index 60059a7f79..f9e693344c 100644 --- a/spec/support/sdam/rs/secondary_mismatched_me.yml +++ b/spec/support/sdam/rs/secondary_mismatched_me.yml @@ -18,7 +18,7 @@ }, "responses": [ [ - "localhost:27017", + "lhost:27017", { "me": "a:27017", "hosts": [ @@ -33,5 +33,5 @@ ] } ], - "uri": "mongodb://localhost:27017/?replicaSet=rs" + "uri": "mongodb://lhost:27017/?replicaSet=rs" }