fix: Handle stalled async task in MapCache (#11015)

production-blackfort
Maxim Filonov 4 weeks ago committed by GitHub
parent d3d768a5ff
commit 684dd5d841
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 7
      apps/explorer/lib/explorer/chain/cache/address_sum.ex
  2. 7
      apps/explorer/lib/explorer/chain/cache/address_sum_minus_burnt.ex
  3. 7
      apps/explorer/lib/explorer/chain/cache/block.ex
  4. 7
      apps/explorer/lib/explorer/chain/cache/gas_price_oracle.ex
  5. 7
      apps/explorer/lib/explorer/chain/cache/gas_usage.ex
  6. 7
      apps/explorer/lib/explorer/chain/cache/pending_block_operation.ex
  7. 7
      apps/explorer/lib/explorer/chain/cache/transaction.ex
  8. 29
      apps/explorer/lib/explorer/chain/map_cache.ex

@ -17,9 +17,10 @@ defmodule Explorer.Chain.Cache.AddressSum do
alias Explorer.Etherscan alias Explorer.Etherscan
defp handle_fallback(:sum) do defp handle_fallback(:sum) do
# This will get the task PID if one exists and launch a new task if not # This will get the task PID if one exists, check if it's running and launch
# a new task if task doesn't exist or it's not running.
# See next `handle_fallback` definition # See next `handle_fallback` definition
get_async_task() safe_get_async_task()
{:return, Decimal.new(0)} {:return, Decimal.new(0)}
end end
@ -49,7 +50,7 @@ defmodule Explorer.Chain.Cache.AddressSum do
# By setting this as a `callback` an async task will be started each time the # By setting this as a `callback` an async task will be started each time the
# `sum` expires (unless there is one already running) # `sum` expires (unless there is one already running)
defp async_task_on_deletion({:delete, _, :sum}), do: get_async_task() defp async_task_on_deletion({:delete, _, :sum}), do: safe_get_async_task()
defp async_task_on_deletion(_data), do: nil defp async_task_on_deletion(_data), do: nil
end end

@ -17,9 +17,10 @@ defmodule Explorer.Chain.Cache.AddressSumMinusBurnt do
alias Explorer.Chain.Cache.Helper alias Explorer.Chain.Cache.Helper
defp handle_fallback(:sum_minus_burnt) do defp handle_fallback(:sum_minus_burnt) do
# This will get the task PID if one exists and launch a new task if not # This will get the task PID if one exists, check if it's running and launch
# a new task if task doesn't exist or it's not running.
# See next `handle_fallback` definition # See next `handle_fallback` definition
get_async_task() safe_get_async_task()
{:return, Decimal.new(0)} {:return, Decimal.new(0)}
end end
@ -56,7 +57,7 @@ defmodule Explorer.Chain.Cache.AddressSumMinusBurnt do
# By setting this as a `callback` an async task will be started each time the # By setting this as a `callback` an async task will be started each time the
# `sum_minus_burnt` expires (unless there is one already running) # `sum_minus_burnt` expires (unless there is one already running)
defp async_task_on_deletion({:delete, _, :sum_minus_burnt}), do: get_async_task() defp async_task_on_deletion({:delete, _, :sum_minus_burnt}), do: safe_get_async_task()
defp async_task_on_deletion(_data), do: nil defp async_task_on_deletion(_data), do: nil
end end

@ -56,9 +56,10 @@ defmodule Explorer.Chain.Cache.Block do
end end
defp handle_fallback(:count) do defp handle_fallback(:count) do
# This will get the task PID if one exists and launch a new task if not # This will get the task PID if one exists, check if it's running and launch
# a new task if task doesn't exist or it's not running.
# See next `handle_fallback` definition # See next `handle_fallback` definition
get_async_task() safe_get_async_task()
{:return, nil} {:return, nil}
end end
@ -95,7 +96,7 @@ defmodule Explorer.Chain.Cache.Block do
# By setting this as a `callback` an async task will be started each time the # By setting this as a `callback` an async task will be started each time the
# `count` expires (unless there is one already running) # `count` expires (unless there is one already running)
defp async_task_on_deletion({:delete, _, :count}), do: get_async_task() defp async_task_on_deletion({:delete, _, :count}), do: safe_get_async_task()
defp async_task_on_deletion(_data), do: nil defp async_task_on_deletion(_data), do: nil

@ -310,9 +310,10 @@ defmodule Explorer.Chain.Cache.GasPriceOracle do
defp fast_time_coefficient, do: Application.get_env(:explorer, __MODULE__)[:fast_time_coefficient] defp fast_time_coefficient, do: Application.get_env(:explorer, __MODULE__)[:fast_time_coefficient]
defp handle_fallback(:gas_prices) do defp handle_fallback(:gas_prices) do
# This will get the task PID if one exists and launch a new task if not # This will get the task PID if one exists, check if it's running and launch
# a new task if task doesn't exist or it's not running.
# See next `handle_fallback` definition # See next `handle_fallback` definition
get_async_task() safe_get_async_task()
{:return, get_old_gas_prices()} {:return, get_old_gas_prices()}
end end
@ -353,7 +354,7 @@ defmodule Explorer.Chain.Cache.GasPriceOracle do
# `gas_prices` expires (unless there is one already running) # `gas_prices` expires (unless there is one already running)
defp async_task_on_deletion({:delete, _, :gas_prices}) do defp async_task_on_deletion({:delete, _, :gas_prices}) do
set_old_gas_prices(get_gas_prices()) set_old_gas_prices(get_gas_prices())
get_async_task() safe_get_async_task()
end end
defp async_task_on_deletion(_data), do: nil defp async_task_on_deletion(_data), do: nil

@ -37,9 +37,10 @@ defmodule Explorer.Chain.Cache.GasUsage do
end end
defp handle_fallback(:sum) do defp handle_fallback(:sum) do
# This will get the task PID if one exists and launch a new task if not # This will get the task PID if one exists, check if it's running and launch
# a new task if task doesn't exist or it's not running.
# See next `handle_fallback` definition # See next `handle_fallback` definition
get_async_task() safe_get_async_task()
{:return, nil} {:return, nil}
end end
@ -73,7 +74,7 @@ defmodule Explorer.Chain.Cache.GasUsage do
# By setting this as a `callback` an async task will be started each time the # By setting this as a `callback` an async task will be started each time the
# `sum` expires (unless there is one already running) # `sum` expires (unless there is one already running)
defp async_task_on_deletion({:delete, _, :sum}), do: get_async_task() defp async_task_on_deletion({:delete, _, :sum}), do: safe_get_async_task()
defp async_task_on_deletion(_data), do: nil defp async_task_on_deletion(_data), do: nil

@ -35,9 +35,10 @@ defmodule Explorer.Chain.Cache.PendingBlockOperation do
end end
defp handle_fallback(:count) do defp handle_fallback(:count) do
# This will get the task PID if one exists and launch a new task if not # This will get the task PID if one exists, check if it's running and launch
# a new task if task doesn't exist or it's not running.
# See next `handle_fallback` definition # See next `handle_fallback` definition
get_async_task() safe_get_async_task()
{:return, nil} {:return, nil}
end end
@ -67,7 +68,7 @@ defmodule Explorer.Chain.Cache.PendingBlockOperation do
# By setting this as a `callback` an async task will be started each time the # By setting this as a `callback` an async task will be started each time the
# `count` expires (unless there is one already running) # `count` expires (unless there is one already running)
defp async_task_on_deletion({:delete, _, :count}), do: get_async_task() defp async_task_on_deletion({:delete, _, :count}), do: safe_get_async_task()
defp async_task_on_deletion(_data), do: nil defp async_task_on_deletion(_data), do: nil
end end

@ -36,9 +36,10 @@ defmodule Explorer.Chain.Cache.Transaction do
end end
defp handle_fallback(:count) do defp handle_fallback(:count) do
# This will get the task PID if one exists and launch a new task if not # This will get the task PID if one exists, check if it's running and launch
# a new task if task doesn't exist or it's not running.
# See next `handle_fallback` definition # See next `handle_fallback` definition
get_async_task() safe_get_async_task()
{:return, nil} {:return, nil}
end end
@ -68,7 +69,7 @@ defmodule Explorer.Chain.Cache.Transaction do
# By setting this as a `callback` an async task will be started each time the # By setting this as a `callback` an async task will be started each time the
# `count` expires (unless there is one already running) # `count` expires (unless there is one already running)
defp async_task_on_deletion({:delete, _, :count}), do: get_async_task() defp async_task_on_deletion({:delete, _, :count}), do: safe_get_async_task()
defp async_task_on_deletion(_data), do: nil defp async_task_on_deletion(_data), do: nil
end end

@ -201,6 +201,35 @@ defmodule Explorer.Chain.MapCache do
end end
end end
defp named_functions(:async_task) do
quote do
def get_async_task, do: get(:async_task)
@doc """
Checks if the asynchronous task has stalled (has finished, but still in cache),
and if so, creates a new replacement task to continue the operation.
"""
def safe_get_async_task do
case get_async_task() do
pid when is_pid(pid) ->
if Process.alive?(pid) do
pid
else
set_async_task(nil)
get_async_task()
end
not_pid ->
not_pid
end
end
def set_async_task(value), do: set(:async_task, value)
def update_async_task(value), do: update(:async_task, value)
end
end
# sobelow_skip ["DOS"] # sobelow_skip ["DOS"]
defp named_functions(key) do defp named_functions(key) do
quote do quote do

Loading…
Cancel
Save