class FlynnAutoScale::Scaler

Public Instance Methods

auto_scale(process='web') click to toggle source

START Auto Scaling Section

# File lib/flynn_auto_scale.rb, line 75
def auto_scale(process='web')
  # This method will utilize the current RAM usage and some ENV variables to make a scaling decision.
  if !ENV['FLYNN_AUTO_SCALE'].present?
    Logger.warn "Flynn Auto-Scale: A call was made to the auto_scale method but the FLYNN_AUTO_SCALE ENV variable is not set. Nothing will be done."
    return
  end
  
  current_ram_use = (OS.rss_bytes / 1024).to_i
  max_ram_use = ENV['FLYNN_AUTO_SCALE_RAM'].present? ? ENV['FLYNN_AUTO_SCALE_RAM'].present?.to_i : 256
  
  if current_ram_use > max_ram_use
    # Consider scaling upwards.
    scale_up(process)
  elsif current_ram_use < (max_ram_use * 0.1).to_i
    # This is a hardcoded attempt at a "downscale".
    # We will attempt to shrink the instance count if using less than 10% of the max ram.
    scale_down(process)
  else
    # There is no need to execute a scale up / down event.
    return
  end
end
can_scale_time(process='web') click to toggle source

START Maintenance Section

# File lib/flynn_auto_scale.rb, line 100
def can_scale_time(process='web')
  # Checks if scaling is appropriate or if it's limited by a time minimum.
  last_scale = FlynnAutoScale::ScalingEvent.where(process_type: process).last
  scaling_restriction = ENV['FLYNN_AUTO_SCALE_COOLDOWN'].present?
  
  if scaling_restriction
    if !last_scale
      # If this is the first time this process is scaled, just ignore the limit and push an initial scaling event into the DB.
      last_scale = FlynnAutoScale::ScalingEvent.create(process_type: process, event_type: 'initial', instances: 1)
      true
    else
      # Calculate if we have breached the cooldown.
      current_time = DateTime.now
      last_time = last_scale.created_at
      diff = (current_time.to_f - last_time.to_f).to_i
      
      # Scaling operations should occur if the cooldown has elapsed.
      if diff > ENV['FLYNN_AUTO_SCALE_COOLDOWN'].to_i
        true
      else
        false
      end
    end
  else
    # No limit, just scale.
    true
  end
end
check_install() click to toggle source

START Setup Section

# File lib/flynn_auto_scale.rb, line 7
def check_install
  # Check for existence of Flynn inside container.
  output = `flynn`
  if output.include? "usage: flynn"
    true
  else
    false
  end
end
connect_cluster() click to toggle source
# File lib/flynn_auto_scale.rb, line 28
def connect_cluster
  # Connect to the cluster and set as default.
  if !install
    # Warn the user something has gone terribly wrong.
    Logger.warn "Flynn Auto-Scale: There was an issue installing / verifying the Flynn installation. Auto Scaling functions will not work."
    false
  else
    # The bare minimum needed to connect to a cluster and manual scaling
    vars = [
    ENV['FLYNN_SETUP_CLUSTER_PIN'],
    ENV['FLYNN_SETUP_CLUSTER_NAME'],
    ENV['FLYNN_SETUP_CLUSTER_CONTROLLER_DOMAIN'],
    ENV['FLYNN_SETUP_CLUSTER_CONTROLLER_KEY'],
    ENV['FLYNN_SETUP_CLUSTER_APP_NAME']]
    
    # Verify the existance of all of the required variables.
    missing_vars = false
    vars.each do |var|
      if !var.present?
        Logger.warn "Flynn Auto-Scale: One of the environment variables required for connecting to the cluster is missing. Initialization will stop immediately."
        missing_vars = true
      end
    end
    
    # Abort if there are missing variables.
    if missing_vars
      return false
    end
    
    # Connect to the cluster.
    # Warning: this method of adding / defaulting the cluster is susceptible to Command Injection.
    # Do not pass params from the internet into any of this (you were warned!)
    # Source: http://brakemanscanner.org/docs/warning_types/command_injection/
    `flynn cluster add -p #{vars[0]} #{vars[1]} #{vars[2]} #{vars[3]}`
    
    output = `flynn cluster default #{vars[4]}`
    if !output.include? "is now the default cluster."
      Logger.warn "Flynn Auto-Scale: There was an issue connecting to the cluster. Please provide this output in a Github Issue: #{output}"
      false
    else
      true
    end
  end
end
install() click to toggle source
# File lib/flynn_auto_scale.rb, line 17
def install
  # Install the Flynn CLI if it doesn't exist.
  if !check_install
    Logger.warn "Flynn Auto-Scale: Installing the Flynn CLI. This will take some time. This installer only works under Linux / Mac OS X."
    `L=/usr/local/bin/flynn && curl -sSL -A "\`uname -sp\`" https://dl.flynn.io/cli | zcat >$L && chmod +x $L`
    check_install
  else
    true
  end
end
scale(process='web', instances=1, event_type='manual') click to toggle source

The foundation of the auto / manual scaling system. You should not be calling this method unless you know what you are doing.

# File lib/flynn_auto_scale.rb, line 154
def scale(process='web', instances=1, event_type='manual')
  if event_type == 'manual' && !ENV['FLYNN_LIMIT_INSTANCES_MANUAL_MODE'].present?
    # If the instances are not limited in manual mode, just let it rip (doesn't wait for confirmation)
    FlynnAutoScale::ScalingEvent.create(process_type: process, event_type: event_type, instances: instances)
    `flynn -a #{ENV['FLYNN_SETUP_CLUSTER_APP_NAME']} scale #{process}=#{instances} -n`
  else
    # This section is active in auto mode and in manual mode with limitations.
    if ENV['FLYNN_MIN_INSTANCES'].present? && ENV['FLYNN_MAX_INSTANCES'].present? 
      if instances < ENV['FLYNN_MIN_INSTANCES'].to_i
        Logger.warn "Flynn Auto-Scale: A scale event was stopped due to being below the minimum instance count"
        return
      end
      
      # Do the min and max instance checks
        
      if instances > ENV['FLYNN_MAX_INSTANCES'].to_i
        Logger.warn "Flynn Auto-Scale: A scale event was stopped due to being above the maximum instance count"
        return
      end
    elsif ENV['FLYNN_MIN_INSTANCES'].present?
      # Do the min instance check
      if instances < ENV['FLYNN_MIN_INSTANCES'].to_i
        Logger.warn "Flynn Auto-Scale: A scale event was stopped due to being below the minimum instance count"
        return
      end
    elsif ENV['FLYNN_MAX_INSTANCES'].present?
      # Do the max instance check
      if instances > ENV['FLYNN_MAX_INSTANCES'].to_i
        Logger.warn "Flynn Auto-Scale: A scale event was stopped due to being above the maximum instance count"
        return
      end
    elsif instances < 1 || instances > 2
      Logger.warn "Flynn Auto-Scale: A scale event was stopped due to exceeding the default values"
      return
    end
    # Okay, we checked, it's a good scaling event, hold on to your butts.
    FlynnAutoScale::ScalingEvent.create(process_type: process, event_type: event_type, instances: instances)
    `flynn -a #{ENV['FLYNN_SETUP_CLUSTER_APP_NAME']} scale #{process}=#{instances} -n`
  end
end
scale_down(process='web') click to toggle source
# File lib/flynn_auto_scale.rb, line 137
def scale_down(process='web')
  # This method will scale the process count down. It assumes it was called from an "AUTO_SCALE" setup.
  if can_scale_time
    scale(process, FlynnAutoScale::ScalingEvent.where(process_type: process).last.instances - 1, 'scale_down')
  else
    Logger.warn "Flynn Auto-Scale: A scale_down event was aborted due to a time limitation."
  end
end
scale_manual(process='web', instances=1) click to toggle source
# File lib/flynn_auto_scale.rb, line 146
def scale_manual(process='web', instances=1)
  # This method will just scale the process.
  # This method will respect very little restrictions, make sure you know what this does.
  scale(process, instances)
end
scale_up(process='web') click to toggle source
# File lib/flynn_auto_scale.rb, line 128
def scale_up(process='web')
  # This method will scale the process count up. It assumes it was called from an "AUTO_SCALE" setup.
  if can_scale_time
    scale(process, FlynnAutoScale::ScalingEvent.where(process_type: process).last.instances + 1, 'scale_up')
  else
    Logger.warn "Flynn Auto-Scale: A scale_up event was aborted due to a time limitation."
  end
end