Merge pull request #1292 from poanetwork/speed-up-homepage

feat: store averge block time in a genserver
pull/1289/head
Luke Imhoff 6 years ago committed by GitHub
commit b48a53efec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex
  2. 3
      apps/block_scout_web/lib/block_scout_web/notifier.ex
  3. 21
      apps/block_scout_web/lib/block_scout_web/templates/chain/show.html.eex
  4. 24
      apps/block_scout_web/priv/gettext/default.pot
  5. 24
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  6. 2
      apps/explorer/config/config.exs
  7. 2
      apps/explorer/config/test.exs
  8. 1
      apps/explorer/lib/explorer/application.ex
  9. 26
      apps/explorer/lib/explorer/chain.ex
  10. 106
      apps/explorer/lib/explorer/counters/average_block_time.ex
  11. 2
      apps/explorer/mix.exs
  12. 14
      apps/explorer/test/explorer/chain_test.exs
  13. 64
      apps/explorer/test/explorer/counters/average_block_time_test.exs

@ -4,6 +4,7 @@ defmodule BlockScoutWeb.ChainController do
alias BlockScoutWeb.ChainView
alias Explorer.{Chain, PagingOptions, Repo}
alias Explorer.Chain.{Address, Block, Transaction}
alias Explorer.Counters.AverageBlockTime
alias Explorer.ExchangeRates.Token
alias Explorer.Market
alias Phoenix.View
@ -17,7 +18,7 @@ defmodule BlockScoutWeb.ChainController do
conn,
"show.html",
address_count: Chain.count_addresses_with_balance_from_cache(),
average_block_time: Chain.average_block_time(),
average_block_time: AverageBlockTime.average_block_time(),
exchange_rate: exchange_rate,
chart_data_path: market_history_chart_path(conn, :show),
transaction_estimated_count: transaction_estimated_count,

@ -7,6 +7,7 @@ defmodule BlockScoutWeb.Notifier do
alias BlockScoutWeb.Endpoint
alias Explorer.{Chain, Market, Repo}
alias Explorer.Chain.{Address, InternalTransaction, Transaction}
alias Explorer.Counters.AverageBlockTime
alias Explorer.ExchangeRates.Token
def handle_event({:chain_event, :addresses, :realtime, addresses}) do
@ -109,7 +110,7 @@ defmodule BlockScoutWeb.Notifier do
defp broadcast_block(block) do
preloaded_block = Repo.preload(block, [[miner: :names], :transactions, :rewards])
average_block_time = Chain.average_block_time()
average_block_time = AverageBlockTime.average_block_time(preloaded_block)
Endpoint.broadcast("blocks:new_block", "new_block", %{
block: preloaded_block,

@ -32,14 +32,19 @@
</div>
</div>
<div class="dashboard-banner-network-stats">
<div class="dashboard-banner-network-stats-item">
<span class="dashboard-banner-network-stats-label">
<%= gettext "Average block time" %>
</span>
<span class="dashboard-banner-network-stats-value" data-selector="average-block-time">
<%= Timex.format_duration(@average_block_time, :humanized) %>
</span>
</div>
<%= case @average_block_time do %>
<% {:error, :disabled} -> %>
<%= nil %>
<% average_block_time -> %>
<div class="dashboard-banner-network-stats-item">
<span class="dashboard-banner-network-stats-label">
<%= gettext "Average block time" %>
</span>
<span class="dashboard-banner-network-stats-value" data-selector="average-block-time">
<%= Timex.format_duration(average_block_time, :humanized) %>
</span>
</div>
<% end %>
<div class="dashboard-banner-network-stats-item">
<span class="dashboard-banner-network-stats-label">
<%= gettext "Total transactions" %>

@ -118,7 +118,7 @@ msgid "All"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:37
#: lib/block_scout_web/templates/chain/show.html.eex:41
msgid "Average block time"
msgstr ""
@ -175,7 +175,7 @@ msgid "BlockScout provides analytics data, API, and Smart Contract tools for the
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:67
#: lib/block_scout_web/templates/chain/show.html.eex:72
#: lib/block_scout_web/templates/layout/_topnav.html.eex:16
#: lib/block_scout_web/templates/layout/_topnav.html.eex:20
msgid "Blocks"
@ -572,7 +572,7 @@ msgid "More internal transactions have come in"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:91
#: lib/block_scout_web/templates/chain/show.html.eex:96
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:14
#: lib/block_scout_web/templates/transaction/index.html.eex:14
msgid "More transactions have come in"
@ -949,7 +949,7 @@ msgid "Total Supply"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:45
#: lib/block_scout_web/templates/chain/show.html.eex:50
msgid "Total transactions"
msgstr ""
@ -983,7 +983,7 @@ msgstr ""
#: lib/block_scout_web/templates/block_transaction/index.html.eex:23
#: lib/block_scout_web/templates/block_transaction/index.html.eex:26
#: lib/block_scout_web/templates/block_transaction/index.html.eex:35
#: lib/block_scout_web/templates/chain/show.html.eex:88
#: lib/block_scout_web/templates/chain/show.html.eex:93
#: lib/block_scout_web/templates/layout/_topnav.html.eex:35
#: lib/block_scout_web/views/address_view.ex:253
msgid "Transactions"
@ -1058,12 +1058,12 @@ msgid "Verify & publish"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:66
#: lib/block_scout_web/templates/chain/show.html.eex:71
msgid "View All Blocks →"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:87
#: lib/block_scout_web/templates/chain/show.html.eex:92
msgid "View All Transactions →"
msgstr ""
@ -1103,7 +1103,7 @@ msgid "WEI"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:53
#: lib/block_scout_web/templates/chain/show.html.eex:58
msgid "Wallet addresses"
msgstr ""
@ -1187,8 +1187,8 @@ msgstr ""
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:19
#: lib/block_scout_web/templates/address_validation/index.html.eex:63
#: lib/block_scout_web/templates/address_validation/index.html.eex:82
#: lib/block_scout_web/templates/chain/show.html.eex:79
#: lib/block_scout_web/templates/chain/show.html.eex:105
#: lib/block_scout_web/templates/chain/show.html.eex:84
#: lib/block_scout_web/templates/chain/show.html.eex:110
#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:24
msgid "Loading..."
msgstr ""
@ -1399,7 +1399,7 @@ msgstr ""
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:26
#: lib/block_scout_web/templates/address_transaction/index.html.eex:55
#: lib/block_scout_web/templates/address_validation/index.html.eex:70
#: lib/block_scout_web/templates/chain/show.html.eex:71
#: lib/block_scout_web/templates/chain/show.html.eex:76
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:23
#: lib/block_scout_web/templates/tokens/holder/index.html.eex:22
#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:20
@ -1522,6 +1522,6 @@ msgid "Emission Contract"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:97
#: lib/block_scout_web/templates/chain/show.html.eex:102
msgid "Something went wrong, click to retry."
msgstr ""

@ -118,7 +118,7 @@ msgid "All"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:37
#: lib/block_scout_web/templates/chain/show.html.eex:41
msgid "Average block time"
msgstr ""
@ -175,7 +175,7 @@ msgid "BlockScout provides analytics data, API, and Smart Contract tools for the
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:67
#: lib/block_scout_web/templates/chain/show.html.eex:72
#: lib/block_scout_web/templates/layout/_topnav.html.eex:16
#: lib/block_scout_web/templates/layout/_topnav.html.eex:20
msgid "Blocks"
@ -572,7 +572,7 @@ msgid "More internal transactions have come in"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:91
#: lib/block_scout_web/templates/chain/show.html.eex:96
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:14
#: lib/block_scout_web/templates/transaction/index.html.eex:14
msgid "More transactions have come in"
@ -949,7 +949,7 @@ msgid "Total Supply"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:45
#: lib/block_scout_web/templates/chain/show.html.eex:50
msgid "Total transactions"
msgstr ""
@ -983,7 +983,7 @@ msgstr ""
#: lib/block_scout_web/templates/block_transaction/index.html.eex:23
#: lib/block_scout_web/templates/block_transaction/index.html.eex:26
#: lib/block_scout_web/templates/block_transaction/index.html.eex:35
#: lib/block_scout_web/templates/chain/show.html.eex:88
#: lib/block_scout_web/templates/chain/show.html.eex:93
#: lib/block_scout_web/templates/layout/_topnav.html.eex:35
#: lib/block_scout_web/views/address_view.ex:253
msgid "Transactions"
@ -1058,12 +1058,12 @@ msgid "Verify & publish"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:66
#: lib/block_scout_web/templates/chain/show.html.eex:71
msgid "View All Blocks →"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:87
#: lib/block_scout_web/templates/chain/show.html.eex:92
msgid "View All Transactions →"
msgstr ""
@ -1103,7 +1103,7 @@ msgid "WEI"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:53
#: lib/block_scout_web/templates/chain/show.html.eex:58
msgid "Wallet addresses"
msgstr ""
@ -1187,8 +1187,8 @@ msgstr ""
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:19
#: lib/block_scout_web/templates/address_validation/index.html.eex:63
#: lib/block_scout_web/templates/address_validation/index.html.eex:82
#: lib/block_scout_web/templates/chain/show.html.eex:79
#: lib/block_scout_web/templates/chain/show.html.eex:105
#: lib/block_scout_web/templates/chain/show.html.eex:84
#: lib/block_scout_web/templates/chain/show.html.eex:110
#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:24
msgid "Loading..."
msgstr ""
@ -1399,7 +1399,7 @@ msgstr ""
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:26
#: lib/block_scout_web/templates/address_transaction/index.html.eex:55
#: lib/block_scout_web/templates/address_validation/index.html.eex:70
#: lib/block_scout_web/templates/chain/show.html.eex:71
#: lib/block_scout_web/templates/chain/show.html.eex:76
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:23
#: lib/block_scout_web/templates/tokens/holder/index.html.eex:22
#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:20
@ -1522,6 +1522,6 @@ msgid "Emission Contract"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:97
#: lib/block_scout_web/templates/chain/show.html.eex:102
msgid "Something went wrong, click to retry."
msgstr ""

@ -11,6 +11,8 @@ config :explorer,
coin: System.get_env("COIN") || "POA",
token_functions_reader_max_retries: 3
config :explorer, Explorer.Counters.AverageBlockTime, enabled: true
config :explorer, Explorer.Counters.AddressesWithBalanceCounter, enabled: true, enable_consolidation: true
config :explorer, Explorer.ExchangeRates, enabled: true, store: :ets

@ -15,6 +15,8 @@ config :explorer, Explorer.ExchangeRates, enabled: false, store: :ets
config :explorer, Explorer.KnownTokens, enabled: false, store: :ets
config :explorer, Explorer.Counters.AverageBlockTime, enabled: false
config :explorer, Explorer.Counters.AddressesWithBalanceCounter, enabled: false, enable_consolidation: false
config :explorer, Explorer.Market.History.Cataloger, enabled: false

@ -37,6 +37,7 @@ defmodule Explorer.Application do
configure(Explorer.KnownTokens),
configure(Explorer.Market.History.Cataloger),
configure(Explorer.Counters.AddressesWithBalanceCounter),
configure(Explorer.Counters.AverageBlockTime),
configure(Explorer.Validator.MetadataProcessor)
]
|> List.flatten()

@ -44,7 +44,6 @@ defmodule Explorer.Chain do
alias Explorer.{PagingOptions, Repo}
alias Dataloader.Ecto, as: DataloaderEcto
alias Timex.Duration
@default_paging_options %PagingOptions{page_size: 50}
@ -287,31 +286,6 @@ defmodule Explorer.Chain do
|> Repo.all()
end
@doc """
The average time it took to mine/validate the last <= 100 `t:Explorer.Chain.Block.t/0`
"""
@spec average_block_time :: %Timex.Duration{}
def average_block_time do
{:ok, %Postgrex.Result{rows: [[%Postgrex.Interval{months: 0, days: days, secs: seconds}]]}} =
SQL.query(
Repo,
"""
SELECT coalesce(avg(difference), interval '0 seconds')
FROM (
SELECT b.timestamp - lag(b.timestamp) over (order by b.timestamp) as difference
FROM (SELECT * FROM blocks ORDER BY number DESC LIMIT 101) b
LIMIT 100 OFFSET 1
) t
""",
[]
)
hours = days * 24
minutes = 0
microseconds = 0
Duration.from_clock({hours, minutes, seconds, microseconds})
end
@doc """
The `t:Explorer.Chain.Address.t/0` `balance` in `unit`.
"""

@ -0,0 +1,106 @@
defmodule Explorer.Counters.AverageBlockTime do
use GenServer
@moduledoc """
Caches the number of token holders of a token.
"""
import Ecto.Query, only: [from: 2]
alias Explorer.Chain.Block
alias Explorer.Repo
alias Timex.Duration
@doc """
Starts a process to periodically update the counter of the token holders.
"""
@spec start_link(term()) :: GenServer.on_start()
def start_link(_) do
GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
end
def average_block_time(block \\ nil) do
enabled? =
:explorer
|> Application.fetch_env!(__MODULE__)
|> Keyword.fetch!(:enabled)
if enabled? do
block = if block, do: {block.number, DateTime.to_unix(block.timestamp)}
GenServer.call(__MODULE__, {:average_block_time, block})
else
{:error, :disabled}
end
end
## Server
@impl true
def init(_) do
timestamps_query =
from(block in Block,
limit: 100,
offset: 1,
order_by: [desc: block.number],
select: {block.number, block.timestamp}
)
timestamps =
timestamps_query
|> Repo.all()
|> Enum.map(fn {number, timestamp} ->
{number, DateTime.to_unix(timestamp)}
end)
{:ok, %{timestamps: timestamps, average: average_distance(timestamps)}}
end
@impl true
def handle_call({:average_block_time, nil}, _from, %{average: average} = state), do: {:reply, average, state}
def handle_call({:average_block_time, block}, _from, state) do
state = add_block(state, block)
{:reply, state.average, state}
end
# This is pretty naive, but we'll only ever be sorting 100 dates so I don't think
# complex logic is really necessary here.
defp add_block(%{timestamps: timestamps} = state, block) do
timestamps =
[block | timestamps]
|> Enum.sort_by(fn {number, _} -> number end, &Kernel.>/2)
|> Enum.take(100)
%{state | timestamps: timestamps, average: average_distance(timestamps)}
end
defp average_distance([]), do: Duration.from_seconds(0)
defp average_distance([_]), do: Duration.from_seconds(0)
defp average_distance(timestamps) do
durations = durations(timestamps)
{sum, count} =
Enum.reduce(durations, {0, 0}, fn duration, {sum, count} ->
{sum + duration, count + 1}
end)
average = sum / count
average
|> round()
|> Duration.from_seconds()
end
defp durations(timestamps) do
timestamps
|> Enum.reduce({[], nil}, fn {_, timestamp}, {durations, last_timestamp} ->
if last_timestamp do
duration = last_timestamp - timestamp
{[duration | durations], timestamp}
else
{durations, timestamp}
end
end)
|> elem(0)
end
end

@ -112,7 +112,7 @@ defmodule Explorer.Mixfile do
{:spandex_ecto, "~> 0.4.0"},
# Attach `:prometheus_ecto` to `:ecto`
{:telemetry, "~> 0.2.0"},
# `Timex.Duration` for `Explorer.Chain.average_block_time/0`
# `Timex.Duration` for `Explorer.Counters.AverageBlockTime.average_block_time/0`
{:timex, "~> 3.4"}
]
end

@ -490,20 +490,6 @@ defmodule Explorer.ChainTest do
end
end
describe "average_block_time/0" do
test "without blocks duration is 0" do
assert Chain.average_block_time() == Timex.Duration.parse!("PT0S")
end
test "with blocks is average duration between blocks" do
first_block = insert(:block)
second_block = insert(:block, timestamp: Timex.shift(first_block.timestamp, seconds: 3))
insert(:block, timestamp: Timex.shift(second_block.timestamp, seconds: 9))
assert Chain.average_block_time() == Timex.Duration.parse!("PT6S")
end
end
describe "balance/2" do
test "with Address.t with :wei" do
assert Chain.balance(%Address{fetched_coin_balance: %Wei{value: Decimal.new(1)}}, :wei) == Decimal.new(1)

@ -0,0 +1,64 @@
defmodule Explorer.Counters.AverageBlockTimeTest do
use Explorer.DataCase
alias Explorer.Counters.AverageBlockTime
defp block(number, last, duration), do: %{number: number, timestamp: Timex.shift(last, seconds: duration)}
setup do
start_supervised!(AverageBlockTime)
Application.put_env(:explorer, AverageBlockTime, enabled: true)
on_exit(fn ->
Application.put_env(:explorer, AverageBlockTime, enabled: false)
end)
end
describe "average_block_time/1" do
test "when disabled, it returns an error" do
Application.put_env(:explorer, AverageBlockTime, enabled: false)
assert AverageBlockTime.average_block_time() == {:error, :disabled}
end
test "without blocks duration is 0" do
assert AverageBlockTime.average_block_time() == Timex.Duration.parse!("PT0S")
end
test "with only one block, the duration is 0" do
now = Timex.now()
block = block(0, now, 0)
assert AverageBlockTime.average_block_time(block) == Timex.Duration.parse!("PT0S")
end
test "once there are two blocks, the duration is the average distance between them all" do
now = Timex.now()
block0 = block(0, now, 0)
block1 = block(1, now, 2)
block2 = block(2, now, 6)
AverageBlockTime.average_block_time(block0)
assert AverageBlockTime.average_block_time(block1) == Timex.Duration.parse!("PT2S")
assert AverageBlockTime.average_block_time(block2) == Timex.Duration.parse!("PT3S")
end
test "only the last 100 blocks are considered" do
now = Timex.now()
block0 = block(0, now, 0)
block1 = block(1, now, 2000)
AverageBlockTime.average_block_time(block0)
AverageBlockTime.average_block_time(block1)
for i <- 1..100 do
block = block(i + 1, now, 2000 + i)
AverageBlockTime.average_block_time(block)
end
assert AverageBlockTime.average_block_time() == Timex.Duration.parse!("PT1S")
end
end
end
Loading…
Cancel
Save