fetch token counters in parallel

the slowest part of the token overview page is
fetching of total token transfers and total token
holders counters queries. Sometimes they even cause
page load failures.

Now we will start fetching these counters in parallel and
limiting query execution to 40 seconds. If they fail
to load, we won't show these counters.
pull/2666/head
Ayrat Badykov 5 years ago
parent cf6592d168
commit 36c1aa9b67
No known key found for this signature in database
GPG Key ID: B44668E265E9396F
  1. 8
      apps/block_scout_web/lib/block_scout_web/controllers/tokens/holder_controller.ex
  2. 7
      apps/block_scout_web/lib/block_scout_web/controllers/tokens/inventory_controller.ex
  3. 8
      apps/block_scout_web/lib/block_scout_web/controllers/tokens/read_contract_controller.ex
  4. 34
      apps/block_scout_web/lib/block_scout_web/controllers/tokens/token_controller.ex
  5. 7
      apps/block_scout_web/lib/block_scout_web/controllers/tokens/transfer_controller.ex
  6. 4
      apps/block_scout_web/lib/block_scout_web/templates/tokens/overview/_details.html.eex

@ -12,6 +12,8 @@ defmodule BlockScoutWeb.Tokens.HolderController do
next_page_params: 3 next_page_params: 3
] ]
import BlockScoutWeb.Tokens.TokenController, only: [fetch_token_counters: 2]
def index(conn, %{"token_id" => address_hash_string, "type" => "JSON"} = params) do def index(conn, %{"token_id" => address_hash_string, "type" => "JSON"} = params) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, token} <- Chain.token_from_address_hash(address_hash), {:ok, token} <- Chain.token_from_address_hash(address_hash),
@ -47,13 +49,15 @@ defmodule BlockScoutWeb.Tokens.HolderController do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, token} <- Chain.token_from_address_hash(address_hash, options) do {:ok, token} <- Chain.token_from_address_hash(address_hash, options) do
{total_token_transfers, total_token_holders} = fetch_token_counters(token, address_hash)
render( render(
conn, conn,
"index.html", "index.html",
current_path: current_path(conn), current_path: current_path(conn),
token: Market.add_price(token), token: Market.add_price(token),
total_token_holders: token.holder_count || Chain.count_token_holders_from_token_hash(address_hash), total_token_holders: total_token_holders,
total_token_transfers: Chain.count_token_transfers_from_token_hash(address_hash) total_token_transfers: total_token_transfers
) )
else else
:error -> :error ->

@ -7,6 +7,7 @@ defmodule BlockScoutWeb.Tokens.InventoryController do
alias Phoenix.View alias Phoenix.View
import BlockScoutWeb.Chain, only: [split_list_by_page: 1, default_paging_options: 0] import BlockScoutWeb.Chain, only: [split_list_by_page: 1, default_paging_options: 0]
import BlockScoutWeb.Tokens.TokenController, only: [fetch_token_counters: 2]
def index(conn, %{"token_id" => address_hash_string, "type" => "JSON"} = params) do def index(conn, %{"token_id" => address_hash_string, "type" => "JSON"} = params) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
@ -64,13 +65,15 @@ defmodule BlockScoutWeb.Tokens.InventoryController do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, token} <- Chain.token_from_address_hash(address_hash, options) do {:ok, token} <- Chain.token_from_address_hash(address_hash, options) do
{total_token_transfers, total_token_holders} = fetch_token_counters(token, address_hash)
render( render(
conn, conn,
"index.html", "index.html",
current_path: current_path(conn), current_path: current_path(conn),
token: Market.add_price(token), token: Market.add_price(token),
total_token_transfers: Chain.count_token_transfers_from_token_hash(address_hash), total_token_transfers: total_token_transfers,
total_token_holders: token.holder_count || Chain.count_token_holders_from_token_hash(address_hash) total_token_holders: total_token_holders
) )
else else
:error -> :error ->

@ -3,17 +3,21 @@ defmodule BlockScoutWeb.Tokens.ReadContractController do
alias Explorer.{Chain, Market} alias Explorer.{Chain, Market}
import BlockScoutWeb.Tokens.TokenController, only: [fetch_token_counters: 2]
def index(conn, %{"token_id" => address_hash_string}) do def index(conn, %{"token_id" => address_hash_string}) do
options = [necessity_by_association: %{[contract_address: :smart_contract] => :optional}] options = [necessity_by_association: %{[contract_address: :smart_contract] => :optional}]
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, token} <- Chain.token_from_address_hash(address_hash, options) do {:ok, token} <- Chain.token_from_address_hash(address_hash, options) do
{total_token_transfers, total_token_holders} = fetch_token_counters(token, address_hash)
render( render(
conn, conn,
"index.html", "index.html",
token: Market.add_price(token), token: Market.add_price(token),
total_token_transfers: token.holder_count || Chain.count_token_transfers_from_token_hash(address_hash), total_token_transfers: total_token_transfers,
total_token_holders: Chain.count_token_holders_from_token_hash(address_hash) total_token_holders: total_token_holders
) )
else else
:error -> :error ->

@ -1,7 +1,41 @@
defmodule BlockScoutWeb.Tokens.TokenController do defmodule BlockScoutWeb.Tokens.TokenController do
use BlockScoutWeb, :controller use BlockScoutWeb, :controller
require Logger
alias Explorer.Chain
def show(conn, %{"id" => address_hash_string}) do def show(conn, %{"id" => address_hash_string}) do
redirect(conn, to: token_transfer_path(conn, :index, address_hash_string)) redirect(conn, to: token_transfer_path(conn, :index, address_hash_string))
end end
def fetch_token_counters(token, address_hash) do
total_token_transfers_task =
Task.async(fn ->
Chain.count_token_transfers_from_token_hash(address_hash)
end)
total_token_holders_task =
Task.async(fn ->
token.holder_count || Chain.count_token_holders_from_token_hash(address_hash)
end)
[total_token_transfers_task, total_token_holders_task]
|> Task.yield_many(:timer.seconds(40))
|> Enum.map(fn {_task, res} ->
case res do
{:ok, result} ->
result
{:exit, reason} ->
Logger.warn("Query fetching token counters terminated: #{inspect(reason)}")
0
nil ->
Logger.warn("Query fetching token counters timed out.")
0
end
end)
|> List.to_tuple()
end
end end

@ -6,6 +6,7 @@ defmodule BlockScoutWeb.Tokens.TransferController do
alias Phoenix.View alias Phoenix.View
import BlockScoutWeb.Chain, only: [split_list_by_page: 1, paging_options: 1, next_page_params: 3] import BlockScoutWeb.Chain, only: [split_list_by_page: 1, paging_options: 1, next_page_params: 3]
import BlockScoutWeb.Tokens.TokenController, only: [fetch_token_counters: 2]
def index(conn, %{"token_id" => address_hash_string, "type" => "JSON"} = params) do def index(conn, %{"token_id" => address_hash_string, "type" => "JSON"} = params) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
@ -48,13 +49,15 @@ defmodule BlockScoutWeb.Tokens.TransferController do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, token} <- Chain.token_from_address_hash(address_hash, options) do {:ok, token} <- Chain.token_from_address_hash(address_hash, options) do
{total_token_transfers, total_token_holders} = fetch_token_counters(token, address_hash)
render( render(
conn, conn,
"index.html", "index.html",
current_path: current_path(conn), current_path: current_path(conn),
token: Market.add_price(token), token: Market.add_price(token),
total_token_transfers: Chain.count_token_transfers_from_token_hash(address_hash), total_token_transfers: total_token_transfers,
total_token_holders: token.holder_count || Chain.count_token_holders_from_token_hash(address_hash) total_token_holders: total_token_holders
) )
else else
:error -> :error ->

@ -57,8 +57,12 @@
</span> </span>
<div class="d-flex flex-row justify-content-start text-muted"> <div class="d-flex flex-row justify-content-start text-muted">
<span class="mr-4"> <%= @token.type %> </span> <span class="mr-4"> <%= @token.type %> </span>
<%= if @total_token_holders > 0 do %>
<span class="mr-4"><%= @total_token_holders %> <%= gettext "Addresses" %></span> <span class="mr-4"><%= @total_token_holders %> <%= gettext "Addresses" %></span>
<% end %>
<%= if @total_token_transfers > 0 do %>
<span class="mr-4"><%= @total_token_transfers %> <%= gettext "Transfers" %></span> <span class="mr-4"><%= @total_token_transfers %> <%= gettext "Transfers" %></span>
<% end %>
<%= if decimals?(@token) do %> <%= if decimals?(@token) do %>
<span class="mr-4"><%= @token.decimals %> <%= gettext "Decimals" %></span> <span class="mr-4"><%= @token.decimals %> <%= gettext "Decimals" %></span>
<% end %> <% end %>

Loading…
Cancel
Save