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