Merge pull request #1267 from poanetwork/ams-event-handler-messages
Decrease BlockScoutWeb.EventHandler message queuepull/1280/head
commit
37121c1d25
@ -0,0 +1,63 @@ |
|||||||
|
defmodule BlockScoutWeb.Counters.BlocksIndexedCounter do |
||||||
|
@moduledoc """ |
||||||
|
Module responsible for fetching and consolidating the number blocks indexed. |
||||||
|
|
||||||
|
It loads the count asynchronously in a time interval. |
||||||
|
""" |
||||||
|
|
||||||
|
use GenServer |
||||||
|
|
||||||
|
alias BlockScoutWeb.Notifier |
||||||
|
alias Explorer.Chain |
||||||
|
|
||||||
|
# It is undesirable to automatically start the counter in all environments. |
||||||
|
# Consider the test environment: if it initiates but does not finish before a |
||||||
|
# test ends, that test will fail. |
||||||
|
config = Application.get_env(:block_scout_web, BlockScoutWeb.Counters.BlocksIndexedCounter) |
||||||
|
@enabled Keyword.get(config, :enabled) |
||||||
|
|
||||||
|
@doc """ |
||||||
|
Starts a process to periodically update the % of blocks indexed. |
||||||
|
""" |
||||||
|
@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 |
||||||
|
if @enabled do |
||||||
|
Task.start_link(&calculate_blocks_indexed/0) |
||||||
|
|
||||||
|
schedule_next_consolidation() |
||||||
|
end |
||||||
|
|
||||||
|
{:ok, args} |
||||||
|
end |
||||||
|
|
||||||
|
def calculate_blocks_indexed do |
||||||
|
ratio = Chain.indexed_ratio() |
||||||
|
|
||||||
|
finished? = |
||||||
|
if ratio < 1 do |
||||||
|
false |
||||||
|
else |
||||||
|
Chain.finished_indexing?() |
||||||
|
end |
||||||
|
|
||||||
|
Notifier.broadcast_blocks_indexed_ratio(ratio, finished?) |
||||||
|
end |
||||||
|
|
||||||
|
defp schedule_next_consolidation do |
||||||
|
Process.send_after(self(), :calculate_blocks_indexed, :timer.minutes(5)) |
||||||
|
end |
||||||
|
|
||||||
|
@impl true |
||||||
|
def handle_info(:calculate_blocks_indexed, state) do |
||||||
|
calculate_blocks_indexed() |
||||||
|
|
||||||
|
schedule_next_consolidation() |
||||||
|
|
||||||
|
{:noreply, state} |
||||||
|
end |
||||||
|
end |
@ -1,34 +0,0 @@ |
|||||||
defmodule BlockScoutWeb.EventHandler do |
|
||||||
@moduledoc """ |
|
||||||
Subscribing process for broadcast events from Chain context. |
|
||||||
""" |
|
||||||
|
|
||||||
use GenServer |
|
||||||
|
|
||||||
alias BlockScoutWeb.Notifier |
|
||||||
alias Explorer.Chain |
|
||||||
|
|
||||||
# Client |
|
||||||
|
|
||||||
def start_link(_) do |
|
||||||
GenServer.start_link(__MODULE__, [], name: __MODULE__) |
|
||||||
end |
|
||||||
|
|
||||||
# Server |
|
||||||
|
|
||||||
def init([]) do |
|
||||||
Chain.subscribe_to_events(:addresses) |
|
||||||
Chain.subscribe_to_events(:address_coin_balances) |
|
||||||
Chain.subscribe_to_events(:blocks) |
|
||||||
Chain.subscribe_to_events(:exchange_rate) |
|
||||||
Chain.subscribe_to_events(:internal_transactions) |
|
||||||
Chain.subscribe_to_events(:transactions) |
|
||||||
Chain.subscribe_to_events(:token_transfers) |
|
||||||
{:ok, []} |
|
||||||
end |
|
||||||
|
|
||||||
def handle_info(event, state) do |
|
||||||
Notifier.handle_event(event) |
|
||||||
{:noreply, state} |
|
||||||
end |
|
||||||
end |
|
@ -0,0 +1,33 @@ |
|||||||
|
defmodule BlockScoutWeb.RealtimeEventHandler do |
||||||
|
@moduledoc """ |
||||||
|
Subscribing process for broadcast events from realtime. |
||||||
|
""" |
||||||
|
|
||||||
|
use GenServer |
||||||
|
|
||||||
|
alias BlockScoutWeb.Notifier |
||||||
|
alias Explorer.Chain.Events.Subscriber |
||||||
|
|
||||||
|
def start_link(_) do |
||||||
|
GenServer.start_link(__MODULE__, [], name: __MODULE__) |
||||||
|
end |
||||||
|
|
||||||
|
@impl true |
||||||
|
def init([]) do |
||||||
|
Subscriber.to(:addresses, :realtime) |
||||||
|
Subscriber.to(:address_coin_balances, :realtime) |
||||||
|
Subscriber.to(:blocks, :realtime) |
||||||
|
Subscriber.to(:internal_transactions, :realtime) |
||||||
|
Subscriber.to(:transactions, :realtime) |
||||||
|
Subscriber.to(:token_transfers, :realtime) |
||||||
|
# Does not come from the indexer |
||||||
|
Subscriber.to(:exchange_rate) |
||||||
|
{:ok, []} |
||||||
|
end |
||||||
|
|
||||||
|
@impl true |
||||||
|
def handle_info(event, state) do |
||||||
|
Notifier.handle_event(event) |
||||||
|
{:noreply, state} |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,41 @@ |
|||||||
|
defmodule Explorer.Chain.Events.Publisher do |
||||||
|
@moduledoc """ |
||||||
|
Publishes events related to the Chain context. |
||||||
|
""" |
||||||
|
|
||||||
|
@allowed_events ~w(addresses address_coin_balances blocks internal_transactions token_transfers transactions)a |
||||||
|
|
||||||
|
def broadcast(_data, false), do: :ok |
||||||
|
|
||||||
|
def broadcast(data, broadcast_type) do |
||||||
|
for {event_type, event_data} <- data, event_type in @allowed_events do |
||||||
|
send_data(event_type, broadcast_type, event_data) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
@spec broadcast(atom()) :: :ok |
||||||
|
def broadcast(event_type) do |
||||||
|
send_data(event_type) |
||||||
|
end |
||||||
|
|
||||||
|
defp send_data(event_type) do |
||||||
|
Registry.dispatch(Registry.ChainEvents, event_type, fn entries -> |
||||||
|
for {pid, _registered_val} <- entries do |
||||||
|
send(pid, {:chain_event, event_type}) |
||||||
|
end |
||||||
|
end) |
||||||
|
end |
||||||
|
|
||||||
|
# The :catchup type of event is not being consumed right now. |
||||||
|
# To avoid a large number of unread messages in the `mailbox` the dispatch of |
||||||
|
# these type of events is disabled for now. |
||||||
|
defp send_data(_event_type, :catchup, _event_data), do: :ok |
||||||
|
|
||||||
|
defp send_data(event_type, broadcast_type, event_data) do |
||||||
|
Registry.dispatch(Registry.ChainEvents, {event_type, broadcast_type}, fn entries -> |
||||||
|
for {pid, _registered_val} <- entries do |
||||||
|
send(pid, {:chain_event, event_type, broadcast_type, event_data}) |
||||||
|
end |
||||||
|
end) |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,48 @@ |
|||||||
|
defmodule Explorer.Chain.Events.Subscriber do |
||||||
|
@moduledoc """ |
||||||
|
Subscribes to events related to the Chain context. |
||||||
|
""" |
||||||
|
|
||||||
|
@allowed_broadcast_events ~w(addresses address_coin_balances blocks internal_transactions token_transfers transactions)a |
||||||
|
|
||||||
|
@allowed_broadcast_types ~w(catchup realtime)a |
||||||
|
|
||||||
|
@allowed_events ~w(exchange_rate)a |
||||||
|
|
||||||
|
@type broadcast_type :: :realtime | :catchup |
||||||
|
|
||||||
|
@doc """ |
||||||
|
Subscribes the caller process to a specified subset of chain-related events. |
||||||
|
|
||||||
|
## Handling An Event |
||||||
|
|
||||||
|
A subscribed process should handle an event message. The message is in the |
||||||
|
format of a three-element tuple. |
||||||
|
|
||||||
|
* Element 0 - `:chain_event` |
||||||
|
* Element 1 - event subscribed to |
||||||
|
* Element 2 - event data in list form |
||||||
|
|
||||||
|
# A new block event in a GenServer |
||||||
|
def handle_info({:chain_event, :blocks, blocks}, state) do |
||||||
|
# Do something with the blocks |
||||||
|
end |
||||||
|
|
||||||
|
## Example |
||||||
|
|
||||||
|
iex> Explorer.Chain.Events.Subscriber.to(:blocks, :realtime) |
||||||
|
:ok |
||||||
|
""" |
||||||
|
@spec to(atom(), broadcast_type()) :: :ok |
||||||
|
def to(event_type, broadcast_type) |
||||||
|
when event_type in @allowed_broadcast_events and broadcast_type in @allowed_broadcast_types do |
||||||
|
Registry.register(Registry.ChainEvents, {event_type, broadcast_type}, []) |
||||||
|
:ok |
||||||
|
end |
||||||
|
|
||||||
|
@spec to(atom()) :: :ok |
||||||
|
def to(event_type) when event_type in @allowed_events do |
||||||
|
Registry.register(Registry.ChainEvents, event_type, []) |
||||||
|
:ok |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,65 @@ |
|||||||
|
defmodule Explorer.Chain.Events.PublisherTest do |
||||||
|
use ExUnit.Case, async: true |
||||||
|
|
||||||
|
doctest Explorer.Chain.Events.Publisher |
||||||
|
|
||||||
|
alias Explorer.Chain.Events.{Publisher, Subscriber} |
||||||
|
|
||||||
|
describe "broadcast/2" do |
||||||
|
test "sends chain_event of realtime type" do |
||||||
|
event_type = :blocks |
||||||
|
broadcast_type = :realtime |
||||||
|
event_data = [] |
||||||
|
|
||||||
|
Subscriber.to(event_type, broadcast_type) |
||||||
|
|
||||||
|
Publisher.broadcast([{event_type, event_data}], broadcast_type) |
||||||
|
|
||||||
|
assert_received {:chain_event, ^event_type, ^broadcast_type, []} |
||||||
|
end |
||||||
|
|
||||||
|
test "won't send chain_event of catchup type" do |
||||||
|
event_type = :blocks |
||||||
|
broadcast_type = :catchup |
||||||
|
event_data = [] |
||||||
|
|
||||||
|
Subscriber.to(event_type, broadcast_type) |
||||||
|
|
||||||
|
Publisher.broadcast([{event_type, event_data}], broadcast_type) |
||||||
|
|
||||||
|
refute_received {:chain_event, ^event_type, ^broadcast_type, []} |
||||||
|
end |
||||||
|
|
||||||
|
test "won't send event that is not allowed" do |
||||||
|
event_type = :not_allowed |
||||||
|
broadcast_type = :catchup |
||||||
|
event_data = [] |
||||||
|
|
||||||
|
Publisher.broadcast([{event_type, event_data}], broadcast_type) |
||||||
|
|
||||||
|
refute_received {:chain_event, ^event_type, ^broadcast_type, []} |
||||||
|
end |
||||||
|
|
||||||
|
test "won't send event of broadcast type not allowed" do |
||||||
|
event_type = :blocks |
||||||
|
broadcast_type = :something |
||||||
|
event_data = [] |
||||||
|
|
||||||
|
Publisher.broadcast([{event_type, event_data}], broadcast_type) |
||||||
|
|
||||||
|
refute_received {:chain_event, ^event_type, ^broadcast_type, []} |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe "broadcast/1" do |
||||||
|
test "sends event whithout type of broadcast" do |
||||||
|
event_type = :exchange_rate |
||||||
|
|
||||||
|
Subscriber.to(event_type) |
||||||
|
|
||||||
|
Publisher.broadcast(event_type) |
||||||
|
|
||||||
|
assert_received {:chain_event, ^event_type} |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,33 @@ |
|||||||
|
defmodule Explorer.Chain.Events.SubscriberTest do |
||||||
|
use ExUnit.Case, async: true |
||||||
|
|
||||||
|
doctest Explorer.Chain.Events.Subscriber |
||||||
|
|
||||||
|
alias Explorer.Chain.Events.{Publisher, Subscriber} |
||||||
|
|
||||||
|
describe "to/2" do |
||||||
|
test "receives event when there is a type of broadcast" do |
||||||
|
event_type = :blocks |
||||||
|
broadcast_type = :realtime |
||||||
|
event_data = [] |
||||||
|
|
||||||
|
Subscriber.to(event_type, broadcast_type) |
||||||
|
|
||||||
|
Publisher.broadcast([{event_type, event_data}], broadcast_type) |
||||||
|
|
||||||
|
assert_received {:chain_event, :blocks, :realtime, []} |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe "to/1" do |
||||||
|
test "receives event when there is not a type of broadcast" do |
||||||
|
event_type = :exchange_rate |
||||||
|
|
||||||
|
Subscriber.to(event_type) |
||||||
|
|
||||||
|
Publisher.broadcast(event_type) |
||||||
|
|
||||||
|
assert_received {:chain_event, :exchange_rate} |
||||||
|
end |
||||||
|
end |
||||||
|
end |
Loading…
Reference in new issue