commit
5f00209f71
@ -0,0 +1,73 @@ |
|||||||
|
defmodule Explorer.Chain.Cache.Accounts do |
||||||
|
@moduledoc """ |
||||||
|
Caches the top Addresses |
||||||
|
""" |
||||||
|
|
||||||
|
alias Explorer.Chain.Address |
||||||
|
|
||||||
|
use Explorer.Chain.OrderedCache, |
||||||
|
name: :accounts, |
||||||
|
max_size: 51, |
||||||
|
preload: :names, |
||||||
|
ttl_check_interval: Application.get_env(:explorer, __MODULE__)[:ttl_check_interval], |
||||||
|
global_ttl: Application.get_env(:explorer, __MODULE__)[:global_ttl] |
||||||
|
|
||||||
|
@type element :: Address.t() |
||||||
|
|
||||||
|
@type id :: {non_neg_integer(), non_neg_integer()} |
||||||
|
|
||||||
|
def element_to_id(%Address{fetched_coin_balance: fetched_coin_balance, hash: hash}) do |
||||||
|
{fetched_coin_balance, hash} |
||||||
|
end |
||||||
|
|
||||||
|
def prevails?({fetched_coin_balance_a, hash_a}, {fetched_coin_balance_b, hash_b}) do |
||||||
|
# same as a query's `order_by: [desc: :fetched_coin_balance, asc: :hash]` |
||||||
|
if fetched_coin_balance_a == fetched_coin_balance_b do |
||||||
|
hash_a < hash_b |
||||||
|
else |
||||||
|
fetched_coin_balance_a > fetched_coin_balance_b |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
def drop(nil), do: :ok |
||||||
|
|
||||||
|
def drop([]), do: :ok |
||||||
|
|
||||||
|
def drop(addresses) when is_list(addresses) do |
||||||
|
# This has to be used by the Indexer insead of `update`. |
||||||
|
# The reason being that addresses already in the cache can change their balance |
||||||
|
# value and removing or updating them will result into a potentially invalid |
||||||
|
# cache status, that would not even get corrected with time. |
||||||
|
# The only thing we can safely do when an address in the cache changes its |
||||||
|
# `fetched_coin_balance` is to invalidate the whole cache and wait for it |
||||||
|
# to be filled again (by the query that it takes the place of when full). |
||||||
|
ConCache.update(cache_name(), ids_list_key(), fn ids -> |
||||||
|
if drop_needed?(ids, addresses) do |
||||||
|
# Remove the addresses immediately |
||||||
|
Enum.each(ids, &ConCache.delete(cache_name(), &1)) |
||||||
|
|
||||||
|
{:ok, []} |
||||||
|
else |
||||||
|
{:ok, ids} |
||||||
|
end |
||||||
|
end) |
||||||
|
end |
||||||
|
|
||||||
|
def drop(address), do: drop([address]) |
||||||
|
|
||||||
|
defp drop_needed?(ids, _addresses) when is_nil(ids), do: false |
||||||
|
|
||||||
|
defp drop_needed?([], _addresses), do: false |
||||||
|
|
||||||
|
defp drop_needed?(ids, addresses) do |
||||||
|
ids_map = Map.new(ids, fn {balance, hash} -> {hash, balance} end) |
||||||
|
|
||||||
|
# Result it `true` only when the address is present in the cache already, |
||||||
|
# but with a different `fetched_coin_balance` |
||||||
|
Enum.find_value(addresses, false, fn address -> |
||||||
|
stored_address_balance = Map.get(ids_map, address.hash) |
||||||
|
|
||||||
|
stored_address_balance && stored_address_balance != address.fetched_coin_balance |
||||||
|
end) |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,42 @@ |
|||||||
|
defmodule Explorer.Chain.Cache.AccountsTest do |
||||||
|
use Explorer.DataCase |
||||||
|
|
||||||
|
alias Explorer.Chain.Cache.Accounts |
||||||
|
alias Explorer.Repo |
||||||
|
|
||||||
|
describe "drop/1" do |
||||||
|
test "does not drop the cache if the address fetched_coin_balance has not changed" do |
||||||
|
address = |
||||||
|
insert(:address, fetched_coin_balance: 100_000, fetched_coin_balance_block_number: 1) |
||||||
|
|> preload_names() |
||||||
|
|
||||||
|
Accounts.update(address) |
||||||
|
|
||||||
|
assert Accounts.take(1) == [address] |
||||||
|
|
||||||
|
Accounts.drop(address) |
||||||
|
|
||||||
|
assert Accounts.take(1) == [address] |
||||||
|
end |
||||||
|
|
||||||
|
test "drops the cache if an address was in the cache with a different fetched_coin_balance" do |
||||||
|
address = |
||||||
|
insert(:address, fetched_coin_balance: 100_000, fetched_coin_balance_block_number: 1) |
||||||
|
|> preload_names() |
||||||
|
|
||||||
|
Accounts.update(address) |
||||||
|
|
||||||
|
assert Accounts.take(1) == [address] |
||||||
|
|
||||||
|
updated_address = %{address | fetched_coin_balance: 100_001} |
||||||
|
|
||||||
|
Accounts.drop(updated_address) |
||||||
|
|
||||||
|
assert Accounts.take(1) == [] |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
defp preload_names(address) do |
||||||
|
Repo.preload(address, [:names]) |
||||||
|
end |
||||||
|
end |
Loading…
Reference in new issue