Use EXPLAIN in count query to speed up and get estimated row results

pull/753/head
Stamates 6 years ago
parent 742e543cdb
commit af6976317d
  1. 2
      apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex
  2. 8
      apps/block_scout_web/test/block_scout_web/features/viewing_addresses_test.exs
  3. 15
      apps/explorer/lib/explorer/chain.ex
  4. 58
      apps/explorer/test/explorer/chain_test.exs

@ -9,6 +9,6 @@ defmodule BlockScoutWeb.AddressController do
end
def transaction_count(%Address{} = address) do
Chain.address_to_transaction_count(address)
Chain.address_to_transaction_count_estimate(address)
end
end

@ -241,14 +241,6 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
end
end
test "viewing transaction count", %{addresses: addresses, session: session} do
insert_list(1000, :transaction, to_address: addresses.lincoln)
session
|> AddressPage.visit_page(addresses.lincoln)
|> assert_text(AddressPage.transaction_count(), "1,002")
end
test "contract creation is shown for to_address on list page", %{
addresses: addresses,
block: block,

@ -132,14 +132,15 @@ defmodule Explorer.Chain do
end
@doc """
Counts the number of `t:Explorer.Chain.Transaction.t/0` to or from the `address`.
Gets an estimated count of `t:Explorer.Chain.Transaction.t/0` to or from the `address` based on the estimated rows
resulting in an EXPLAIN of the query plan for the count query.
"""
@spec address_to_transaction_count(Address.t()) :: non_neg_integer()
def address_to_transaction_count(%Address{hash: address_hash}) do
{:ok, %{rows: [[result]]}} =
@spec address_to_transaction_count_estimate(Address.t()) :: non_neg_integer()
def address_to_transaction_count_estimate(%Address{hash: address_hash}) do
{:ok, %Postgrex.Result{rows: result}} =
Repo.query(
"""
SELECT COUNT(DISTINCT t.hash) FROM
EXPLAIN SELECT COUNT(DISTINCT t.hash) FROM
(
SELECT t0.hash FROM transactions AS t0 WHERE t0.from_address_hash = $1
UNION
@ -157,7 +158,9 @@ defmodule Explorer.Chain do
[address_hash.bytes]
)
result
{[unique_explain], _} = List.pop_at(result, 1)
[[_ | [rows]]] = Regex.scan(~r/rows=(\d+)/, unique_explain)
String.to_integer(rows)
end
@doc """

@ -29,64 +29,6 @@ defmodule Explorer.ChainTest do
end
end
describe "address_to_transaction_count/1" do
test "without transactions" do
address = insert(:address)
assert Chain.address_to_transaction_count(address) == 0
end
test "with transactions" do
%Transaction{from_address: address} = insert(:transaction) |> Repo.preload(:from_address)
insert(:transaction, to_address: address)
assert Chain.address_to_transaction_count(address) == 2
end
test "with contract creation transactions the contract address is counted" do
contract_address = insert(:contract_address)
insert(:transaction, to_address: nil, created_contract_address: contract_address)
assert Chain.address_to_transaction_count(contract_address) == 1
end
test "doesn't double count addresses when to_address = from_address" do
%Transaction{from_address: address} = insert(:transaction) |> Repo.preload(:from_address)
insert(:transaction, to_address: address, from_address: address)
assert Chain.address_to_transaction_count(address) == 2
end
test "does not count non-contract-creation parent transactions" do
transaction_with_to_address =
%Transaction{} =
:transaction
|> insert()
|> with_block()
%InternalTransaction{created_contract_address: address} =
insert(:internal_transaction_create, transaction: transaction_with_to_address, index: 0)
assert Chain.address_to_transaction_count(address) == 0
end
test "counts transactions with token transfers on to or from address" do
address = insert(:address)
insert(:token_transfer, to_address: address, transaction: build(:transaction))
insert(:token_transfer, from_address: address, transaction: build(:transaction))
assert Chain.address_to_transaction_count(address) == 2
end
test "does not double count transactions" do
address = insert(:address)
transaction = insert(:transaction, to_address: address, from_address: address)
insert(:token_transfer, to_address: address, transaction: transaction)
assert Chain.address_to_transaction_count(address) == 1
end
end
describe "address_to_transactions/2" do
test "without transactions" do
address = insert(:address)

Loading…
Cancel
Save