Skip to content

Commit

Permalink
Fix stats adding multiple times same summary (#1141)
Browse files Browse the repository at this point in the history
Do not store each summary stats as only last one is used
  • Loading branch information
Neylix committed Jul 4, 2023
1 parent 2c7232b commit 255b70b
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 63 deletions.
102 changes: 39 additions & 63 deletions lib/archethic/db/embedded_impl/stats_info.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ defmodule Archethic.DB.EmbeddedImpl.StatsInfo do
use GenServer
@vsn Mix.Project.config()[:version]

alias Archethic.Crypto

def start_link(opts \\ []) do
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
end
Expand Down Expand Up @@ -44,62 +42,27 @@ defmodule Archethic.DB.EmbeddedImpl.StatsInfo do
GenServer.cast(__MODULE__, {:new_stats, date, tps, nb_transactions, burned_fees})
end

def register_p2p_summaries(node_public_key, date, available?, avg_availability)
when is_binary(node_public_key) and is_boolean(available?) and is_float(avg_availability) do
GenServer.cast(
__MODULE__,
{:new_p2p_summaries, node_public_key, date, available?, avg_availability}
)
end

@doc """
Return the last P2P summary from the last self-repair cycle
"""
@spec get_last_p2p_summaries :: %{
(node_public_key :: Crypto.key()) => {
available? :: boolean(),
average_availability :: float()
}
}
def get_last_p2p_summaries do
GenServer.call(__MODULE__, :get_p2p_summaries)
end

def init(opts) do
db_path = Keyword.get(opts, :path)
filepath = Path.join(db_path, "stats")
fd = File.open!(filepath, [:binary, :read, :append])

{:ok, %{fd: fd, filepath: filepath, tps: 0.0, nb_transactions: 0, burned_fees: 0},
{:continue, :load_from_file}}
end

def handle_continue(:load_from_file, state = %{filepath: filepath, fd: fd}) do
if File.exists?(filepath) do
{tps, nb_transactions, burned_fees} = load_from_file(fd)

new_state =
state
|> Map.put(:tps, tps)
|> Map.put(:nb_transactions, nb_transactions)
|> Map.put(:burned_fees, burned_fees)
{last_update, tps, nb_transactions, burned_fees} =
case File.read(filepath) do
{:ok, <<timestamp::32, tps::float-64, nb_transactions::64, burned_fees::64>>} ->
{DateTime.from_unix!(timestamp), tps, nb_transactions, burned_fees}

{:noreply, new_state}
else
{:noreply, state}
end
end
_ ->
{DateTime.from_unix!(0), 0.0, 0, 0}
end

defp load_from_file(fd, acc \\ {0.0, 0, 0}) do
# Read each stats entry 28 bytes: 4(timestamp) + 8(tps) + 8(nb transactions) + 8(burned_fees)
case :file.read(fd, 28) do
{:ok, <<_timestamp::32, tps::float-64, nb_transactions::64, burned_fees::64>>} ->
{_, prev_nb_transactions, _} = acc
load_from_file(fd, {tps, prev_nb_transactions + nb_transactions, burned_fees})
state =
%{:filepath => filepath}
|> Map.put(:last_update, last_update)
|> Map.put(:tps, tps)
|> Map.put(:nb_transactions, nb_transactions)
|> Map.put(:burned_fees, burned_fees)

:eof ->
acc
end
{:ok, state}
end

def handle_call(:get_nb_transactions, _, state = %{nb_transactions: nb_transactions}) do
Expand All @@ -114,22 +77,35 @@ defmodule Archethic.DB.EmbeddedImpl.StatsInfo do
{:reply, burned_fees, state}
end

def handle_cast({:new_stats, date, tps, nb_transactions, burned_fees}, state = %{fd: fd}) do
append_to_file(fd, date, tps, nb_transactions, burned_fees)

def handle_cast(
{:new_stats, date, tps, nb_transactions, burned_fees},
state = %{last_update: last_update, filepath: filepath, nb_transactions: prev_nb_tx}
) do
new_state =
state
|> Map.put(:tps, tps)
|> Map.update!(:nb_transactions, &(&1 + nb_transactions))
|> Map.put(:burned_fees, burned_fees)
if DateTime.compare(date, last_update) == :gt do
new_nb_transactions = prev_nb_tx + nb_transactions

File.write!(
filepath,
<<DateTime.to_unix(date)::32, tps::float-64, new_nb_transactions::64, burned_fees::64>>
)

state
|> Map.put(:tps, tps)
|> Map.put(:nb_transactions, new_nb_transactions)
|> Map.put(:burned_fees, burned_fees)
else
state
end

{:noreply, new_state}
end

defp append_to_file(fd, date, tps, nb_transactions, burned_fees) do
IO.binwrite(
fd,
<<DateTime.to_unix(date)::32, tps::float-64, nb_transactions::64, burned_fees::64>>
)
def code_change("1.2.4", state, _extra) do
{fd, new_state} = Map.pop(state, :fd)
File.close(fd)
{:ok, new_state}
end

def code_change(_, state, _), do: {:ok, state}
end
45 changes: 45 additions & 0 deletions priv/migration_tasks/prod/1.2.4@stats_info.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
defmodule Migration_1_2_4 do
@moduledoc """
Erase stats file to keep only values of last beacon summary
"""

alias Archethic.DB.EmbeddedImpl

def run() do
db_path = EmbeddedImpl.db_path()
filepath = Path.join(db_path, "stats")
filepath_backup = Path.join(db_path, "stats.backup")

# make a backup
:ok = File.rename(filepath, filepath_backup)

fd = File.open!(filepath_backup)

{last_update, tps, nb_transactions, burned_fees} = load_from_file(fd)

File.write!(
filepath,
<<DateTime.to_unix(last_update)::32, tps::float-64, nb_transactions::64, burned_fees::64>>
)

# remove backup
File.rm(filepath_backup)
end

defp load_from_file(fd, acc \\ {0.0, 0, 0}) do
# Read each stats entry 28 bytes: 4(timestamp) + 8(tps) + 8(nb transactions) + 8(burned_fees)
case :file.read(fd, 28) do
{:ok, <<timestamp::32, tps::float-64, nb_transactions::64, burned_fees::64>>} ->
{_, prev_nb_transactions, _} = acc

load_from_file(
fd,
{DateTime.from_unix!(timestamp), tps, prev_nb_transactions + nb_transactions,
burned_fees}
)

:eof ->
acc
end
end
end

0 comments on commit 255b70b

Please sign in to comment.