diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bddae1..b00d6d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 0.5.6 +* Feature that allows measuring number of RPC calls via an adapter https://github.com/mana-ethereum/ethereumex/pull/67 + # 0.5.5 * Allow request counter resetting (https://github.com/mana-ethereum/ethereumex/pull/65) diff --git a/README.md b/README.md index dac8a35..cb30791 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,19 @@ config :ethereumex, ipc_path: "/path/to/ipc" ``` +If you want to count the number of RPC calls per RPC method, +set adapter in the configuration. The adapter needs to implement `increment/3` +where the first argument is the key, the second is an integer and the third are optional keyword options you +want to pass to your adapter on each increment call. + +```elixir +config :ethereumex, + adapter: Datadog + adapter_options: [] +``` +An example of an adapter would be [Statix](https://github.com/lexmag/statix/blob/v1.2.1/lib/statix.ex#L288-L290). +Adapter options could be `[sample_rate: 1.0, tags: ["foo", "bar"]]`. + The IPC client type mode opens a pool of connection workers (default is 5 and 2, respectively). You can configure the pool size. ```elixir config :ethereumex, diff --git a/lib/ethereumex/client/base_client.ex b/lib/ethereumex/client/base_client.ex index ee3a1a2..de4e15e 100644 --- a/lib/ethereumex/client/base_client.ex +++ b/lib/ethereumex/client/base_client.ex @@ -487,20 +487,22 @@ defmodule Ethereumex.Client.BaseClient do end defp prepare_request(params) when is_list(params) do - id = Counter.increment(:rpc_counter) + id = Counter.get(:rpc_counter) params = params - |> Enum.with_index() + |> Enum.with_index(1) |> Enum.map(fn {req_data, index} -> Map.put(req_data, "id", index + id) end) - _ = Counter.increment(:rpc_counter, id + Enum.count(params)) + _ = Counter.increment(:rpc_counter, id + Enum.count(params), "eth_batch") + params end - defp prepare_request(params), do: Map.put(params, "id", Counter.increment(:rpc_counter)) + defp prepare_request(params), + do: Map.put(params, "id", Counter.increment(:rpc_counter, params["method"])) defp request(params, opts) do __MODULE__.single_request(params, opts) diff --git a/lib/ethereumex/counter.ex b/lib/ethereumex/counter.ex index 6a514eb..bb720ba 100644 --- a/lib/ethereumex/counter.ex +++ b/lib/ethereumex/counter.ex @@ -17,41 +17,59 @@ defmodule Ethereumex.Counter do :ok end - @spec increment(atom()) :: integer() - def increment(key) do - do_increment(Application.get_env(:ethereumex, :id_reset), key) + @spec get(atom()) :: integer() + def get(key) do + case :ets.lookup(@tab, key) do + [] -> 1 + [{^key, num}] -> num + end end - @spec increment(atom(), integer()) :: integer() - def increment(key, count) do - do_increment(Application.get_env(:ethereumex, :id_reset), key, count) + @spec increment(atom(), String.t()) :: integer() + def increment(key, method) do + do_increment(Application.get_env(:ethereumex, :id_reset), key, method) end - @spec do_increment(binary() | nil, atom()) :: integer() - defp do_increment(true, key) do + @spec increment(atom(), integer(), String.t()) :: integer() + def increment(key, count, method) do + do_increment(Application.get_env(:ethereumex, :id_reset), key, count, method) + end + + @spec do_increment(binary() | nil, atom(), String.t()) :: integer() + defp do_increment(true, key, method) do :ets.insert(@tab, {key, 0}) - inc(key) + inc(key, method, Application.get_env(:ethereumex, :adapter)) end - defp do_increment(_, key) do - inc(key) + defp do_increment(_, key, method) do + inc(key, method, Application.get_env(:ethereumex, :adapter)) end - @spec do_increment(boolean() | nil, atom(), integer()) :: integer() - defp do_increment(true, key, count) do + @spec do_increment(boolean() | nil, atom(), integer(), String.t()) :: integer() + defp do_increment(true, key, count, method) do :ets.insert(@tab, {key, 0}) - inc(key, count) + inc(key, count, method, Application.get_env(:ethereumex, :adapter)) + end + + defp do_increment(_, key, count, method) do + inc(key, count, method, Application.get_env(:ethereumex, :adapter)) end - defp do_increment(_, key, count) do - inc(key, count) + defp inc(key, _, nil) do + :ets.update_counter(@tab, key, {2, 1}, {key, 0}) end - defp inc(key) do + defp inc(key, method, adapter) do + _ = adapter.increment(method, 1, Application.get_env(:ethereumex, :adapter_options) || []) :ets.update_counter(@tab, key, {2, 1}, {key, 0}) end - defp inc(key, count) do + defp inc(key, count, _, nil) do + :ets.update_counter(@tab, key, {2, count}, {key, 0}) + end + + defp inc(key, count, method, adapter) do + _ = adapter.increment(method, 1, Application.get_env(:ethereumex, :adapter_options) || []) :ets.update_counter(@tab, key, {2, count}, {key, 0}) end end diff --git a/mix.exs b/mix.exs index f67dd40..a283a91 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule Ethereumex.Mixfile do def project do [ app: :ethereumex, - version: "0.5.5", + version: "0.5.6", elixir: "~> 1.7", description: "Elixir JSON-RPC client for the Ethereum blockchain", package: [ diff --git a/test/ethereumex/counter_reset_test.exs b/test/ethereumex/counter_reset_test.exs index e04ef2f..0a4efc7 100644 --- a/test/ethereumex/counter_reset_test.exs +++ b/test/ethereumex/counter_reset_test.exs @@ -11,18 +11,18 @@ defmodule Ethereumex.ResetLockTest do end test "incrementing twice returns correct locked binary" do - 1 = Counter.increment(:test_11) - 1 = Counter.increment(:test_11) + 1 = Counter.increment(:test_11, "method_11") + 1 = Counter.increment(:test_11, "method_11") end test "incrementing twice and updating with a count returns correct locked binary" do - 1 = Counter.increment(:test_22) - 2 = Counter.increment(:test_22, 2) + 1 = Counter.increment(:test_22, "method_22") + 2 = Counter.increment(:test_22, 2, "method_22") end test "incrementing twice, updating with a count and incrementing again returns correct locked binary" do - 1 = Counter.increment(:test_33) - 2 = Counter.increment(:test_33, 2) - 1 = Counter.increment(:test_33) + 1 = Counter.increment(:test_33, "method_33") + 2 = Counter.increment(:test_33, 2, "method_33") + 1 = Counter.increment(:test_33, "method_33") end end diff --git a/test/ethereumex/counter_test.exs b/test/ethereumex/counter_test.exs index 1c608e6..ae44087 100644 --- a/test/ethereumex/counter_test.exs +++ b/test/ethereumex/counter_test.exs @@ -2,21 +2,46 @@ defmodule Ethereumex.CounterTest do use ExUnit.Case alias Ethereumex.Counter + setup_all do + on_exit(fn -> + Application.put_env(:ethereumex, :adapter, nil) + Application.put_env(:ethereumex, :adapter_options, nil) + end) + + :ok + end + test "incrementing twice returns correct number" do - 1 = Counter.increment(:test_1) - 2 = Counter.increment(:test_1) + 1 = Counter.increment(:test_1, "method_1") + 2 = Counter.increment(:test_1, "method_1") end test "incrementing twice and updating with a count returns correct number" do - 1 = Counter.increment(:test_2) - 2 = Counter.increment(:test_2) - 4 = Counter.increment(:test_2, 2) + 1 = Counter.increment(:test_2, "method_2") + 2 = Counter.increment(:test_2, "method_2") + 4 = Counter.increment(:test_2, 2, ["method_2"]) end test "incrementing twice, updating with a count and incrementing again returns correct number" do - 1 = Counter.increment(:test_3) - 2 = Counter.increment(:test_3) - 4 = Counter.increment(:test_3, 2) - 5 = Counter.increment(:test_3) + 1 = Counter.increment(:test_3, "method_3") + 2 = Counter.increment(:test_3, "method_3") + 4 = Counter.increment(:test_3, 2, ["method_3"]) + 5 = Counter.increment(:test_3, "method_3") + end + + test "an adapter gets called with increment/2" do + :ok = Application.put_env(:ethereumex, :adapter, __MODULE__.Adapter) + 1 = Counter.increment(:test_4, "method_4") + assert_receive({"method_4", 1, []}) + 3 = Counter.increment(:test_4, 2, "eth_batch") + assert_receive({"eth_batch", 1, []}) + + :ok = Application.put_env(:ethereumex, :adapter_options, sample_rate: 0.5) + 5 = Counter.increment(:test_4, 2, "eth_batch") + assert_receive({"eth_batch", 1, [sample_rate: 0.5]}) + end + + defmodule Adapter do + def increment(method, count, options), do: Kernel.send(self(), {method, count, options}) end end