<commit_before>module Vector
  module Function
    class PredictiveScaling
      def initialize(options)
        @cloudwatch = options[:cloudwatch]
        @lookback_windows = options[:lookback_windows]
        @lookahead_window = options[:lookahead_window]
        @valid_threshold = options[:valid_threshold]
        @valid_period = options[:valid_period]
      end

      def run_for(group)
        return if @lookback_windows.length == 0

        group.scaling_policies.each do |policy|
          # only examine scaleup policies
          next if policy.scaling_adjustment <= 0

          policy.alarms.keys.each do |alarm_name|
            alarm = @cloudwatch.alarms[alarm_name]
            next unless alarm.enabled?

            now_load, now_num = load_for(group, alarm.metric,
              Time.now, @valid_period)

            if now_load.nil?
              puts "#{group.name}: could not get current load for metric #{alarm.metric.name}"
              next
            end

            @lookback_windows.each do |window|
              then_load, = load_for(group, alarm.metric,
                Time.now - window, @valid_period)

              if then_load.nil?
                puts "#{group.name}: could not get -#{window.inspect} load for metric #{alarm.metric.name}"
                next
              end

              # check that the past total utilization is within
              # threshold% of the current total utilization
              if !Vector.within_threshold(@valid_threshold,
                                          now_load, then_load)
                puts "#{group.name}: past load not within threshold (current #{now_load}, then #{then_load})"
                next
              end

              past_load, = load_for(group, alarm.metric,
                Time.now - window + @lookahead_window,
                alarm.period)

              if past_load.nil?
                puts "#{group.name}: could not get -#{window.inspect} +#{@lookahead_window.inspect} load for metric #{alarm.metric.name}"
                next
              end

              # now take the past total load and divide it by the
              # current number of instances to get the predicted avg
              # load
              predicted_load = past_load.to_f / now_num
              puts "#{group.name}: predicted load: #{predicted_load}"

              if check_alarm_threshold(alarm, predicted_load)
                puts "#{group.name}: executing policy #{policy.name}"
                policy.execute
              end
            end
          end
        end
      end

      protected

      def check_alarm_threshold(alarm, value)
        case alarm.comparison_operator
        when "GreaterThanOrEqualToThreshold"
          value >= alarm.threshold
        when "GreaterThanThreshold"
          value > alarm.threshold
        when "LessThanThreshold"
          value < alarm.threshold
        when "LessThanOrEqualToThreshold"
          value <= alarm.threshold
        end
      end<commit_msg>add logging to predictive scaling<commit_after>module Vector
  module Function
    class PredictiveScaling
      include Vector::HLogger

      def initialize(options)
        @cloudwatch = options[:cloudwatch]
        @lookback_windows = options[:lookback_windows]
        @lookahead_window = options[:lookahead_window]
        @valid_threshold = options[:valid_threshold]
        @valid_period = options[:valid_period]
      end

      def run_for(group)
        hlog_ctx "group: #{group.name}" do
          return if @lookback_windows.length == 0

          scaleup_policies = group.scaling_policies.select do |policy|
            policy.scaling_adjustment > 0
          end

          scaleup_policies.each do |policy|
            hlog_ctx "policy: #{policy.name}" do

              policy.alarms.keys.each do |alarm_name|
                alarm = @cloudwatch.alarms[alarm_name]
                hlog_ctx "alarm: #{alarm.name} (metric #{alarm.metric.name})" do

                  unless alarm.enabled?
                    hlog "Skipping disabled alarm"
                    next
                  end

                  now_load, now_num = load_for(group, alarm.metric,
                    Time.now, @valid_period)

                  if now_load.nil?
                    hlog "Could not get current load for metric"
                    next
                  end

                  @lookback_windows.each do |window|
                    hlog_ctx "window: #{window.inspect}" do
                      then_load, = load_for(group, alarm.metric,
                        Time.now - window, @valid_period)

                      if then_load.nil?
                        hlog "Could not get past load for metric"
                        next
                      end

                      # check that the past total utilization is within
                      # threshold% of the current total utilization
                      if @valid_threshold &&
                        !Vector.within_threshold(@valid_threshold, now_load, then_load)
                        hlog "Past load not within threshold (current #{now_load}, then #{then_load})"
                        next
                      end

                      past_load, = load_for(group, alarm.metric,
                        Time.now - window + @lookahead_window,
                        alarm.period)

                      if past_load.nil?
                        hlog "Could not get past + #{@lookahead_window.inspect} load for metric"
                        next
                      end

                      # now take the past total load and divide it by the
                      # current number of instances to get the predicted avg
                      # load
                      predicted_load = past_load.to_f / now_num
                      hlog "Predicted load: #{predicted_load}"

                      if check_alarm_threshold(alarm, predicted_load)
                        hlog "Executing policy"
                        policy.execute(honor_cooldown: true)

                        # don't need to evaluate further windows or policies on this group
                        return
                      end
                    end
                  end
                end
              end
            end
          end
        end
      end

      protected

      def check_alarm_threshold(alarm, value)
        case alarm.comparison_operator
        when "GreaterThanOrEqualToThreshold"
          value >= alarm.threshold
        when "GreaterThanThreshold"
          value > alarm.threshold
        when "LessThanThreshold"
          value < alarm.threshold
        when "LessThanOrEqualToThreshold"
          value <= alarm.threshold
        end
      end
{'fraction_non_alphanumeric': 0.06712997190595889, 'fraction_numerical': 0.0005914534969688008, 'mean_word_length': 1.4313443565780015, 'pattern_counts': {'":': 0, '<': 8, '<?xml version=': 0, '>': 8, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '965552', 'n_tokens_mistral': 1703, 'n_tokens_neox': 1605, 'n_words': 449}