Ensure min and max block number is for consensus blocks

Adding `WHERE consensus = true` both fixes the bug that
`min_block_number` and `max_block_number` were including non-consensus
blocks, but it also makes the query faster because there is an index
for consensus blocks.

Times from Eth Mainnet production:

```
ether=> SELECT min(b0."number") FROM "blocks" AS b0;
 min
-----
   0
(1 row)

Time: 414.218 ms
ether=> EXPLAIN SELECT min(b0."number") FROM "blocks" AS b0;
                                          QUERY PLAN
-----------------------------------------------------------------------------------------------
 Finalize Aggregate  (cost=223454.95..223454.96 rows=1 width=8)
   ->  Gather  (cost=223454.73..223454.95 rows=2 width=8)
         Workers Planned: 2
         ->  Partial Aggregate  (cost=222454.73..222454.74 rows=1 width=8)
               ->  Parallel Seq Scan on blocks b0  (cost=0.00..215051.99 rows=2961099 width=8)
(5 rows)

Time: 1.040 ms
ether=> EXPLAIN SELECT min(b0."number") FROM "blocks" AS b0 WHERE b0.consensus = true;
                                                         QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------
 Result  (cost=0.45..0.46 rows=1 width=8)
   InitPlan 1 (returns $0)
     ->  Limit  (cost=0.43..0.45 rows=1 width=8)
           ->  Index Only Scan using one_consensus_block_at_height on blocks b0  (cost=0.43..158050.90 rows=7045520 width=8)
                 Index Cond: (number IS NOT NULL)
(5 rows)

Time: 3.350 ms
ether=> SELECT min(b0."number") FROM "blocks" AS b0 WHERE b0.consensus = true;
 min
-----
   0
(1 row)

Time: 1.059 ms
```
pull/1332/head
Luke Imhoff 6 years ago
parent 44f50b161d
commit 083e5d3278
  1. 2
      apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/logs_controller.ex
  2. 2
      apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/transaction_controller.ex
  3. 2
      apps/block_scout_web/lib/block_scout_web/controllers/block_transaction_controller.ex
  4. 2
      apps/block_scout_web/lib/block_scout_web/controllers/transaction_internal_transaction_controller.ex
  5. 2
      apps/block_scout_web/lib/block_scout_web/controllers/transaction_log_controller.ex
  6. 2
      apps/block_scout_web/lib/block_scout_web/controllers/transaction_token_transfer_controller.ex
  7. 54
      apps/explorer/lib/explorer/chain.ex
  8. 2
      apps/explorer/lib/explorer/chain/supply/proof_of_authority.ex
  9. 4
      apps/explorer/lib/explorer/etherscan.ex
  10. 2
      apps/explorer/test/explorer/chain_test.exs
  11. 4
      apps/explorer/test/explorer/etherscan_test.exs
  12. 2
      apps/indexer/lib/indexer.ex

@ -183,7 +183,7 @@ defmodule BlockScoutWeb.API.RPC.LogsController do
defp to_block_number(params, param_key) do
case params[param_key] do
"latest" ->
Chain.max_block_number()
Chain.consensus_block_number(:max)
_ ->
to_integer(params, param_key)

@ -83,7 +83,7 @@ defmodule BlockScoutWeb.API.RPC.TransactionController do
end
defp max_block_number do
case Chain.max_block_number() do
case Chain.consensus_block_number(:max) do
{:ok, number} -> number
{:error, :not_found} -> 0
end

@ -82,7 +82,7 @@ defmodule BlockScoutWeb.BlockTransactionController do
defp block_above_tip?("0x" <> _), do: nil
defp block_above_tip?(block_hash_or_number) when is_binary(block_hash_or_number) do
with {:ok, max_block_number} <- Chain.max_block_number() do
with {:ok, max_block_number} <- Chain.consensus_block_number(:max) do
{block_number, _} = Integer.parse(block_hash_or_number)
block_number > max_block_number
else

@ -65,7 +65,7 @@ defmodule BlockScoutWeb.TransactionInternalTransactionController do
end
defp max_block_number do
case Chain.max_block_number() do
case Chain.consensus_block_number(:max) do
{:ok, number} -> number
{:error, :not_found} -> 0
end

@ -61,7 +61,7 @@ defmodule BlockScoutWeb.TransactionLogController do
end
defp max_block_number do
case Chain.max_block_number() do
case Chain.consensus_block_number(:max) do
{:ok, number} -> number
{:error, :not_found} -> 0
end

@ -65,7 +65,7 @@ defmodule BlockScoutWeb.TransactionTokenTransferController do
end
defp max_block_number do
case Chain.max_block_number() do
case Chain.consensus_block_number(:max) do
{:ok, number} -> number
{:error, :not_found} -> 0
end

@ -845,8 +845,8 @@ defmodule Explorer.Chain do
"""
@spec indexed_ratio() :: float()
def indexed_ratio do
with {:ok, min_block_number} <- min_block_number(),
{:ok, max_block_number} <- max_block_number() do
with {:ok, min_block_number} <- consensus_block_number(:min),
{:ok, max_block_number} <- consensus_block_number(:max) do
indexed_blocks = max_block_number - min_block_number + 1
indexed_blocks / (max_block_number + 1)
else
@ -1156,48 +1156,40 @@ defmodule Explorer.Chain do
end
@doc """
The maximum `t:Explorer.Chain.Block.t/0` `number`
Aggregate of consensus block numbers.
If blocks are skipped and inserted out of number order, the max number is still returned
If blocks are skipped and inserted out of number order, the `:max` and `:min` numbers are still returned
iex> insert(:block, number: 2)
iex> insert(:block, number: 1)
iex> Explorer.Chain.max_block_number()
iex> Explorer.Chain.consensus_block_number(:min)
{:ok, 1}
iex> Explorer.Chain.consensus_block_number(:max)
{:ok, 2}
If there are no blocks, `{:error, :not_found}` is returned
iex> Explorer.Chain.max_block_number()
{:error, :not_found}
"""
@spec max_block_number() :: {:ok, Block.block_number()} | {:error, :not_found}
def max_block_number do
case Repo.aggregate(Block, :max, :number) do
nil -> {:error, :not_found}
number -> {:ok, number}
end
end
@doc """
The minimum `t:Explorer.Chain.Block.t/0` `number` (used to show loading status while indexing)
If blocks are skipped and inserted out of number order, the min number is still returned
Non-consensus blocks are ignored
iex> insert(:block, number: 2)
iex> insert(:block, number: 1)
iex> Explorer.Chain.min_block_number()
{:ok, 1}
iex> insert(:block, number: 3, consensus: false)
iex> insert(:block, number: 2, consensus: true)
iex> insert(:block, number: 1, consensus: false)
iex> Explorer.Chain.consensus_block_number(:min)
{:ok, 2}
iex> Explorer.Chain.consensus_block_number(:max)
{:ok, 2}
If there are no blocks, `{:error, :not_found}` is returned
iex> Explorer.Chain.min_block_number()
iex> Explorer.Chain.consensus_block_number(:min)
{:error, :not_found}
iex> Explorer.Chain.consensus_block_number(:max)
{:error, :not_found}
"""
@spec min_block_number() :: {:ok, Block.block_number()} | {:error, :not_found}
def min_block_number do
case Repo.aggregate(Block, :min, :number) do
def consensus_block_number(aggregate) do
Block
|> where(consensus: true)
|> Repo.aggregate(aggregate, :number)
|> case do
nil -> {:error, :not_found}
number -> {:ok, number}
end

@ -41,7 +41,7 @@ defmodule Explorer.Chain.Supply.ProofOfAuthority do
end
defp block_height do
case Chain.max_block_number() do
case Chain.consensus_block_number(:max) do
{:ok, height} -> height
_ -> 0
end

@ -43,7 +43,7 @@ defmodule Explorer.Etherscan do
%Hash{byte_count: unquote(Hash.Address.byte_count())} = address_hash,
options \\ @default_options
) do
case Chain.max_block_number() do
case Chain.consensus_block_number(:max) do
{:ok, max_block_number} ->
merged_options = Map.merge(@default_options, options)
list_transactions(address_hash, max_block_number, merged_options)
@ -152,7 +152,7 @@ defmodule Explorer.Etherscan do
contract_address_hash,
options \\ @default_options
) do
case Chain.max_block_number() do
case Chain.consensus_block_number(:max) do
{:ok, max_block_number} ->
merged_options = Map.merge(@default_options, options)
list_token_transfers(address_hash, contract_address_hash, max_block_number, merged_options)

@ -594,7 +594,7 @@ defmodule Explorer.ChainTest do
describe "confirmations/1" do
test "with block.number == max_block_number " do
block = insert(:block)
{:ok, max_block_number} = Chain.max_block_number()
{:ok, max_block_number} = Chain.consensus_block_number(:max)
assert block.number == max_block_number
assert Chain.confirmations(block, max_block_number: max_block_number) == 0

@ -122,7 +122,7 @@ defmodule Explorer.EtherscanTest do
[found_transaction] = Etherscan.list_transactions(address.hash)
{:ok, max_block_number} = Chain.max_block_number()
{:ok, max_block_number} = Chain.consensus_block_number(:max)
expected_confirmations = max_block_number - transaction.block_number
assert found_transaction.confirmations == expected_confirmations
@ -888,7 +888,7 @@ defmodule Explorer.EtherscanTest do
[found_token_transfer] = Etherscan.list_token_transfers(token_transfer.from_address_hash, nil)
{:ok, max_block_number} = Chain.max_block_number()
{:ok, max_block_number} = Chain.consensus_block_number(:max)
expected_confirmations = max_block_number - transaction.block_number
assert found_token_transfer.confirmations == expected_confirmations

@ -22,7 +22,7 @@ defmodule Indexer do
"""
def max_block_number do
case Chain.max_block_number() do
case Chain.consensus_block_number(:max) do
{:ok, number} -> number
{:error, :not_found} -> 0
end

Loading…
Cancel
Save