From 1028795b975de330ba4fcacc46a2a7b4880abd02 Mon Sep 17 00:00:00 2001 From: Mihail Dobrev Date: Fri, 13 Sep 2024 17:08:58 +0300 Subject: [PATCH 1/2] feat: add minutes per block to stats endpoint --- lib/ae_mdw/stats.ex | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/lib/ae_mdw/stats.ex b/lib/ae_mdw/stats.ex index 52b5d2723..63c641fef 100644 --- a/lib/ae_mdw/stats.ex +++ b/lib/ae_mdw/stats.ex @@ -155,7 +155,8 @@ defmodule AeMdw.Stats do with {:ok, Model.stat(payload: {tps, tps_block_hash})} <- State.get(state, Model.Stat, @tps_stat_key), {:ok, Model.stat(payload: miners_count)} <- - State.get(state, Model.Stat, @miners_count_stat_key) do + State.get(state, Model.Stat, @miners_count_stat_key), + {:ok, minutes_per_block} = minutes_per_block(state) do {{last_24hs_txs_count, trend}, {last_24hs_tx_fees_average, fees_trend}} = last_24hs_txs_count_and_fee_with_trend(state) @@ -167,11 +168,12 @@ defmodule AeMdw.Stats do last_24hs_transactions: last_24hs_txs_count, transactions_trend: trend, last_24hs_average_transaction_fees: last_24hs_tx_fees_average, - fees_trend: fees_trend + fees_trend: fees_trend, + minutes_per_block: minutes_per_block }} else :not_found -> - {:error, ErrInput.NotFound.exception(value: "no stats")} + {:error, ErrInput.NotFound.exception(value: "no last_gen")} end end @@ -674,4 +676,27 @@ defmodule AeMdw.Stats do 0 end end + + defp minutes_per_block(state) do + with {:ok, first_block} <- :aec_chain.get_key_block_by_height(1), + last_gen <- DbUtil.last_gen(state), + {:ok, last_block} <- :aec_chain.get_key_block_by_height(last_gen) do + first_block_time = :aec_blocks.time_in_msecs(first_block) + + last_block_time = + :aec_blocks.time_in_msecs(last_block) + + diff_in_minutes = (last_block_time - first_block_time) / 60_000 + minutes_per_block = diff_in_minutes / last_gen + + minutes = floor(minutes_per_block) + float_seconds = (minutes_per_block - minutes) * 60 + seconds = floor(float_seconds) + miliseconds = round((float_seconds - seconds) * 1_000) + + {h, m, s} = 0 |> Time.new!(minutes, 0) |> Time.add(seconds, :second) |> Time.to_erl() + + {:ok, "#{h}h #{m}m #{s}s #{miliseconds}ms"} + end + end end From 10bb2a1801e9f18152ce67217547a8fcd7169c7e Mon Sep 17 00:00:00 2001 From: Mihail Dobrev Date: Mon, 16 Sep 2024 18:01:56 +0300 Subject: [PATCH 2/2] fix: display minutes in ms and update tests --- lib/ae_mdw/stats.ex | 16 ++---- test/ae_mdw/stats_test.exs | 53 +++++++++++++++---- .../controllers/stats_controller_test.exs | 22 ++++++-- 3 files changed, 65 insertions(+), 26 deletions(-) diff --git a/lib/ae_mdw/stats.ex b/lib/ae_mdw/stats.ex index 63c641fef..9277f4e37 100644 --- a/lib/ae_mdw/stats.ex +++ b/lib/ae_mdw/stats.ex @@ -156,7 +156,7 @@ defmodule AeMdw.Stats do State.get(state, Model.Stat, @tps_stat_key), {:ok, Model.stat(payload: miners_count)} <- State.get(state, Model.Stat, @miners_count_stat_key), - {:ok, minutes_per_block} = minutes_per_block(state) do + {:ok, minutes_per_block} <- minutes_per_block(state) do {{last_24hs_txs_count, trend}, {last_24hs_tx_fees_average, fees_trend}} = last_24hs_txs_count_and_fee_with_trend(state) @@ -173,7 +173,7 @@ defmodule AeMdw.Stats do }} else :not_found -> - {:error, ErrInput.NotFound.exception(value: "no last_gen")} + {:error, ErrInput.NotFound.exception(value: "no stats")} end end @@ -686,17 +686,7 @@ defmodule AeMdw.Stats do last_block_time = :aec_blocks.time_in_msecs(last_block) - diff_in_minutes = (last_block_time - first_block_time) / 60_000 - minutes_per_block = diff_in_minutes / last_gen - - minutes = floor(minutes_per_block) - float_seconds = (minutes_per_block - minutes) * 60 - seconds = floor(float_seconds) - miliseconds = round((float_seconds - seconds) * 1_000) - - {h, m, s} = 0 |> Time.new!(minutes, 0) |> Time.add(seconds, :second) |> Time.to_erl() - - {:ok, "#{h}h #{m}m #{s}s #{miliseconds}ms"} + {:ok, div(last_block_time - first_block_time, last_gen)} end end end diff --git a/test/ae_mdw/stats_test.exs b/test/ae_mdw/stats_test.exs index cbdda1214..e50565619 100644 --- a/test/ae_mdw/stats_test.exs +++ b/test/ae_mdw/stats_test.exs @@ -1,12 +1,12 @@ defmodule AeMdw.StatsTest do use ExUnit.Case + import Mock alias AeMdw.Db.State alias AeMdw.Db.MemStore alias AeMdw.Db.Model alias AeMdw.Db.NullStore alias AeMdw.Error.Input - alias AeMdw.Db.Store alias AeMdw.Stats alias AeMdw.TestSamples, as: TS @@ -16,20 +16,38 @@ defmodule AeMdw.StatsTest do test "it displays the max tps and miners_count" do key_block_hash = TS.key_block_hash(0) enc_hash = :aeser_api_encoder.encode(:key_block_hash, key_block_hash) + now = :aeu_time.now_in_msecs() + three_minutes = 3 * 60 * 1000 state = NullStore.new() |> MemStore.new() - |> Store.put(Model.Stat, Model.stat(index: :max_tps, payload: {16.05, key_block_hash})) - |> Store.put(Model.Stat, Model.stat(index: :miners_count, payload: 20)) |> State.new() + |> State.put(Model.Stat, Model.stat(index: :max_tps, payload: {16.05, key_block_hash})) + |> State.put(Model.Stat, Model.stat(index: :miners_count, payload: 20)) + |> State.put(Model.Block, Model.block(index: {1, -1}, hash: <<1::256>>)) + |> State.put(Model.Block, Model.block(index: {10, -1}, hash: key_block_hash)) - assert {:ok, - %{ - max_transactions_per_second: 16.05, - max_transactions_per_second_block_hash: ^enc_hash, - miners_count: 20 - }} = Stats.fetch_stats(state) + with_mocks([ + {:aec_chain, [], + get_key_block_by_height: fn + 1 -> {:ok, :first_block} + _n -> {:ok, :other_block} + end}, + {:aec_blocks, [], + time_in_msecs: fn + :first_block -> now - 10 * three_minutes + :other_block -> now + end} + ]) do + assert {:ok, + %{ + max_transactions_per_second: 16.05, + max_transactions_per_second_block_hash: ^enc_hash, + miners_count: 20, + minutes_per_block: ^three_minutes + }} = Stats.fetch_stats(state) + end end test "when not stat found, it returns an error" do @@ -37,10 +55,25 @@ defmodule AeMdw.StatsTest do NullStore.new() |> MemStore.new() |> State.new() + |> State.put(Model.Block, Model.block(index: {1, -1}, hash: <<1::256>>)) + |> State.put(Model.Block, Model.block(index: {10, -1}, hash: <<10::256>>)) error_msg = "not found: no stats" - assert {:error, %Input{message: ^error_msg}} = Stats.fetch_stats(state) + with_mocks([ + {:aec_chain, [], + get_key_block_by_height: fn + 1 -> {:ok, :first_block} + _n -> {:ok, :other_block} + end}, + {:aec_blocks, [], + time_in_msecs: fn + :first_block -> 1 + :other_block -> 10 + end} + ]) do + assert {:error, %Input{message: ^error_msg}} = Stats.fetch_stats(state) + end end end end diff --git a/test/ae_mdw_web/controllers/stats_controller_test.exs b/test/ae_mdw_web/controllers/stats_controller_test.exs index 711ebcbd5..81344cf25 100644 --- a/test/ae_mdw_web/controllers/stats_controller_test.exs +++ b/test/ae_mdw_web/controllers/stats_controller_test.exs @@ -856,6 +856,7 @@ defmodule AeMdwWeb.StatsControllerTest do %{conn: conn, store: store} do now = :aeu_time.now_in_msecs() msecs_per_day = 3_600 * 24 * 1_000 + three_minutes = 3 * 60 * 1_000 delay = 500 last_24hs_start_txi = 8 @@ -871,21 +872,36 @@ defmodule AeMdwWeb.StatsControllerTest do |> Store.put(Model.Time, Model.time(index: {now - msecs_per_day * 2 + delay, 1})) |> Store.put(Model.Stat, Model.stat(index: :miners_count, payload: 2)) |> Store.put(Model.Stat, Model.stat(index: :max_tps, payload: {2, <<0::256>>})) + |> Store.put(Model.Block, Model.block(index: {1, -1}, hash: <<1::256>>)) + |> Store.put(Model.Block, Model.block(index: {10, -1}, hash: <<2::256>>)) txis = last_24hs_start_txi..last_txi fee_avg = Enum.sum(txis) / Enum.count(txis) - with_mocks([{AeMdw.Node.Db, [], get_tx_fee: fn <> -> i end}]) do + with_mocks([ + {AeMdw.Node.Db, [], get_tx_fee: fn <> -> i end}, + {:aec_chain, [], + get_key_block_by_height: fn + 1 -> {:ok, :first_block} + _n -> {:ok, :other_block} + end}, + {:aec_blocks, [], + time_in_msecs: fn + :first_block -> now - 10 * three_minutes + :other_block -> now + end} + ]) do assert %{ "last_24hs_transactions" => 13, "transactions_trend" => 0.46, "fees_trend" => 0.69, - "last_24hs_average_transaction_fees" => ^fee_avg + "last_24hs_average_transaction_fees" => ^fee_avg, + "minutes_per_block" => ^three_minutes } = conn |> with_store(store) - |> get("/v2/stats") + |> get("/v3/stats") |> json_response(200) end end