lib/cache.ex

defmodule Kdb.Cache do
  @table_name __MODULE__
  @unit_time :millisecond
  # @cleanup_interval :timer.minutes(15)
  @expiration_time :timer.minutes(10)
  @dev Mix.env() == :dev

  def child_spec(_) do
    %{
      id: __MODULE__,
      start: {__MODULE__, :init, []},
      type: :worker,
      restart: :permanent,
      shutdown: 500
    }
  end

  @doc """
  Inicia el módulo de hits creando la tabla ETS.
  """
  def init() do
    # Crear tabla ETS con nombre del módulo, pública y con concurrencia de lectura
    if :ets.whereis(@table_name) == :undefined do
      :ets.new(@table_name, [
        :set,
        :public,
        :named_table,
        read_concurrency: true,
        write_concurrency: true
      ])
    end

    :ignore
  end

  @spec put(atom() | String.t(), binary()) :: boolean()
  def put(type, id) do
    timestamp = now() + @expiration_time
    :ets.insert(@table_name, {{id, type}, timestamp})
  end

  # @spec retrive_by_type(atom()) :: [binary() | String.t()]
  # def retrive_by_type(type) do
  #   # :ets.fun2ms(fn {{id, 1}, _readed_at} -> id end)
  #   match_spec =
  #     [{{{:"$1", type}, :"$2"}, [], [:"$1"]}]

  #   :ets.select(@table_name, match_spec)
  # end

  @spec delete(atom(), binary()) :: true
  def delete(type, id) do
    :ets.delete(@table_name, {id, type})
  end

  defp now do
    :os.system_time(@unit_time)
  end

  def cleanup(older_than) do
    n =
      :ets.foldl(
        fn {key, readed_at}, acc ->
          if readed_at < older_than do
            :ets.delete(@table_name, key)

            acc + 1
          else
            acc
          end
        end,
        0,
        @table_name
      )

    @dev and IO.puts("Deleted #{n} entries")

    n
  end
end