2023-08-07 15:13:01 +12:00
|
|
|
defmodule AshCubDB do
|
|
|
|
@moduledoc """
|
2023-08-07 17:47:06 +12:00
|
|
|
`AshCubDB` is an [Ash DataLayer](https://ash-hq.org/docs/module/ash/latest/ash-datalayer)
|
|
|
|
which adds support for persisting Ash resources with [CubDB](https://hex.pm/packages/cubdb).
|
|
|
|
|
|
|
|
CubDB is an Elixir-based key value store which supports all Erlang-native
|
|
|
|
terms. More information can be found in
|
|
|
|
[the CubDB readme](https://hexdocs.pm/cubdb/readme.html).
|
|
|
|
"""
|
|
|
|
|
|
|
|
alias AshCubDB.{Info, Migration}
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
Ensure that the CubDB process is running for the specified resource.
|
|
|
|
"""
|
|
|
|
@spec start(Ash.Resource.t()) :: {:ok, pid} | {:error, any}
|
|
|
|
def start(resource) do
|
|
|
|
directory = Info.cubdb_directory!(resource)
|
|
|
|
auto_compact? = Info.cubdb_auto_compact?(resource)
|
|
|
|
auto_file_sync? = Info.cubdb_auto_file_sync?(resource)
|
|
|
|
name = via(resource)
|
|
|
|
|
|
|
|
with {:ok, pid} <-
|
|
|
|
DynamicSupervisor.start_child(
|
|
|
|
AshCubDB.DynamicSupervisor,
|
|
|
|
{CubDB, [data_dir: directory, name: name]}
|
|
|
|
),
|
|
|
|
:ok <- CubDB.set_auto_compact(pid, auto_compact?),
|
|
|
|
:ok <- CubDB.set_auto_file_sync(pid, auto_file_sync?),
|
|
|
|
:ok <- Migration.check(pid, resource) do
|
|
|
|
{:ok, pid}
|
|
|
|
else
|
|
|
|
{:error, {:already_started, pid}} -> {:ok, pid}
|
|
|
|
{:error, reason} -> {:error, reason}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
Stop the CubDB process running for a specific resource.
|
|
|
|
"""
|
|
|
|
@spec stop(Ash.Resource.t()) :: :ok
|
|
|
|
def stop(resource) do
|
|
|
|
AshCubDB
|
|
|
|
|> Registry.lookup(resource)
|
|
|
|
|> Enum.each(&DynamicSupervisor.terminate_child(AshCubDB.DynamicSupervisor, &1))
|
|
|
|
end
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
Creates a backup of the database into the target directory path.
|
|
|
|
|
|
|
|
Wrapper around `CubDB.back_up/2`
|
|
|
|
"""
|
|
|
|
@spec back_up(Ash.Resource.t(), Path.t()) :: :ok | {:error, any}
|
|
|
|
def back_up(resource, target_path) do
|
|
|
|
case start(resource) do
|
|
|
|
{:ok, pid} -> CubDB.back_up(pid, target_path)
|
|
|
|
{:error, reason} -> {:error, reason}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
Deletes all entries, resulting in an empty database.
|
|
|
|
|
|
|
|
Wrapper around `CubDB.clear/1`
|
|
|
|
"""
|
|
|
|
@spec clear(Ash.Resource.t()) :: :ok
|
|
|
|
def clear(resource) do
|
|
|
|
case start(resource) do
|
|
|
|
{:ok, pid} -> CubDB.clear(pid)
|
|
|
|
_ -> :ok
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
Runs a database compaction.
|
|
|
|
|
|
|
|
Wrapper around `CubDB.compact/1`
|
2023-08-07 15:13:01 +12:00
|
|
|
"""
|
2023-08-07 17:47:06 +12:00
|
|
|
@spec compact(Ash.Resource.t()) :: :ok | {:error, any}
|
|
|
|
def compact(resource) do
|
|
|
|
case start(resource) do
|
|
|
|
{:ok, pid} -> CubDB.compact(pid)
|
|
|
|
{:error, reason} -> {:error, reason}
|
|
|
|
end
|
|
|
|
end
|
2023-08-07 15:13:01 +12:00
|
|
|
|
|
|
|
@doc """
|
2023-08-07 17:47:06 +12:00
|
|
|
Returns true if a compaction operation is currently running, false otherwise.
|
2023-08-07 15:13:01 +12:00
|
|
|
|
2023-08-07 17:47:06 +12:00
|
|
|
Wrapper around `CubDB.compacting?/1`
|
|
|
|
"""
|
|
|
|
@spec compacting?(Ash.Resource.t()) :: boolean
|
|
|
|
def compacting?(resource) do
|
|
|
|
case start(resource) do
|
|
|
|
{:ok, pid} -> CubDB.compacting?(pid)
|
|
|
|
_ -> false
|
|
|
|
end
|
|
|
|
end
|
2023-08-07 15:13:01 +12:00
|
|
|
|
2023-08-07 17:47:06 +12:00
|
|
|
@doc """
|
|
|
|
Returns the path of the current database file.
|
2023-08-07 15:13:01 +12:00
|
|
|
|
2023-08-07 17:47:06 +12:00
|
|
|
Wrapper around `CubDB.current_db_file/1`
|
2023-08-07 15:13:01 +12:00
|
|
|
"""
|
2023-08-07 17:47:06 +12:00
|
|
|
@spec current_db_file(Ash.Resource.t()) :: String.t()
|
|
|
|
def current_db_file(resource) do
|
|
|
|
resource
|
|
|
|
|> via()
|
|
|
|
|> CubDB.current_db_file()
|
2023-08-07 15:13:01 +12:00
|
|
|
end
|
2023-08-07 17:47:06 +12:00
|
|
|
|
|
|
|
@doc """
|
|
|
|
Returns the path of the data directory, as given when the `CubDB` process was started.
|
|
|
|
|
|
|
|
Wrapper around `CubDB.data_dir/1`
|
|
|
|
"""
|
|
|
|
@spec data_dir(Ash.Resource.t()) :: String.t()
|
|
|
|
def data_dir(resource) do
|
|
|
|
resource
|
|
|
|
|> via()
|
|
|
|
|> CubDB.data_dir()
|
|
|
|
end
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
Returns the dirt factor.
|
|
|
|
|
|
|
|
Wrapper around `CubDB.dirt_factor/1`
|
|
|
|
"""
|
|
|
|
@spec dirt_factor(Ash.Resource.t()) :: float
|
|
|
|
def dirt_factor(resource) do
|
|
|
|
resource
|
|
|
|
|> via()
|
|
|
|
|> CubDB.dirt_factor()
|
|
|
|
end
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
Performs an `fsync`, forcing to flush all data that might be buffered by the OS to disk.
|
|
|
|
|
|
|
|
Wrapper around `CubDB.file_sync/1`
|
|
|
|
"""
|
|
|
|
@spec file_sync(Ash.Resource.t()) :: :ok
|
|
|
|
def file_sync(resource) do
|
|
|
|
resource
|
|
|
|
|> via()
|
|
|
|
|> CubDB.file_sync()
|
|
|
|
end
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
Stops a running compaction.
|
|
|
|
|
|
|
|
Wrapper around `CubDB.halt_compaction/1`
|
|
|
|
"""
|
|
|
|
@spec halt_compaction(Ash.Resource.t()) :: :ok | {:error, :no_compaction_running}
|
|
|
|
def halt_compaction(resource) do
|
|
|
|
resource
|
|
|
|
|> via()
|
|
|
|
|> CubDB.halt_compaction()
|
|
|
|
end
|
|
|
|
|
|
|
|
defp via(resource), do: {:via, Registry, {AshCubDB.Registry, resource}}
|
2023-08-07 15:13:01 +12:00
|
|
|
end
|