Concurrent and simple. No blocking calls or bottlenecks. Increments are atomic.
Throughput.start_link()
Throughput.increment()
Throughput.get_throughput_per_second()
Throughput.get_max_throughput_per_second()
Concurrent and simple. No blocking calls or bottlenecks. Increments are atomic.
Throughput.start_link()
Throughput.increment()
Throughput.get_throughput_per_second()
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 |