Skip to content

Instantly share code, notes, and snippets.

@amokan
Last active March 14, 2019 18:48
Show Gist options
  • Save amokan/8af1c605c25eafcdf204f7bcf87e84d7 to your computer and use it in GitHub Desktop.
Save amokan/8af1c605c25eafcdf204f7bcf87e84d7 to your computer and use it in GitHub Desktop.
Throughput Monitor

Simple and Dumb Throughput Counter

Concurrent and simple. No blocking calls or bottlenecks. Increments are atomic.

Usage

Start up the proc

Throughput.start_link()

Increment the counter

Throughput.increment()

View Current Throughput

Throughput.get_throughput_per_second()

View Max Throughput

Throughput.get_max_throughput_per_second()
defmodule Throughput do
@moduledoc """
Simple ETS-based store for keeping track of throughput for stuff
"""
use GenServer
require Logger
@table_name :throughput_stats
@throughput_calc_every 5_000 # calc throughput every 5 seconds
@doc """
Start up the process.
"""
def start_link(_), do: start_link()
def start_link, do: GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
@doc """
Increment the number of things processed
"""
def increment, do: :ets.update_counter(@table_name, :processed, {2, 1}, {:processed, 0})
@doc """
Returns the latest calculated throughput per second
"""
def get_throughput_per_second, do: get_value(:throughput_per_second)
@doc """
Returns the max calculated throughput per second
"""
def get_max_throughput_per_second, do: get_value(:max_throughput_per_second)
@doc false
def init(_) do
# create new ets table for handling counts
:ets.new(@table_name, [:set, :named_table, :public, write_concurrency: true])
Process.send_after(self(), :update_throughput, @throughput_calc_every)
{:ok, %{}}
end
@doc false
def handle_info(:update_throughput, state) do
# get the processed count and reset to zero
count = get_value(:processed)
:ets.insert(@table_name, {:processed, 0})
(count / 5.0)
|> Float.round(2)
|> set_throughput_per_second()
Process.send_after(self(), :update_throughput, @throughput_calc_every)
{:noreply, state}
end
# generic get for ets
defp get_value(key_name) do
case :ets.lookup(@table_name, key_name) do
[{_, count}|_] -> count
_ -> 0
end
end
# set the throughput per second
defp set_throughput_per_second(throughput) do
:ets.insert(@table_name, {:throughput_per_second, throughput})
set_max_throughput_per_second(throughput)
end
# only bother if the throughput was greater than zero
defp set_max_throughput_per_second(current_throughput) when current_throughput > 0 do
persist_max_throughput_per_second(current_throughput, get_max_throughput_per_second())
end
defp set_max_throughput_per_second(_current_throughput), do: nil
# only persist max throughput if we exceeded the existing max value
defp persist_max_throughput_per_second(current_throughput, max_throughput) when current_throughput > max_throughput do
:ets.insert(@table_name, {:max_throughput_per_second, current_throughput})
end
defp persist_max_throughput_per_second(_current_throughput, _max_throughput), do: nil
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment