From c8d16b7511969edbde58bc49ef44e0ff63e5cb8f Mon Sep 17 00:00:00 2001 From: Patrick J Cherry Date: Tue, 17 Apr 2012 11:14:35 +0100 Subject: * DuringRunner#now? caches its answer, indexed by time. The cache is renewed on reinitialization. * AlertGroup#notify now initialises and keeps track of each DuringRunner to ensure cache usage. --- lib/mauve/alert_group.rb | 41 +++++++++++++++----- lib/mauve/configuration_builders/alert_group.rb | 6 +-- lib/mauve/notification.rb | 34 +++++++++++++---- test/tc_mauve_notification.rb | 51 +++++++++++++++++++++++++ 4 files changed, 112 insertions(+), 20 deletions(-) diff --git a/lib/mauve/alert_group.rb b/lib/mauve/alert_group.rb index aa47ef6..31d9cc8 100644 --- a/lib/mauve/alert_group.rb +++ b/lib/mauve/alert_group.rb @@ -153,16 +153,39 @@ module Mauve return false end + during_runners = [] + # # This is where we set the reminder -- i.e. on a per-alert-group basis. - # - remind_at = notifications.inject(nil) do |reminder_time, notification| - this_time = notification.remind_at_next(alert, at) - if reminder_time.nil? or (!this_time.nil? and reminder_time > this_time) - this_time - else - reminder_time - end + + remind_at = nil + notifications.each do |notification| + # + # Create a new during_runner for this notification clause, and keep it + # handy. + # + during_runner = DuringRunner.new(at, alert, ¬ification.during) + during_runners << during_runner + + # + # Work out the next reminder time + # + this_remind_at = notification.remind_at_next(alert, during_runner) + + # + # Skip this one if no reminder time can be found + # + next if this_remind_at.nil? + + # + # Set the next reminder time if we've not had one already. + # + remind_at = this_remind_at if remind_at.nil? + + # + # We need the next soonest reminder time. + # + remind_at = this_remind_at if remind_at > this_remind_at end # @@ -186,7 +209,7 @@ module Mauve # sent_to = [] notifications.each do |notification| - sent_to << notification.notify(alert, sent_to, at) + sent_to << notification.notify(alert, sent_to, during_runners.shift) end return (sent_to.length > 0) diff --git a/lib/mauve/configuration_builders/alert_group.rb b/lib/mauve/configuration_builders/alert_group.rb index c652de8..56c446d 100644 --- a/lib/mauve/configuration_builders/alert_group.rb +++ b/lib/mauve/configuration_builders/alert_group.rb @@ -20,15 +20,15 @@ module Mauve # @return [Mauve::Notification] New notification instance. def builder_setup(*who) who = who.map do |username| - #raise BuildException.new("You haven't declared who #{username} is") unless - # @context.people[username] - #@context.people[username] if @context.people[username] @context.people[username] + elsif @context.people_lists[username] @context.people_lists[username] + else raise ArgumentError.new("You have not declared who #{username} is") + end end @result = Mauve::Notification.new(who, @context.last_alert_group.level) diff --git a/lib/mauve/notification.rb b/lib/mauve/notification.rb index a9795f3..522f9fa 100644 --- a/lib/mauve/notification.rb +++ b/lib/mauve/notification.rb @@ -42,6 +42,7 @@ module Mauve @alert = alert @during = during || Proc.new { true } @logger = Log4r::Logger.new "Mauve::DuringRunner" + @now_cache = Hash.new end # This evaluates the +during+ block, returning the result. @@ -49,8 +50,21 @@ module Mauve # @param [Time] t Set the time at which to evaluate the +during+ block. # @return [Boolean] def now?(t=@time) + # + # Use the cache if we've already worked out what happens at time t. + # + return @now_cache[t] if @now_cache.has_key?(t) + + # + # Store the test time in an instance variable so the test knows what time + # we're testing against. + # @test_time = t - instance_eval(&@during) ? true : false + + # + # Store the answer in our cache and return. + # + @now_cache[t] = (instance_eval(&@during) ? true : false) end # This finds the next occurance of the +during+ block evaluating to true. @@ -195,15 +209,18 @@ module Mauve # @param [Array] already_sent_to A list of people that have already received this alert. # # @return [Array] The list of people that have received this alert. - def notify(alert, already_sent_to = [], at=Time.now) + def notify(alert, already_sent_to = [], during_runner = nil) if people.nil? or people.empty? logger.warn "No people found in for notification #{list}" return end + # Set up a during_runner + during_runner ||= DuringRunner.new(Time.now, self.alert, &self.during) + # Should we notify at all? - return already_sent_to unless DuringRunner.new(at, alert, &during).now? + return already_sent_to unless during_runner.now? people.collect do |person| case person @@ -236,15 +253,16 @@ module Mauve # @param [Mauve::Alert] alert The alert in question # @return [Time or nil] The time a reminder should get sent, or nil if it # should never get sent again. - def remind_at_next(alert, at = Time.now) + def remind_at_next(alert, during_runner = nil) return nil unless alert.raised? - dr = DuringRunner.new(at, alert, &during) + # Set up a during_runner + during_runner ||= DuringRunner.new(Time.now, self.alert, &self.during) - if dr.now? - return dr.find_next(every) + if during_runner.now? + return during_runner.find_next(every) else - return dr.find_next() + return during_runner.find_next() end end diff --git a/test/tc_mauve_notification.rb b/test/tc_mauve_notification.rb index 0b3bc04..ec5eaf6 100644 --- a/test/tc_mauve_notification.rb +++ b/test/tc_mauve_notification.rb @@ -315,4 +315,55 @@ EOF end + # + # Test to make sure that if a bondary is crossed, then the during clauses all + # work. + # + def test_no_race_conditions_in_during + + config=< "test", + :source => "test", + :subject => "test" + ) + alert.raise! + + a = AlertChanged.first + assert_equal("urgent", a.level, "Level is wrong for #{a.person}") + assert_equal("raised", a.update_type, "Update type is wrong for #{a.person}") + + assert_equal(1, Server.instance.notification_buffer.size, "Wrong number of notifications sent") + end + + end -- cgit v1.2.1