Merge pull request #3146 from poanetwork/vb-refine-coin-balance-history

Fix coin balance history page: order of items, fix if no balance changes
pull/3149/head
Victor Baranov 5 years ago committed by GitHub
commit 0e8c627c08
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 12
      apps/block_scout_web/lib/block_scout_web/controllers/address_coin_balance_controller.ex
  3. 99
      apps/explorer/lib/explorer/chain.ex
  4. 48
      apps/explorer/lib/explorer/chain/address/coin_balance.ex

@ -4,6 +4,7 @@
- [#3125](https://github.com/poanetwork/blockscout/pull/3125) - Availability to configure a number of days to consider at coin balance history chart via environment variable
### Fixes
- [#3146](https://github.com/poanetwork/blockscout/pull/3146) - Fix coin balance history page: order of items, fix if no balance changes
- [#3142](https://github.com/poanetwork/blockscout/pull/3142) - Speed-up last coin balance timestamp query (coin balance history page performance improvement)
- [#3140](https://github.com/poanetwork/blockscout/pull/3140) - Fix performance of the balance changing history list loading
- [#3133](https://github.com/poanetwork/blockscout/pull/3133) - Take into account FIRST_BLOCK in trace_ReplayBlockTransactions requests

@ -23,16 +23,6 @@ defmodule BlockScoutWeb.AddressCoinBalanceController do
{coin_balances, next_page} = split_list_by_page(coin_balances_plus_one)
deduplicated_coin_balances =
coin_balances
|> Enum.dedup_by(fn record ->
if record.delta == Decimal.new(0) do
:dup
else
System.unique_integer()
end
end)
next_page_url =
case next_page_params(next_page, coin_balances, params) do
nil ->
@ -48,7 +38,7 @@ defmodule BlockScoutWeb.AddressCoinBalanceController do
end
coin_balances_json =
Enum.map(deduplicated_coin_balances, fn coin_balance ->
Enum.map(coin_balances, fn coin_balance ->
View.render_to_string(
AddressCoinBalanceView,
"_coin_balances.html",

@ -3499,56 +3499,63 @@ defmodule Explorer.Chain do
|> page_coin_balances(paging_options)
|> Repo.all()
min_block_number =
if Enum.empty?(balances_raw) do
balances_raw
|> Enum.min_by(fn balance -> balance.block_number end)
|> Map.get(:block_number)
max_block_number =
balances_raw
|> Enum.max_by(fn balance -> balance.block_number end)
|> Map.get(:block_number)
min_block_timestamp = find_block_timestamp(min_block_number)
max_block_timestamp = find_block_timestamp(max_block_number)
min_block_unix_timestamp =
min_block_timestamp
|> Timex.to_unix()
max_block_unix_timestamp =
max_block_timestamp
|> Timex.to_unix()
blocks_delta = max_block_number - min_block_number
balances_with_dates =
if blocks_delta > 0 do
balances_raw
|> Enum.map(fn balance ->
date =
trunc(
min_block_unix_timestamp +
(balance.block_number - min_block_number) * (max_block_unix_timestamp - min_block_unix_timestamp) /
blocks_delta
)
formatted_date = Timex.from_unix(date)
%{balance | block_timestamp: formatted_date}
end)
else
else
balances_raw_filtered =
balances_raw
|> Enum.map(fn balance ->
date = min_block_unix_timestamp
|> Enum.filter(fn balance -> balance.value end)
min_block_number =
balances_raw_filtered
|> Enum.min_by(fn balance -> balance.block_number end, fn -> %{} end)
|> Map.get(:block_number)
max_block_number =
balances_raw_filtered
|> Enum.max_by(fn balance -> balance.block_number end, fn -> %{} end)
|> Map.get(:block_number)
min_block_timestamp = find_block_timestamp(min_block_number)
max_block_timestamp = find_block_timestamp(max_block_number)
min_block_unix_timestamp =
min_block_timestamp
|> Timex.to_unix()
max_block_unix_timestamp =
max_block_timestamp
|> Timex.to_unix()
blocks_delta = max_block_number - min_block_number
balances_with_dates =
if blocks_delta > 0 do
balances_raw_filtered
|> Enum.map(fn balance ->
date =
trunc(
min_block_unix_timestamp +
(balance.block_number - min_block_number) * (max_block_unix_timestamp - min_block_unix_timestamp) /
blocks_delta
)
formatted_date = Timex.from_unix(date)
%{balance | block_timestamp: formatted_date}
end)
else
balances_raw_filtered
|> Enum.map(fn balance ->
date = min_block_unix_timestamp
formatted_date = Timex.from_unix(date)
%{balance | block_timestamp: formatted_date}
end)
end
formatted_date = Timex.from_unix(date)
%{balance | block_timestamp: formatted_date}
end)
end
balances_with_dates
|> Enum.filter(fn balance -> balance.value end)
|> Enum.sort(fn balance1, balance2 -> balance1.block_timestamp >= balance2.block_timestamp end)
balances_with_dates
|> Enum.sort(fn balance1, balance2 -> balance1.block_number >= balance2.block_number end)
end
end
def get_coin_balance(address_hash, block_number) do

@ -72,13 +72,18 @@ defmodule Explorer.Chain.Address.CoinBalance do
The last coin balance from an Address is the last block indexed.
"""
def fetch_coin_balances(address_hash, %PagingOptions{page_size: page_size}) do
from(
cb in CoinBalance,
where: cb.address_hash == ^address_hash,
where: not is_nil(cb.value),
order_by: [desc: :block_number],
limit: ^page_size,
select_merge: %{delta: fragment("value - coalesce(lead(value, 1) over (order by block_number desc), 0)")}
query =
from(
cb in CoinBalance,
where: cb.address_hash == ^address_hash,
where: not is_nil(cb.value),
order_by: [desc: :block_number],
select_merge: %{delta: fragment("value - coalesce(lead(value, 1) over (order by block_number desc), 0)")}
)
from(balance in subquery(query),
where: balance.delta != 0,
limit: ^page_size
)
end
@ -87,21 +92,40 @@ defmodule Explorer.Chain.Address.CoinBalance do
corresponds to the maximum balance in that day. Only the last 90 days of data are used.
"""
def balances_by_day(address_hash, block_timestamp \\ nil) do
{days_to_consider, _} =
Application.get_env(:block_scout_web, BlockScoutWeb.Chain.Address.CoinBalance)[:coin_balance_history_days]
|> Integer.parse()
CoinBalance
|> join(:inner, [cb], b in Block, on: cb.block_number == b.number)
|> where([cb], cb.address_hash == ^address_hash)
|> limit_time_interval(block_timestamp)
|> limit_time_interval(days_to_consider, block_timestamp)
|> group_by([cb, b], fragment("date_trunc('day', ?)", b.timestamp))
|> order_by([cb, b], fragment("date_trunc('day', ?)", b.timestamp))
|> select([cb, b], %{date: type(fragment("date_trunc('day', ?)", b.timestamp), :date), value: max(cb.value)})
end
def limit_time_interval(query, nil) do
query |> where([cb, b], b.timestamp >= fragment("date_trunc('day', now()) - interval '90 days'"))
def limit_time_interval(query, days_to_consider, nil) do
query
|> where(
[cb, b],
b.timestamp >=
fragment("date_trunc('day', now() - CAST(? AS INTERVAL))", ^%Postgrex.Interval{days: days_to_consider})
)
end
def limit_time_interval(query, %{timestamp: timestamp}) do
query |> where([cb, b], b.timestamp >= fragment("(? AT TIME ZONE ?) - interval '90 days'", ^timestamp, ^"Etc/UTC"))
def limit_time_interval(query, days_to_consider, %{timestamp: timestamp}) do
query
|> where(
[cb, b],
b.timestamp >=
fragment(
"(? AT TIME ZONE ?) - CAST(? AS INTERVAL)",
^timestamp,
^"Etc/UTC",
^%Postgrex.Interval{days: days_to_consider}
)
)
end
def last_coin_balance_timestamp(address_hash) do

Loading…
Cancel
Save