lib/sqlcache.ex

defmodule Sqlcache do
  alias Exqlite.Basic
  alias Sqlcache.Compressor
  use GenServer

  def data_dir do
    Application.get_env(:sqlcache, :datadir, Sqlcache.Config.default_datadir())
  end

  def init(arg) do
    {:ok, conn} = Exqlite.Basic.open(Path.join(data_dir(), "sqlcache.db"))
    init_schema(conn)
    :persistent_term.put(:cache_conn, conn)
    {:ok, arg}
  end

  def init_schema(conn) do
    [
      "create table if not exists cache (key string primary key, value text, kind text, ts text)",
      "create index if not exists cache_kind_idx on cache(kind);"
    ]
    |> Enum.each(fn stmt -> :ok = Exqlite.Sqlite3.execute(conn.db, stmt) end)
  end

  def conn do
    :persistent_term.get(:cache_conn)
  end

  def start_link(arg) do
    GenServer.start_link(__MODULE__, arg, name: __MODULE__)
  end

  def put(kind, k, v) do
    v = Compressor.compress(v)
    ts = DateTime.utc_now() |> DateTime.to_unix(:microsecond)

    Basic.exec(conn(), "insert or replace into cache(key, value, kind, ts) values (?, ?, ?, ?)", [
      k,
      v,
      kind,
      ts
    ])
    |> Basic.rows()
  end

  def get(kind, k) do
    case Basic.exec(conn(), "select value from cache where key =  ? and kind = ?", [k, kind])
         |> Basic.rows() do
      {:ok, [], ["value"]} -> {:error, nil}
      {:ok, [[val]], ["value"]} -> {:ok, Compressor.uncompress(val)}
    end
  end

  def del(kind, k) do
    case Basic.exec(conn(), "delete from cache where key =  ? and kind = ?", [k, kind])
         |> Basic.rows() do
      {:ok, [], []} -> :ok
    end
  end

  def clear_kind(kind) do
    case Basic.exec(conn(), "delete from cache where kind = ?", [kind]) |> Basic.rows() do
      {:ok, [], []} ->
        :ok
    end
  end

  def debug() do
    Basic.exec(conn(), "select * from cache limit 100") |> Basic.rows()
  end
end