4 # Condition Symbol :flapping
7 # Trigger when a Task transitions to or from a state or states a given number
8 # of times within a given period.
12 # +times+ is the number of times that the Task must transition before
14 # +within+ is the number of seconds within which the Task must transition
15 # the specified number of times before triggering. You may use
16 # the sugar methods #seconds, #minutes, #hours, #days to clarify
17 # your code (see examples).
19 # +from_state+ is the state (as a Symbol) from which the transition must occur.
20 # +to_state is the state (as a Symbol) to which the transition must occur.
23 # +retry_in+ is the number of seconds after which to re-monitor the Task after
24 # it has been disabled by the condition.
25 # +retry_times+ is the number of times after which to permanently unmonitor
27 # +retry_within+ is the number of seconds within which
32 class Flapping < TriggerCondition
42 self.info = "process is flapping"
46 @timeline = Timeline.new(self.times)
47 @retry_timeline = Timeline.new(self.retry_times)
52 valid &= complain("Attribute 'times' must be specified", self) if self.times.nil?
53 valid &= complain("Attribute 'within' must be specified", self) if self.within.nil?
54 valid &= complain("Attributes 'from_state', 'to_state', or both must be specified", self) if self.from_state.nil? && self.to_state.nil?
58 def process(event, payload)
60 if event == :state_change
61 event_from_state, event_to_state = *payload
63 from_state_match = !self.from_state || self.from_state && Array(self.from_state).include?(event_from_state)
64 to_state_match = !self.to_state || self.to_state && Array(self.to_state).include?(event_to_state)
66 if from_state_match && to_state_match
69 concensus = (@timeline.size == self.times)
70 duration = (@timeline.last - @timeline.first) < self.within
72 if concensus && duration
81 puts e.backtrace.join("\n")
89 @retry_timeline << Time.now
91 concensus = (@retry_timeline.size == self.retry_times)
92 duration = (@retry_timeline.last - @retry_timeline.first) < self.retry_within
94 if concensus && duration
100 msg = "#{self.watch.name} giving up"
101 applog(self.watch, :info, msg)
109 msg = "#{self.watch.name} auto-reenable monitoring in #{self.retry_in} seconds"
110 applog(self.watch, :info, msg)
115 msg = "#{self.watch.name} auto-reenabling monitoring"
116 applog(self.watch, :info, msg)
118 if self.watch.state == :unmonitored