add transaction count cache

pull/1634/head
Ayrat Badykov 6 years ago
parent b9e8e0aa36
commit 978af2df39
No known key found for this signature in database
GPG Key ID: B44668E265E9396F
  1. 115
      apps/explorer/lib/explorer/chain/transaction_count_cache.ex
  2. 58
      apps/explorer/test/explorer/chain/transaction_count_cache_test.exs

@ -0,0 +1,115 @@
defmodule Explorer.Chain.TransactionCountCache do
@moduledoc """
Cache for estimated transaction count.
"""
use GenServer
alias Explorer.Chain.Transaction
alias Explorer.Repo
@tab :transaction_count_cache
# 2 hours
@cache_period 1_000 * 60 * 60 * 2
@default_value 0
@key "count"
@name __MODULE__
def start_link([params, gen_server_options]) do
GenServer.start_link(__MODULE__, params, name: gen_server_options[:name] || @name)
end
def init(params) do
cache_period = params[:cache_period] || @cache_period
current_value = params[:default_value] || @default_value
init_ets_table()
schedule_cache_update()
{:ok, {{cache_period, current_value}, nil}}
end
def value(process_name \\ __MODULE__) do
GenServer.call(process_name, :value)
end
def handle_call(:value, _, {{cache_period, default_value}, task}) do
{value, task} =
case cached_values() do
nil ->
{default_value, update_cache(task)}
{cached_value, timestamp} ->
task =
if current_time() - timestamp > cache_period do
update_cache(task)
end
{cached_value, task}
end
{:reply, value, {{cache_period, default_value}, task}}
end
def update_cache(nil) do
async_update_cache()
end
def update_cache(task) do
task
end
def handle_cast({:update_cache, value}, {{cache_period, default_value}, _}) do
current_time = current_time()
tuple = {value, current_time}
:ets.insert(@tab, {@key, tuple})
{:noreply, {{cache_period, default_value}, nil}}
end
def handle_info({:DOWN, _, _, _, _}, {{cache_period, default_value}, _}) do
{:noreply, {{cache_period, default_value}, nil}}
end
def handle_info(_, {{cache_period, default_value}, _}) do
{:noreply, {{cache_period, default_value}, nil}}
end
def async_update_cache do
Task.async(fn ->
result = Repo.aggregate(Transaction, :count, :hash, timeout: :infinity)
GenServer.cast(__MODULE__, {:update_cache, result})
end)
end
defp init_ets_table do
if :ets.whereis(@tab) == :undefined do
:ets.new(@tab, [
:set,
:named_table,
:public,
write_concurrency: true
])
end
end
defp cached_values do
case :ets.lookup(@tab, @key) do
[{_, cached_values}] -> cached_values
_ -> nil
end
end
defp schedule_cache_update do
Process.send_after(self(), :update_cache, 2_000)
end
defp current_time do
utc_now = DateTime.utc_now()
DateTime.to_unix(utc_now, :millisecond)
end
end

@ -0,0 +1,58 @@
defmodule Explorer.Chain.TransactionCountCacheTest do
use Explorer.DataCase
alias Explorer.Chain.TransactionCountCache
test "returns default transaction count" do
TransactionCountCache.start_link([[], []])
result = TransactionCountCache.value()
assert result == 0
end
test "updates cache if initial value is zero" do
TransactionCountCache.start_link([[], []])
insert(:transaction)
insert(:transaction)
result = TransactionCountCache.value()
assert result == 0
Process.sleep(500)
updated_value = TransactionCountCache.value()
assert updated_value == 2
end
test "does not update cache if cache period did not pass" do
TransactionCountCache.start_link([[], []])
insert(:transaction)
insert(:transaction)
result = TransactionCountCache.value()
assert result == 0
Process.sleep(500)
updated_value = TransactionCountCache.value()
assert updated_value == 2
insert(:transaction)
insert(:transaction)
_updated_value = TransactionCountCache.value()
Process.sleep(500)
updated_value = TransactionCountCache.value()
assert updated_value == 2
end
end
Loading…
Cancel
Save