Skip to content

fix: trigger pruning on tick #1086

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions lib/lambda_ethereum_consensus/fork_choice/fork_choice.ex
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,7 @@ defmodule LambdaEthereumConsensus.ForkChoice do

%Store{finalized_checkpoint: new_finalized_checkpoint} = new_store

if last_finalized_checkpoint.epoch < new_finalized_checkpoint.epoch do
new_finalized_slot =
new_finalized_checkpoint.epoch * ChainSpec.get("SLOTS_PER_EPOCH")

Task.async(StateDb, :prune_states_older_than, [new_finalized_slot])
end
prune_old_states(last_finalized_checkpoint.epoch, new_finalized_checkpoint.epoch)

GenServer.cast(from, {:block_processed, block_root, true})
{:noreply, new_store}
Expand Down Expand Up @@ -139,7 +134,11 @@ defmodule LambdaEthereumConsensus.ForkChoice do

@impl GenServer
def handle_cast({:on_tick, time}, store) do
%Store{finalized_checkpoint: last_finalized_checkpoint} = store

new_store = Handlers.on_tick(store, time)
%Store{finalized_checkpoint: new_finalized_checkpoint} = new_store
prune_old_states(last_finalized_checkpoint.epoch, new_finalized_checkpoint.epoch)
{:noreply, new_store}
end

Expand All @@ -152,6 +151,15 @@ defmodule LambdaEthereumConsensus.ForkChoice do
### Private Functions
##########################

def prune_old_states(last_finalized_epoch, new_finalized_epoch) do
if last_finalized_epoch < new_finalized_epoch do
new_finalized_slot =
new_finalized_epoch * ChainSpec.get("SLOTS_PER_EPOCH")

Task.async(StateDb, :prune_states_older_than, [new_finalized_slot])
end
end

@spec apply_handler(any(), any(), any()) :: any()
def apply_handler(iter, state, handler) do
iter
Expand Down
14 changes: 10 additions & 4 deletions lib/lambda_ethereum_consensus/store/state_db.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ defmodule LambdaEthereumConsensus.Store.StateDb do
@moduledoc """
Beacon node state storage.
"""
require Logger
alias LambdaEthereumConsensus.Store.Db
alias LambdaEthereumConsensus.Store.Utils
alias Types.BeaconState
Expand Down Expand Up @@ -30,30 +31,35 @@ defmodule LambdaEthereumConsensus.Store.StateDb do
Db.put(slothash_key_block, block_root)
end

@spec prune_states_older_than(non_neg_integer()) :: :ok | {:error, String.t()} | :not_found
def prune_states_older_than(slot) do
Logger.info("[StateDb] Pruning started.", slot: slot)
last_finalized_key = slot |> root_by_slot_key()

with {:ok, it} <- Db.iterate(),
{:ok, @stateslot_prefix <> _slot, _value} <-
Exleveldb.iterator_move(it, last_finalized_key),
{:ok, slots_to_remove} <- get_slots_to_remove(it),
:ok <- Exleveldb.iterator_close(it) do
slots_to_remove |> Enum.map(&remove_by_slot/1)
slots_to_remove |> Enum.each(&remove_state_by_slot/1)
Logger.info("[StateDb] Pruning finished. #{length(slots_to_remove)} slots removed.")
end
end

@spec get_slots_to_remove(list(non_neg_integer()), :eleveldb.itr_ref()) ::
{:ok, list(non_neg_integer())}
defp get_slots_to_remove(slots_to_remove \\ [], iterator) do
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add typespec here? I am confused if slots are integers or binaries

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I can create an issue to refactor this for clarity.

case Exleveldb.iterator_move(iterator, :prev) do
{:ok, @stateslot_prefix <> slot, _root} ->
{:ok, @stateslot_prefix <> <<slot::unsigned-size(64)>>, _root} ->
[slot | slots_to_remove] |> get_slots_to_remove(iterator)

_ ->
{:ok, slots_to_remove}
end
end

defp remove_by_slot(binary_slot) do
slot = :binary.decode_unsigned(binary_slot)
@spec remove_state_by_slot(non_neg_integer()) :: :ok | :not_found
defp remove_state_by_slot(slot) do
key_slot = root_by_slot_key(slot)

with {:ok, block_root} <- Db.get(key_slot),
Expand Down
Loading