Add last output root size counter (#9532)
parent
0eb7501211
commit
208192d918
@ -0,0 +1,112 @@ |
||||
defmodule Explorer.Counters.LastOutputRootSizeCounter do |
||||
@moduledoc """ |
||||
Caches number of transactions in last output root. |
||||
|
||||
It loads the count asynchronously and in a time interval of :cache_period (default to 5 minutes). |
||||
""" |
||||
|
||||
use GenServer |
||||
|
||||
import Ecto.Query |
||||
|
||||
alias Explorer.{Chain, Repo} |
||||
alias Explorer.Chain.Optimism.OutputRoot |
||||
alias Explorer.Chain.Transaction |
||||
|
||||
@counter_type "last_output_root_size_count" |
||||
|
||||
@doc """ |
||||
Starts a process to periodically update the counter. |
||||
""" |
||||
@spec start_link(term()) :: GenServer.on_start() |
||||
def start_link(_) do |
||||
GenServer.start_link(__MODULE__, :ok, name: __MODULE__) |
||||
end |
||||
|
||||
@impl true |
||||
def init(_args) do |
||||
{:ok, %{consolidate?: enable_consolidation?()}, {:continue, :ok}} |
||||
end |
||||
|
||||
defp schedule_next_consolidation do |
||||
Process.send_after(self(), :consolidate, cache_interval()) |
||||
end |
||||
|
||||
@impl true |
||||
def handle_continue(:ok, %{consolidate?: true} = state) do |
||||
consolidate() |
||||
schedule_next_consolidation() |
||||
|
||||
{:noreply, state} |
||||
end |
||||
|
||||
@impl true |
||||
def handle_continue(:ok, state) do |
||||
{:noreply, state} |
||||
end |
||||
|
||||
@impl true |
||||
def handle_info(:consolidate, state) do |
||||
consolidate() |
||||
schedule_next_consolidation() |
||||
|
||||
{:noreply, state} |
||||
end |
||||
|
||||
@doc """ |
||||
Fetches the value for a `#{@counter_type}` counter type from the `last_fetched_counters` table. |
||||
""" |
||||
def fetch(options) do |
||||
Chain.get_last_fetched_counter(@counter_type, options |> Keyword.put_new(:nullable, true)) |
||||
end |
||||
|
||||
@doc """ |
||||
Consolidates the info by populating the `last_fetched_counters` table with the current database information. |
||||
""" |
||||
def consolidate do |
||||
output_root_query = |
||||
from(root in OutputRoot, |
||||
select: {root.l2_block_number}, |
||||
order_by: [desc: root.l2_output_index], |
||||
limit: 2 |
||||
) |
||||
|
||||
count = |
||||
case output_root_query |> Repo.all() do |
||||
[{last_block_number}, {prev_block_number}] -> |
||||
query = |
||||
from(transaction in Transaction, |
||||
where: |
||||
not is_nil(transaction.block_hash) and transaction.block_number > ^prev_block_number and |
||||
transaction.block_number <= ^last_block_number, |
||||
select: count(transaction.hash) |
||||
) |
||||
|
||||
Repo.one!(query, timeout: :infinity) |
||||
|
||||
_ -> |
||||
nil |
||||
end |
||||
|
||||
Chain.upsert_last_fetched_counter(%{ |
||||
counter_type: @counter_type, |
||||
value: count |
||||
}) |
||||
end |
||||
|
||||
@doc """ |
||||
Returns a boolean that indicates whether consolidation is enabled |
||||
|
||||
In order to choose whether or not to enable the scheduler and the initial |
||||
consolidation, change the following Explorer config: |
||||
|
||||
`config :explorer, #{__MODULE__}, enable_consolidation: true` |
||||
|
||||
to: |
||||
|
||||
`config :explorer, #{__MODULE__}, enable_consolidation: false` |
||||
""" |
||||
def enable_consolidation?, do: Application.get_env(:explorer, __MODULE__)[:enable_consolidation] |
||||
|
||||
defp cache_interval, do: Application.get_env(:explorer, __MODULE__)[:cache_period] |
||||
end |
@ -0,0 +1,47 @@ |
||||
defmodule Explorer.Counters.LastOutputRootSizeCounterTest do |
||||
use Explorer.DataCase |
||||
|
||||
alias Explorer.Counters.LastOutputRootSizeCounter |
||||
|
||||
if Application.compile_env(:explorer, :chain_type) == "optimism" do |
||||
test "populates the cache with the number of transactions in last output root" do |
||||
first_block = insert(:block) |
||||
|
||||
insert(:op_output_root, l2_block_number: first_block.number) |
||||
|
||||
second_block = insert(:block, number: first_block.number + 10) |
||||
insert(:op_output_root, l2_block_number: second_block.number) |
||||
|
||||
insert(:transaction) |> with_block(first_block) |
||||
insert(:transaction) |> with_block(second_block) |
||||
insert(:transaction) |> with_block(second_block) |
||||
|
||||
start_supervised!(LastOutputRootSizeCounter) |
||||
LastOutputRootSizeCounter.consolidate() |
||||
|
||||
assert LastOutputRootSizeCounter.fetch([]) == Decimal.new("2") |
||||
end |
||||
|
||||
test "does not count transactions that are not in output root yet" do |
||||
first_block = insert(:block) |
||||
|
||||
insert(:op_output_root, l2_block_number: first_block.number) |
||||
|
||||
second_block = insert(:block, number: first_block.number + 10) |
||||
insert(:op_output_root, l2_block_number: second_block.number) |
||||
|
||||
insert(:transaction) |> with_block(first_block) |
||||
insert(:transaction) |> with_block(second_block) |
||||
insert(:transaction) |> with_block(second_block) |
||||
|
||||
third_block = insert(:block, number: second_block.number + 1) |
||||
insert(:transaction) |> with_block(third_block) |
||||
insert(:transaction) |> with_block(third_block) |
||||
|
||||
start_supervised!(LastOutputRootSizeCounter) |
||||
LastOutputRootSizeCounter.consolidate() |
||||
|
||||
assert LastOutputRootSizeCounter.fetch([]) == Decimal.new("2") |
||||
end |
||||
end |
||||
end |
Loading…
Reference in new issue