module Mmtrix::Agent::BusyCalculator

This module supports calculation of actual time spent processing requests over the course of one harvest period. It’s similar to what you would get if you just added up all the execution times of controller calls, however that will be inaccurate when requests span the minute boundaries. This module manages accounting of requests not yet completed.

Calls are re-entrant. All start calls must be paired with finish calls, or a reset call.

Attributes

accumulator[R]

For testability, add accessors:

harvest_start[R]

For testability, add accessors:

Public Instance Methods

busy_count() click to toggle source

this returns the size of the entry point stack, which determines how many transactions are running

# File lib/mmtrix/agent/busy_calculator.rb, line 62
def busy_count
  @entrypoint_stack.size
end
dispatcher_finish(end_time = nil) click to toggle source

called when a transaction finishes, to add time to the instance variable accumulator. this is harvested when we send data to the server

# File lib/mmtrix/agent/busy_calculator.rb, line 40
def dispatcher_finish(end_time = nil) #THREAD_LOCAL_ACCESS
  state = TransactionState.tl_get
  # If #dispatcher_start hasn't been called at least once, abort early
  return unless state.busy_entries

  end_time ||= time_now
  callers = state.busy_entries -= 1

  # Ignore nested calls
  return if callers > 0

  @lock.synchronize do
    if @entrypoint_stack.empty?
      ::Mmtrix::Agent.logger.warn("Stack underflow tracking dispatcher entry and exit!\n  #{caller.join("  \n")}")
    else
      @accumulator += (end_time - @entrypoint_stack.pop).to_f
    end
  end
end
dispatcher_start(time) click to toggle source

sets up busy calculations based on the start and end of transactions - used for a rough estimate of what percentage of wall clock time is spent processing requests

# File lib/mmtrix/agent/busy_calculator.rb, line 27
def dispatcher_start(time) #THREAD_LOCAL_ACCESS
  state = TransactionState.tl_get
  state.busy_entries ||= 0
  callers = state.busy_entries += 1
  return if callers > 1
  @lock.synchronize do
    @entrypoint_stack.push time
  end
end
harvest_busy() click to toggle source

Called before uploading to to the server to collect current busy stats.

# File lib/mmtrix/agent/busy_calculator.rb, line 78
def harvest_busy
  busy = 0
  t0 = time_now
  @lock.synchronize do
    busy = accumulator
    @accumulator = 0

    # Walk through the stack and capture all times up to
    # now for entrypoints
    @entrypoint_stack.size.times do |frame|
      busy += (t0 - @entrypoint_stack[frame]).to_f
      @entrypoint_stack[frame] = t0
    end

  end

  busy = 0.0 if busy < 0.0 # don't go below 0%

  time_window = (t0 - harvest_start).to_f
  time_window = 1.0 if time_window == 0.0  # protect against divide by zero

  busy = busy / time_window

  if Agent.config[:report_instance_busy]
    Mmtrix::Agent.record_metric('Instance/Busy', busy)
  end
  @harvest_start = t0
end
reset() click to toggle source

Reset the state of the information accumulated by all threads, but only reset the recursion counter for this thread.

# File lib/mmtrix/agent/busy_calculator.rb, line 68
def reset #THREAD_LOCAL_ACCESS
  @entrypoint_stack = []
  TransactionState.tl_get.busy_entries = 0
  @lock ||= Mutex.new
  @accumulator = 0
  @harvest_start = time_now
end

Private Instance Methods

time_now() click to toggle source

so we can stub Time.now only for the BusyCalculator in tests

# File lib/mmtrix/agent/busy_calculator.rb, line 110
def time_now
  Time.now
end