Skip to content

Commit

Permalink
Fix stats adding multiple times same summary
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 3, 2023
1 parent 063a4c6 commit d87ed60
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.3.0", 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 d87ed60

Please sign in to comment.