Skip to content

Commit

Permalink
feat: prune blocks cache with LRU eviction policy (#649)
Browse files Browse the repository at this point in the history
  • Loading branch information
MegaRedHand authored Jan 19, 2024
1 parent 3e07a5f commit b1b0c78
Showing 1 changed file with 60 additions and 5 deletions.
65 changes: 60 additions & 5 deletions lib/lambda_ethereum_consensus/store/blocks.ex
Original file line number Diff line number Diff line change
@@ -1,24 +1,31 @@
defmodule LambdaEthereumConsensus.Store.Blocks do
@moduledoc false
alias LambdaEthereumConsensus.Store.BlockStore
alias Types.SignedBeaconBlock

use GenServer

@ets_block_by_hash __MODULE__
@ets_block_by_hash :blocks_by_hash
@ets_ttl_data :"#{@ets_block_by_hash}_ttl_data"
@max_blocks 512
@batch_prune_size 32

##########################
### Public API
##########################

@spec start_link(any()) :: GenServer.on_start()
def start_link(_opts) do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end

@spec store_block(Types.root(), SignedBeaconBlock.t()) :: :ok
def store_block(block_root, signed_block) do
cache_block(block_root, signed_block)
GenServer.cast(__MODULE__, {:store_block, block_root, signed_block})
end

@spec get_block(Types.root()) :: SignedBeaconBlock.t() | nil
def get_block(block_root), do: lookup(block_root)

@spec clear() :: any()
Expand All @@ -30,14 +37,29 @@ defmodule LambdaEthereumConsensus.Store.Blocks do

@impl GenServer
def init(_) do
:ets.new(@ets_ttl_data, [
:ordered_set,
:private,
:named_table,
read_concurrency: false,
write_concurrency: false,
decentralized_counters: false
])

:ets.new(@ets_block_by_hash, [:set, :public, :named_table])
{:ok, nil}
end

@impl GenServer
def handle_cast({:store_block, block_root, signed_block}, state) do
BlockStore.store_block(signed_block, block_root)
# TODO: remove old blocks from cache
handle_cast({:touch_entry, block_root}, state)
end

@impl GenServer
def handle_cast({:touch_entry, block_root}, state) do
update_ttl(block_root)
prune_cache()
{:noreply, state}
end

Expand All @@ -47,8 +69,12 @@ defmodule LambdaEthereumConsensus.Store.Blocks do

defp lookup(block_root) do
case :ets.lookup_element(@ets_block_by_hash, block_root, 2, nil) do
nil -> cache_miss(block_root)
block -> block
nil ->
cache_miss(block_root)

block ->
GenServer.cast(__MODULE__, {:touch_entry, block_root})
block
end
end

Expand All @@ -69,7 +95,36 @@ defmodule LambdaEthereumConsensus.Store.Blocks do
end

defp cache_block(block_root, signed_block) do
:ets.insert_new(@ets_block_by_hash, {block_root, signed_block})
:ets.insert_new(@ets_block_by_hash, {block_root, signed_block, nil})
GenServer.cast(__MODULE__, {:touch_entry, block_root})
signed_block
end

defp update_ttl(block_root) do
delete_ttl(block_root)
uniq = :erlang.unique_integer([:monotonic])
:ets.insert_new(@ets_ttl_data, {uniq, block_root})
end

defp delete_ttl(block_root) do
case :ets.lookup_element(@ets_block_by_hash, block_root, 3, nil) do
nil -> nil
uniq -> :ets.delete(@ets_ttl_data, uniq)
end
end

defp prune_cache do
to_prune = :ets.info(@ets_block_by_hash, :size) - @max_blocks

if to_prune > 0 do
{elems, _cont} =
:ets.select(@ets_ttl_data, [{:_, [], [:"$_"]}], to_prune + @batch_prune_size)

elems
|> Enum.each(fn {uniq, root} ->
:ets.delete(@ets_ttl_data, uniq)
:ets.delete(@ets_block_by_hash, root)
end)
end
end
end

0 comments on commit b1b0c78

Please sign in to comment.