|
|
|
@ -23,19 +23,16 @@ defmodule Indexer.Block.Catchup.Fetcher do |
|
|
|
|
] |
|
|
|
|
|
|
|
|
|
alias Ecto.Changeset |
|
|
|
|
alias EthereumJSONRPC.Utility.RangesHelper |
|
|
|
|
alias Explorer.Chain |
|
|
|
|
alias Explorer.Chain.NullRoundHeight |
|
|
|
|
alias Explorer.Utility.{MassiveBlock, MissingRangesManipulator} |
|
|
|
|
alias Indexer.{Block, Tracer} |
|
|
|
|
alias Indexer.Block.Catchup.{Sequence, TaskSupervisor} |
|
|
|
|
alias Indexer.Memory.Shrinkable |
|
|
|
|
alias Indexer.Block.Catchup.TaskSupervisor |
|
|
|
|
alias Indexer.Prometheus |
|
|
|
|
|
|
|
|
|
@behaviour Block.Fetcher |
|
|
|
|
|
|
|
|
|
@shutdown_after :timer.minutes(5) |
|
|
|
|
@sequence_name :block_catchup_sequencer |
|
|
|
|
|
|
|
|
|
defstruct block_fetcher: nil, |
|
|
|
|
memory_monitor: nil |
|
|
|
|
|
|
|
|
@ -47,8 +44,9 @@ defmodule Indexer.Block.Catchup.Fetcher do |
|
|
|
|
""" |
|
|
|
|
def task(state) do |
|
|
|
|
Logger.metadata(fetcher: :block_catchup) |
|
|
|
|
Process.flag(:trap_exit, true) |
|
|
|
|
|
|
|
|
|
case MissingRangesManipulator.get_latest_batch() do |
|
|
|
|
case MissingRangesManipulator.get_latest_batch(blocks_batch_size() * blocks_concurrency()) do |
|
|
|
|
[] -> |
|
|
|
|
%{ |
|
|
|
|
first_block_number: nil, |
|
|
|
@ -57,9 +55,7 @@ defmodule Indexer.Block.Catchup.Fetcher do |
|
|
|
|
shrunk: false |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
latest_missing_ranges -> |
|
|
|
|
missing_ranges = filter_consensus_blocks(latest_missing_ranges) |
|
|
|
|
|
|
|
|
|
missing_ranges -> |
|
|
|
|
first.._ = List.first(missing_ranges) |
|
|
|
|
_..last = List.last(missing_ranges) |
|
|
|
|
|
|
|
|
@ -70,40 +66,17 @@ defmodule Indexer.Block.Catchup.Fetcher do |
|
|
|
|
|> Stream.map(&Enum.count/1) |
|
|
|
|
|> Enum.sum() |
|
|
|
|
|
|
|
|
|
step = step(first, last, blocks_batch_size()) |
|
|
|
|
sequence_opts = put_memory_monitor([ranges: missing_ranges, step: step], state) |
|
|
|
|
gen_server_opts = [name: @sequence_name] |
|
|
|
|
{:ok, sequence} = Sequence.start_link(sequence_opts, gen_server_opts) |
|
|
|
|
Sequence.cap(sequence) |
|
|
|
|
|
|
|
|
|
stream_fetch_and_import(state, sequence) |
|
|
|
|
|
|
|
|
|
shrunk = Shrinkable.shrunk?(sequence) |
|
|
|
|
stream_fetch_and_import(state, missing_ranges) |
|
|
|
|
|
|
|
|
|
%{ |
|
|
|
|
first_block_number: first, |
|
|
|
|
last_block_number: last, |
|
|
|
|
missing_block_count: missing_block_count, |
|
|
|
|
shrunk: shrunk |
|
|
|
|
shrunk: false |
|
|
|
|
} |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
defp filter_consensus_blocks(ranges) do |
|
|
|
|
filtered_ranges = |
|
|
|
|
ranges |
|
|
|
|
|> Enum.map(&Chain.missing_block_number_ranges(&1)) |
|
|
|
|
|> List.flatten() |
|
|
|
|
|
|
|
|
|
consensus_blocks = ranges_to_numbers(ranges) -- ranges_to_numbers(filtered_ranges) |
|
|
|
|
|
|
|
|
|
consensus_blocks |
|
|
|
|
|> numbers_to_ranges() |
|
|
|
|
|> MissingRangesManipulator.clear_batch() |
|
|
|
|
|
|
|
|
|
filtered_ranges |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
@doc """ |
|
|
|
|
The number of blocks to request in one call to the JSONRPC. Defaults to |
|
|
|
|
10. Block requests also include the transactions for those blocks. *These transactions |
|
|
|
@ -125,10 +98,6 @@ defmodule Indexer.Block.Catchup.Fetcher do |
|
|
|
|
Application.get_env(:indexer, __MODULE__)[:concurrency] |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
defp step(first, last, blocks_batch_size) do |
|
|
|
|
if first < last, do: blocks_batch_size, else: -1 * blocks_batch_size |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
@async_import_remaining_block_data_options ~w(address_hash_to_fetched_balance_block_number)a |
|
|
|
|
|
|
|
|
|
@impl Block.Fetcher |
|
|
|
@ -168,15 +137,14 @@ defmodule Indexer.Block.Catchup.Fetcher do |
|
|
|
|
async_import_blobs(imported) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
defp stream_fetch_and_import(state, sequence) |
|
|
|
|
when is_pid(sequence) do |
|
|
|
|
ranges = Sequence.build_stream(sequence) |
|
|
|
|
|
|
|
|
|
defp stream_fetch_and_import(state, ranges) do |
|
|
|
|
TaskSupervisor |
|
|
|
|
|> Task.Supervisor.async_stream(ranges, &fetch_and_import_range_from_sequence(state, &1, sequence), |
|
|
|
|
|> Task.Supervisor.async_stream( |
|
|
|
|
RangesHelper.split(ranges, blocks_batch_size()), |
|
|
|
|
&fetch_and_import_missing_range(state, &1), |
|
|
|
|
max_concurrency: blocks_concurrency(), |
|
|
|
|
timeout: :infinity, |
|
|
|
|
shutdown: @shutdown_after |
|
|
|
|
shutdown: Application.get_env(:indexer, :graceful_shutdown_period) |
|
|
|
|
) |
|
|
|
|
|> Stream.run() |
|
|
|
|
end |
|
|
|
@ -184,13 +152,12 @@ defmodule Indexer.Block.Catchup.Fetcher do |
|
|
|
|
# Run at state.blocks_concurrency max_concurrency when called by `stream_import/1` |
|
|
|
|
@decorate trace( |
|
|
|
|
name: "fetch", |
|
|
|
|
resource: "Indexer.Block.Catchup.Fetcher.fetch_and_import_range_from_sequence/3", |
|
|
|
|
resource: "Indexer.Block.Catchup.Fetcher.fetch_and_import_missing_range/3", |
|
|
|
|
tracer: Tracer |
|
|
|
|
) |
|
|
|
|
defp fetch_and_import_range_from_sequence( |
|
|
|
|
defp fetch_and_import_missing_range( |
|
|
|
|
%__MODULE__{block_fetcher: %Block.Fetcher{} = block_fetcher}, |
|
|
|
|
first..last = range, |
|
|
|
|
sequence |
|
|
|
|
first..last = range |
|
|
|
|
) do |
|
|
|
|
Logger.metadata(fetcher: :block_catchup, first_block_number: first, last_block_number: last) |
|
|
|
|
Process.flag(:trap_exit, true) |
|
|
|
@ -201,9 +168,7 @@ defmodule Indexer.Block.Catchup.Fetcher do |
|
|
|
|
|
|
|
|
|
case result do |
|
|
|
|
{:ok, %{inserted: inserted, errors: errors}} -> |
|
|
|
|
valid_errors = handle_null_rounds(errors) |
|
|
|
|
errors = cap_seq(sequence, valid_errors) |
|
|
|
|
retry(sequence, errors) |
|
|
|
|
handle_null_rounds(errors) |
|
|
|
|
clear_missing_ranges(range, errors) |
|
|
|
|
|
|
|
|
|
{:ok, inserted: inserted} |
|
|
|
@ -212,8 +177,6 @@ defmodule Indexer.Block.Catchup.Fetcher do |
|
|
|
|
Prometheus.Instrumenter.import_errors() |
|
|
|
|
Logger.error(fn -> ["failed to validate: ", inspect(changesets), ". Retrying."] end, step: step) |
|
|
|
|
|
|
|
|
|
push_back(sequence, range) |
|
|
|
|
|
|
|
|
|
error |
|
|
|
|
|
|
|
|
|
{:error, {:import = step, reason}} = error -> |
|
|
|
@ -221,8 +184,6 @@ defmodule Indexer.Block.Catchup.Fetcher do |
|
|
|
|
Logger.error(fn -> [inspect(reason), ". Retrying."] end, step: step) |
|
|
|
|
if reason == :timeout, do: add_range_to_massive_blocks(range) |
|
|
|
|
|
|
|
|
|
push_back(sequence, range) |
|
|
|
|
|
|
|
|
|
error |
|
|
|
|
|
|
|
|
|
{:error, {step, reason}} = error -> |
|
|
|
@ -233,8 +194,6 @@ defmodule Indexer.Block.Catchup.Fetcher do |
|
|
|
|
step: step |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
push_back(sequence, range) |
|
|
|
|
|
|
|
|
|
error |
|
|
|
|
|
|
|
|
|
{:error, {step, failed_value, _changes_so_far}} = error -> |
|
|
|
@ -245,8 +204,6 @@ defmodule Indexer.Block.Catchup.Fetcher do |
|
|
|
|
step: step |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
push_back(sequence, range) |
|
|
|
|
|
|
|
|
|
error |
|
|
|
|
end |
|
|
|
|
rescue |
|
|
|
@ -284,39 +241,6 @@ defmodule Indexer.Block.Catchup.Fetcher do |
|
|
|
|
|> MassiveBlock.insert_block_numbers() |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
defp cap_seq(seq, errors) do |
|
|
|
|
{not_founds, other_errors} = |
|
|
|
|
Enum.split_with(errors, fn |
|
|
|
|
%{code: 404, data: %{number: _}} -> true |
|
|
|
|
_ -> false |
|
|
|
|
end) |
|
|
|
|
|
|
|
|
|
case not_founds do |
|
|
|
|
[] -> |
|
|
|
|
Logger.debug("got blocks") |
|
|
|
|
|
|
|
|
|
other_errors |
|
|
|
|
|
|
|
|
|
_ -> |
|
|
|
|
Sequence.cap(seq) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
other_errors |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
defp push_back(sequence, range) do |
|
|
|
|
case Sequence.push_back(sequence, range) do |
|
|
|
|
:ok -> :ok |
|
|
|
|
{:error, reason} -> Logger.error(fn -> ["Could not push back to Sequence: ", inspect(reason)] end) |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
defp retry(sequence, block_errors) when is_list(block_errors) do |
|
|
|
|
block_errors |
|
|
|
|
|> block_errors_to_block_number_ranges() |
|
|
|
|
|> Enum.map(&push_back(sequence, &1)) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
defp clear_missing_ranges(initial_range, errors \\ []) do |
|
|
|
|
success_numbers = Enum.to_list(initial_range) -- Enum.map(errors, &block_error_to_number/1) |
|
|
|
|
|
|
|
|
@ -325,12 +249,6 @@ defmodule Indexer.Block.Catchup.Fetcher do |
|
|
|
|
|> MissingRangesManipulator.clear_batch() |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
defp block_errors_to_block_number_ranges(block_errors) when is_list(block_errors) do |
|
|
|
|
block_errors |
|
|
|
|
|> Enum.map(&block_error_to_number/1) |
|
|
|
|
|> numbers_to_ranges() |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
defp block_error_to_number(%{data: %{number: number}}) when is_integer(number), do: number |
|
|
|
|
|
|
|
|
|
defp numbers_to_ranges([]), do: [] |
|
|
|
@ -353,43 +271,4 @@ defmodule Indexer.Block.Catchup.Fetcher do |
|
|
|
|
fn range -> {:cont, range, nil} end |
|
|
|
|
) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
defp ranges_to_numbers(ranges) do |
|
|
|
|
ranges |
|
|
|
|
|> Enum.map(&Enum.to_list/1) |
|
|
|
|
|> List.flatten() |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
defp put_memory_monitor(sequence_options, %__MODULE__{memory_monitor: nil}) when is_list(sequence_options), |
|
|
|
|
do: sequence_options |
|
|
|
|
|
|
|
|
|
defp put_memory_monitor(sequence_options, %__MODULE__{memory_monitor: memory_monitor}) |
|
|
|
|
when is_list(sequence_options) do |
|
|
|
|
Keyword.put(sequence_options, :memory_monitor, memory_monitor) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
@doc """ |
|
|
|
|
Puts a list of block numbers to the front of the sequencing queue. |
|
|
|
|
""" |
|
|
|
|
@spec push_front([non_neg_integer()]) :: :ok | {:error, :queue_unavailable | :maximum_size | String.t()} |
|
|
|
|
def push_front(block_numbers) do |
|
|
|
|
if Process.whereis(@sequence_name) do |
|
|
|
|
Enum.reduce_while(block_numbers, :ok, fn block_number, :ok -> |
|
|
|
|
sequence_push_front(block_number) |
|
|
|
|
end) |
|
|
|
|
else |
|
|
|
|
{:error, :queue_unavailable} |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
defp sequence_push_front(block_number) do |
|
|
|
|
if is_integer(block_number) do |
|
|
|
|
case Sequence.push_front(@sequence_name, block_number..block_number) do |
|
|
|
|
:ok -> {:cont, :ok} |
|
|
|
|
{:error, _} = error -> {:halt, error} |
|
|
|
|
end |
|
|
|
|
else |
|
|
|
|
Logger.warn(fn -> ["Received a non-integer block number: ", inspect(block_number)] end) |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|