What is: cached_value_from_ets = __MODULE__.get_count()

I can not find where is the definition of get_count() from.

  def estimated_count do
    cached_value_from_ets = __MODULE__.get_count()

    if is_nil(cached_value_from_ets) do
      cached_value_from_db =
        @cache_key
        |> Chain.get_last_fetched_counter()
        |> Decimal.to_integer()

      if cached_value_from_db === 0 do
        estimated_count_from_blocks()
      else
        cached_value_from_db
      end
    else
      cached_value_from_ets
    end
  end

And here is the source code’s link:

Thank you so much!

It is generated from Explorer.Chain.MapCache when it is used by the module.

Long story short, if you are new to elixir, this are more advanced topics related to metaprogramming. Usually tracking down code generated at compile-time can be pretty difficult, one thing you can do is use the following from iex:

Module.__info__/1 callback to get the list of all functions:

Explorer.Chain.Cache.Block.__info__(:functions)

Or you can try to use beam_file to get the elixir code after all the macros were expanded: How to view module source after code generation? - #13 by Marcus

3 Likes

Thank you, Sir!
With your guide, I found the possible source of definition of get_count here:
link: blockscout/apps/explorer/lib/explorer/chain/map_cache.ex at ac3d00acd2210fe70798da83730c39a01d0f5b43 · blockscout/blockscout · GitHub

# apps/explorer/lib/explorer/chain/map_cache.ex
defmodule Explorer.Chain.MapCache do
    @moduledoc """
  Behaviour for a map-like cache of elements.

  A macro based on `ConCache` is provided as well, at its minimum it can be used as;
use Explorer.Chain.MapCache,
  name: :name,
  keys: [:fst, :snd]
Note: `keys` can also be set singularly with the option `key`, e.g.:
use Explorer.Chain.MapCache,
  name: :cache,
  key: :fst,
  key: :snd
"""

    @impl MapCache
    def get(key) do
      case ConCache.get(cache_name(), key) do
        nil ->
          case handle_fallback(key) do
            {:update, new_value} ->
              update(key, new_value)
              new_value

            {:return, new_value} ->
              new_value
          end

        value ->
          value
      end
    end


then in this file:

# apps/explorer/lib/explorer/chain/cache/block.ex
# :fst is :count now  
 use Explorer.Chain.MapCache,
    name: :block_count,
    **key: :count,**
    key: :async_task,
    global_ttl: Application.get_env(:explorer, __MODULE__)[:global_ttl],
    ttl_check_interval: :timer.seconds(1),
    callback: &async_task_on_deletion(&1)

I have another question.
Why does __MODULE__ in the code above represent Explorer.Chain.MapCache module but not the current module, i, e. Explorer.Chain.Cache.Block?
Or I misunderstood what is the usage of __MODULE__ used here. Could you explain the metaprogramming a bit more for me? Especially, how is the get_count defined here?
Thank you so much.

1 Like

Yea it’s a bit confusing but the use is a macro, the option :global_ttl is an argument to that macro. The macro is defined in Explorer.Chain.MapCache so that’s why you get that module as the __MODULE__. If instead you were to IO.inspect(__MODULE__) inside the quote in the macro, then you would see that it was equal to the module at the call site.

I’d recommend having a play but chucking IO.inspect(__MODULE__) in various places and seeing what happens

1 Like

I set two IO.inspect:


  def estimated_count do
    cached_value_from_ets = __MODULE__.get_count()
    IO.inspect( __MODULE__)
    IO.inspect(cached_value_from_ets)
    if is_nil(cached_value_from_ets) do
      cached_value_from_db =
        @cache_key
        |> Chain.get_last_fetched_counter()
        |> Decimal.to_integer()

      if cached_value_from_db === 0 do
        estimated_count_from_blocks()
      else
        cached_value_from_db
      end
    else
      cached_value_from_ets
    end
  end

#=> Explorer.Chain.Cache.Block
#=> 459398

So __MODULE__ represents current module, indeed.

I run “iex -S mix phx.server” and did as you said:

iex> Explorer.Chain.Cache.Block.__info__(:functions)

[
  cache_keys: 0,
  cache_name: 0,
  child_id: 0,
  child_spec: 0,
  child_spec: 1,
  estimated_count: 0,
  get: 1,
  get_all: 0,
  get_async_task: 0,
  get_count: 0,
  handle_update: 3,
  set: 2,
  set_all: 1,
  set_async_task: 1,
  set_count: 1,
  update: 2,
  update_all: 1,
  update_async_task: 1,
  update_count: 1
]

There is a function listed: get_count: 0,
Thank you, Sir.