Rewrite missing blocks range query

pull/3440/head
Victor Baranov 4 years ago
parent f3e4e0081c
commit 15ffd1c996
  1. 1
      CHANGELOG.md
  2. 104
      apps/explorer/lib/explorer/chain.ex

@ -17,6 +17,7 @@
### Fixes ### Fixes
- [#3440](https://github.com/poanetwork/blockscout/pull/3440) - Rewrite missing blocks range query
- [#3439](https://github.com/poanetwork/blockscout/pull/3439) - Dark mode color fixes (search, charts) - [#3439](https://github.com/poanetwork/blockscout/pull/3439) - Dark mode color fixes (search, charts)
- [#3437](https://github.com/poanetwork/blockscout/pull/3437) - Fix Postgres Docker container - [#3437](https://github.com/poanetwork/blockscout/pull/3437) - Fix Postgres Docker container
- [#3428](https://github.com/poanetwork/blockscout/pull/3428) - Fix address tokens search - [#3428](https://github.com/poanetwork/blockscout/pull/3428) - Fix address tokens search

@ -17,7 +17,6 @@ defmodule Explorer.Chain do
select: 3, select: 3,
subquery: 1, subquery: 1,
union: 2, union: 2,
union_all: 2,
where: 2, where: 2,
where: 3 where: 3
] ]
@ -2456,66 +2455,73 @@ defmodule Explorer.Chain do
range_min = min(range_start, range_end) range_min = min(range_start, range_end)
range_max = max(range_start, range_end) range_max = max(range_start, range_end)
missing_prefix_query = ordered_missing_query =
from(block in Block, from(b in Block,
select: %{min: type(^range_min, block.number), max: min(block.number) - 1}, right_join:
where: block.consensus == true, missing_range in fragment(
having: ^range_min < min(block.number) and min(block.number) < ^range_max """
) (SELECT distinct b1.number
FROM generate_series((?)::integer, (?)::integer) AS b1(number)
missing_suffix_query = WHERE NOT EXISTS
from(block in Block, (SELECT 1 FROM blocks b2 WHERE b2.number=b1.number AND b2.consensus))
select: %{min: max(block.number) + 1, max: type(^range_max, block.number)}, """,
where: block.consensus == true, ^range_min,
having: ^range_min < max(block.number) and max(block.number) < ^range_max ^range_max
),
on: b.number == missing_range.number,
select: missing_range.number,
order_by: missing_range.number,
distinct: missing_range.number
) )
missing_infix_query = missing_blocks = Repo.all(ordered_missing_query, timeout: :infinity)
from(block in Block,
select: %{min: type(^range_min, block.number), max: type(^range_max, block.number)},
where: block.consensus == true,
having:
(is_nil(min(block.number)) and is_nil(max(block.number))) or
(^range_max < min(block.number) or max(block.number) < ^range_min)
)
# Gaps and Islands is the term-of-art for finding the runs of missing (gaps) and existing (islands) data. If you [block_ranges, last_block_range_start, last_block_range_end] =
# Google for `sql missing ranges` you won't find much, but `sql gaps and islands` will get a lot of hits. missing_blocks
|> Enum.reduce([[], nil, nil], fn block_number, [block_ranges, last_block_range_start, last_block_range_end] ->
cond do
!last_block_range_start ->
[block_ranges, block_number, block_number]
land_query = block_number == last_block_range_end + 1 ->
from(block in Block, [block_ranges, last_block_range_start, block_number]
where: block.consensus == true and ^range_min <= block.number and block.number <= ^range_max,
windows: [w: [order_by: block.number]],
select: %{last_number: block.number |> lag() |> over(:w), next_number: block.number}
)
gap_query = true ->
from( block_ranges = block_ranges_extend(block_ranges, last_block_range_start, last_block_range_end)
coastline in subquery(land_query), [block_ranges, block_number, block_number]
where: coastline.last_number != coastline.next_number - 1, end
select: %{min: coastline.last_number + 1, max: coastline.next_number - 1} end)
)
missing_query = final_block_ranges =
missing_prefix_query if last_block_range_start && last_block_range_end do
|> union_all(^missing_infix_query) block_ranges_extend(block_ranges, last_block_range_start, last_block_range_end)
|> union_all(^gap_query) else
|> union_all(^missing_suffix_query) block_ranges
end
{first, last, direction} = ordered_block_ranges =
final_block_ranges
|> Enum.sort(fn %Range{first: first1, last: _}, %Range{first: first2, last: _} ->
if range_start <= range_end do if range_start <= range_end do
{:min, :max, :asc} first1 <= first2
else else
{:max, :min, :desc} first1 >= first2
end end
end)
|> Enum.map(fn %Range{first: first, last: last} = range ->
if range_start <= range_end do
range
else
%Range{first: last, last: first}
end
end)
ordered_missing_query = ordered_block_ranges
from(missing_range in subquery(missing_query), end
select: %Range{first: field(missing_range, ^first), last: field(missing_range, ^last)},
order_by: [{^direction, field(missing_range, ^first)}]
)
Repo.all(ordered_missing_query, timeout: :infinity) defp block_ranges_extend(block_ranges, block_range_start, block_range_end) do
# credo:disable-for-next-line
block_ranges ++ [Range.new(block_range_start, block_range_end)]
end end
@doc """ @doc """

Loading…
Cancel
Save