utilities/plot_visibility_timeout.rb

Summary

Maintainability
A
0 mins
Test Coverage
require './../lib/eventq/eventq_aws/aws_calculate_visibility_timeout'
require 'csv'
require 'logger'

class PlotVisibilityTimeout
  # Folder where the plot results are saved
  PLOT_FOLDER = 'plot_results'

  def plot(settings)
    setup

    @plot_seconds                     = settings.fetch(:plot_seconds)
    @plot_min_timeout                 = settings.fetch(:plot_min_timeout)
    @plot_file_name                   = "#{PLOT_FOLDER}/plot_#{settings.values.join('__')}"

    @queue_allow_retry_back_off       = settings.fetch(:queue_allow_retry_back_off)
    @queue_allow_exponential_back_off = settings.fetch(:queue_allow_exponential_back_off)
    @queue_retry_back_off_weight      = settings.fetch(:queue_retry_back_off_weight)
    @queue_retry_jitter_ratio         = settings.fetch(:queue_retry_jitter_ratio)
    @queue_max_retry_delay            = settings.fetch(:queue_max_retry_delay)
    @queue_max_timeout                = settings.fetch(:queue_max_timeout)
    @queue_retry_back_off_grace       = settings.fetch(:queue_retry_back_off_grace)
    @queue_retry_delay                = settings.fetch(:queue_retry_delay)

    logger = Logger.new(STDOUT)

    @calculator = EventQ::Amazon::CalculateVisibilityTimeout.new(max_timeout: @queue_max_timeout, logger: logger)

    print_settings_list(settings)

    execute
  end

  private
  attr_reader :calculator

  def setup
    unless Dir.exist?(PLOT_FOLDER)
      Dir.mkdir(PLOT_FOLDER)
    end
  end

  def execute
    puts "Executing..."
    CSV.open("#{@plot_file_name}.csv", 'w') do |csv|
      csv << ['retry_counter', 'visibility_timeout', 'total_elapsed_time', ]

      retry_counter = 0
      total_elapsed_time = 0
      max_visibility_timeout = 0
      while(total_elapsed_time <= @plot_seconds) do
        retry_counter += 1

        visibility_timeout = calculate(retry_counter)

        if (visibility_timeout == 0)
          visibility_timeout = @plot_min_timeout
        end

        max_visibility_timeout = visibility_timeout
        total_elapsed_time += visibility_timeout
        total_elapsed_time = total_elapsed_time.round(2)

        csv << [retry_counter, visibility_timeout, total_elapsed_time]
      end

      print_output(retry_counter, max_visibility_timeout,total_elapsed_time)
    end
  end

  def calculate(retry_counter)
    calculator.call(
      retry_attempts:       retry_counter,
      queue_settings: {
        allow_retry_back_off:       @queue_allow_retry_back_off,
        allow_exponential_back_off: @queue_allow_exponential_back_off,
        max_retry_delay:            @queue_max_retry_delay,
        retry_back_off_grace:       @queue_retry_back_off_grace,
        retry_back_off_weight:      @queue_retry_back_off_weight,
        retry_jitter_ratio:         @queue_retry_jitter_ratio,
        retry_delay:                @queue_retry_delay
      }
    )
  end

  def print_settings_list(settings)
    settings_list = [
      "-" * 50,
      settings.map { |arr| "#{arr[0]} = #{arr[1]}" }.join("\n"),
      "-" * 50,
      "\n"
    ].join("\n")

    puts settings_list
    File.write("#{@plot_file_name}.txt", settings_list)
  end

  def print_output(retry_counter, max_visibility_timeout,total_elapsed_time)
    output_details = [
      "Completed:",
      "• #{retry_counter} total retries.",
      "• #{retry_counter - @queue_retry_back_off_grace} total retries after grace period.",
      "• #{max_visibility_timeout} max timeout in seconds",
      "• #{total_elapsed_time} total elapsed time in seconds"
    ].join("\n")

    puts output_details
    puts "=> #{@plot_file_name}.csv"

    File.open("#{@plot_file_name}.txt", 'a') { |f| f.write(output_details) }
  end
end

settings = {
  # Sometimes the calculated timeout is zero so we must default to a value
  # since in real life the is no zero second connections.
  plot_min_timeout:                 0.03,       # 30ms which is the average connection time between worker and queue

  # The amount of time we should plot for.
  plot_seconds:                     72*60*60,   # simulate 72h
  queue_allow_retry_back_off:       true,       # enables backoff strategy
  queue_allow_exponential_back_off: false,      # disables exponential backoff strategy

  # The cap value for the queue retry
  queue_max_retry_delay:            1_500_000,  # will wait max 1500s
  queue_max_timeout:                43_200,     # 12h which AWS max message visibility timeout

  # Waiting period before the backoff strategy kicks in.
  # Multiply with query_retry_delay and divide by 60 to see how many minutes it will wait.
  queue_retry_back_off_grace:       30_000,     # wait 15min: (queue_retry_back_off_grace * queue_retry_delay)/60

  # Delay and retry for each queue iterations. The multiplier is necessary in case the calculated values
  # are insignificant between iterations.
  queue_retry_back_off_weight:      100,        # Backoff multiplier
  queue_retry_delay:                30,         # 30ms

  # Ratio of randomness allowed on retry delay to avoid a bulk of retries hitting again at the same time
  queue_retry_jitter_ratio:         0           # ratio for randomness on retry delay
}

PlotVisibilityTimeout.new.plot(settings)