diff --git a/lib/mongo/event.rb b/lib/mongo/event.rb index fc70f50d6f..b769fb9cba 100644 --- a/lib/mongo/event.rb +++ b/lib/mongo/event.rb @@ -14,18 +14,31 @@ require 'mongo/event/publisher' require 'mongo/event/subscriber' +require 'mongo/event/host_added' +require 'mongo/event/host_removed' module Mongo module Event - ARBITER_ADDED = "arbiter_added".freeze - ARBITER_REMOVED = "arbiter_removed".freeze + # When a server description has a new host added. + # + # @since 3.0.0 + HOST_ADDED = "host_added".freeze - SECONDARY_ADDED = "server_added".freeze - SECONDARY_REMOVED = "server_removed".freeze + # When a server description has a host removed. + # + # @since 3.0.0 + HOST_REMOVED = "host_removed".freeze - SERVER_PROMOTED = "server_promoted".freeze - SERVER_DEMOTED = "server_demoted".freeze + # When a server is to be added to a cluster. + # + # @since 3.0.0 + SERVER_ADDED = "server_added".freeze + + # When a server is to be removed from a cluster. + # + # @since 3.0.0 + SERVER_REMOVED = "server_removed".freeze end end diff --git a/lib/mongo/event/host_added.rb b/lib/mongo/event/host_added.rb new file mode 100644 index 0000000000..6d65519bb9 --- /dev/null +++ b/lib/mongo/event/host_added.rb @@ -0,0 +1,44 @@ +# Copyright (C) 2009-2014 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 host added events for server descriptions. + # + # @since 3.0.0 + class HostAdded + + # @return [ Mongo::Server ] server The event publisher. + attr_reader :server + + # Initialize the new host added event handler. + # + # @example Create the new handler. + # HostAdded.new(server) + # + # @param [ Mongo::Server ] server The server to publish from. + # + # @since 3.0.0 + def initialize(server) + @server = server + end + + def handle(address) + # @todo: Log the description change here. + server.publish(Event::SERVER_ADDED, address) + end + end + end +end diff --git a/lib/mongo/event/host_removed.rb b/lib/mongo/event/host_removed.rb new file mode 100644 index 0000000000..63f953cefb --- /dev/null +++ b/lib/mongo/event/host_removed.rb @@ -0,0 +1,44 @@ +# Copyright (C) 2009-2014 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 host removed events for server descriptions. + # + # @since 3.0.0 + class HostRemoved + + # @return [ Mongo::Server ] server The event publisher. + attr_reader :server + + # Initialize the new host removed event handler. + # + # @example Create the new handler. + # HostRemoved.new(server) + # + # @param [ Mongo::Server ] server The server to publish from. + # + # @since 3.0.0 + def initialize(server) + @server = server + end + + def handle(address) + # @todo: Log the description change here. + server.publish(Event::SERVER_REMOVED, address) + end + end + end +end diff --git a/lib/mongo/server.rb b/lib/mongo/server.rb index d895331f5a..50be023e00 100644 --- a/lib/mongo/server.rb +++ b/lib/mongo/server.rb @@ -24,6 +24,7 @@ module Mongo # @since 3.0.0 class Server include Event::Publisher + include Event::Subscriber # The default time for a server to refresh its status is 5 seconds. # @@ -52,6 +53,7 @@ def initialize(address, options = {}) @address = Address.new(address) @options = options @mutex = Mutex.new + initialize_description! @refresh = Refresh.new(self, refresh_interval) @refresh.run end @@ -60,15 +62,22 @@ def operable? true end + # Refresh the configuration for this server. Is thread-safe since the + # periodic refresh is invoked from another thread in order not to continue + # blocking operations on the current thread. + # + # @example Refresh the server. + # server.refresh! + # + # @note Is mutable in that the underlying server description can get + # mutated on this call. + # + # @return [ Server::Description ] The updated server description. + # + # @since 3.0.0 def refresh! mutex.synchronize do - # Update the server description here. For changes in the description to - # the previous we need to fire events. - # - # refreshed = Description.new(read(refresh_command)) - # - # publish(Event::SERVER_ADDED, address) - # publish(Event::SERVER_REMOVED, address) + description.update!(dispatch([ refresh_command ])) end end @@ -108,16 +117,23 @@ def refresh_interval private + def initialize_description! + # @description = Description.new(dispatch([ refresh_command ])) + # subscribe_to(description, Event::HOST_ADDED, Event::HostAdded.new(self)) + # subscribe_to(description, Event::HOST_REMOVED, Event::HostRemoved.new(self)) + end + def pool @pool ||= Pool.get(self) end + # @todo: Need to sort out read preference here. def refresh_command Protocol::Query.new( Database::ADMIN, Database::COMMAND, STATUS, - :limit => -1, :read => cluster.client.read_preference + :limit => -1 ) end diff --git a/lib/mongo/server/description.rb b/lib/mongo/server/description.rb index b861fc5136..99620d13b9 100644 --- a/lib/mongo/server/description.rb +++ b/lib/mongo/server/description.rb @@ -20,6 +20,7 @@ class Server # # @since 3.0.0 class Description + include Event::Publisher # Constant for reading arbiter info from config. # @@ -207,6 +208,41 @@ def secondary? def set_name config[SET_NAME] end + + # Update this description with a new description. Will fire the + # necessary events depending on what has changed from the old description + # to the new one. + # + # @example Update the description with the new config. + # description.update!({ "ismaster" => false }) + # + # @note This modifies the state of the description. + # + # @param [ Hash ] new_config The new configuration. + # + # @return [ Description ] The updated description. + # + # @since 3.0.0 + def update!(new_config) + find_new_servers(new_config) + find_removed_servers(new_config) + @config = new_config + self + end + + private + + def find_new_servers(new_config) + new_config[HOSTS].each do |host| + publish(Event::HOST_ADDED, host) unless hosts.include?(host) + end + end + + def find_removed_servers(new_config) + hosts.each do |host| + publish(Event::HOST_REMOVED, host) unless new_config[HOSTS].include?(host) + end + end end end end diff --git a/spec/mongo/client_spec.rb b/spec/mongo/client_spec.rb index ebec1f8434..3f4e10ce97 100644 --- a/spec/mongo/client_spec.rb +++ b/spec/mongo/client_spec.rb @@ -194,15 +194,12 @@ describe '#inspect' do let(:client) do - described_class.new( - ['1.0.0.1:2', '1.0.0.1:1'], - :read => :primary - ) + described_class.new(['127.0.0.1:27017'], :read => :primary) end it 'returns the cluster information' do expect(client.inspect).to eq( - "" + "" ) end end diff --git a/spec/mongo/event/host_added_spec.rb b/spec/mongo/event/host_added_spec.rb new file mode 100644 index 0000000000..1a6ad42d96 --- /dev/null +++ b/spec/mongo/event/host_added_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe Mongo::Event::HostAdded do + + describe '#handle' do + + let(:server) do + double('server') + end + + let(:handler) do + described_class.new(server) + end + + it 'publishes the event from the server' do + expect(server).to receive(:publish).with(Mongo::Event::SERVER_ADDED, 'test') + handler.handle('test') + end + end +end diff --git a/spec/mongo/event/host_removed_spec.rb b/spec/mongo/event/host_removed_spec.rb new file mode 100644 index 0000000000..59389a21b1 --- /dev/null +++ b/spec/mongo/event/host_removed_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe Mongo::Event::HostRemoved do + + describe '#handle' do + + let(:server) do + double('server') + end + + let(:handler) do + described_class.new(server) + end + + it 'publishes the event from the server' do + expect(server).to receive(:publish).with(Mongo::Event::SERVER_REMOVED, 'test') + handler.handle('test') + end + end +end diff --git a/spec/mongo/server/description_spec.rb b/spec/mongo/server/description_spec.rb index b95354c8b7..ca73abc340 100644 --- a/spec/mongo/server/description_spec.rb +++ b/spec/mongo/server/description_spec.rb @@ -8,14 +8,14 @@ 'ismaster' => true, 'secondary' => false, 'hosts' => [ - '127.0.0.1:27118', - '127.0.0.1:27119' + '127.0.0.1:27018', + '127.0.0.1:27019' ], 'arbiters' => [ '127.0.0.1:27120' ], - 'primary' => '127.0.0.1:27119', - 'me' => '127.0.0.1:27119', + 'primary' => '127.0.0.1:27019', + 'me' => '127.0.0.1:27019', 'maxBsonObjectSize' => 16777216, 'maxMessageSizeBytes' => 48000000, 'ok' => 1 @@ -104,7 +104,7 @@ end it 'returns all the hosts in the replica set' do - expect(description.hosts).to eq([ '127.0.0.1:27118', '127.0.0.1:27119' ]) + expect(description.hosts).to eq([ '127.0.0.1:27018', '127.0.0.1:27019' ]) end end @@ -229,4 +229,67 @@ end end end + + describe 'update!' do + + let(:config) do + { + 'ismaster' => true, + 'secondary' => false, + 'hosts' => [ '127.0.0.1:27018', '127.0.0.1:27019' ] + } + end + + let(:listener) do + double('listener') + end + + context 'when a server is added' do + + let(:new) do + { 'hosts' => [ '127.0.0.1:27019', '127.0.0.1:27020' ] } + end + + let(:description) do + described_class.new(config) + end + + let(:updated) do + description.update!(new) + end + + before do + description.add_listener(Mongo::Event::HOST_ADDED, listener) + end + + it 'fires a server added event' do + expect(listener).to receive(:handle).with('127.0.0.1:27020') + expect(updated.hosts).to eq([ '127.0.0.1:27019', '127.0.0.1:27020' ]) + end + end + + context 'when a server is removed' do + + let(:new) do + { 'hosts' => [ '127.0.0.1:27019', '127.0.0.1:27020' ] } + end + + let(:description) do + described_class.new(config) + end + + let(:updated) do + description.update!(new) + end + + before do + description.add_listener(Mongo::Event::HOST_REMOVED, listener) + end + + it 'fires a server added event' do + expect(listener).to receive(:handle).with('127.0.0.1:27018') + expect(updated.hosts).to eq([ '127.0.0.1:27019', '127.0.0.1:27020' ]) + end + end + end end