ash_cubdb/lib/ash_cub_db.ex
James Harton 74d878b70e
All checks were successful
continuous-integration/drone/push Build is passing
feat: create and read works.
2023-09-29 20:30:42 +13:00

162 lines
4.1 KiB
Elixir

defmodule AshCubDB do
@moduledoc """
`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`
"""
@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
@doc """
Returns true if a compaction operation is currently running, false otherwise.
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
@doc """
Returns the path of the current database file.
Wrapper around `CubDB.current_db_file/1`
"""
@spec current_db_file(Ash.Resource.t()) :: String.t()
def current_db_file(resource) do
resource
|> via()
|> CubDB.current_db_file()
end
@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}}
end