Current section

Files

Jump to
ecto_foundationdb lib ecto adapters foundationdb ecto_adapter_storage.ex
Raw

lib/ecto/adapters/foundationdb/ecto_adapter_storage.ex

defmodule Ecto.Adapters.FoundationDB.EctoAdapterStorage do
@moduledoc false
@behaviour Ecto.Adapter.Storage
alias EctoFoundationDB.Layer.Pack
alias EctoFoundationDB.Options
@all_data_start_key ""
@all_data_end_key <<0xFF>>
def list_tenants(dbtx, options) do
start_key = get_tenant_name("", options)
end_key = :erlfdb_key.strinc(start_key)
:erlfdb_tenant_management.list_tenants(dbtx, start_key, end_key, [])
end
def tenant_exists?(dbtx, tenant_id, options) do
case get_tenant(dbtx, tenant_id, options) do
{:ok, _} -> true
{:error, :tenant_does_not_exist} -> false
end
end
def tenant_name_to_id!(tenant_name, options) do
prefix = get_tenant_name("", options)
len = String.length(prefix)
^prefix = String.slice(tenant_name, 0, len)
String.slice(tenant_name, len, String.length(tenant_name) - len)
end
def create_tenant(dbtx, tenant_id, options) do
tenant_name = get_tenant_name(tenant_id, options)
try do
:erlfdb_tenant_management.create_tenant(dbtx, tenant_name)
rescue
e in ErlangError ->
case e do
%ErlangError{original: {:erlfdb_error, 2132}} ->
{:error, :tenant_already_exists}
end
end
end
def open_tenant(db, tenant_id, options) do
tenant_name = get_tenant_name(tenant_id, options)
open_named_tenant(db, tenant_name)
end
def delete_tenant(dbtx, tenant_id, options) do
tenant_name = get_tenant_name(tenant_id, options)
try do
:erlfdb_tenant_management.delete_tenant(dbtx, tenant_name)
rescue
e in ErlangError ->
case e do
%ErlangError{original: {:erlfdb_error, 2133}} ->
{:error, :tenant_nonempty}
end
end
end
def empty_tenant(dbtx, tenant_id, options) do
tenant = open_tenant(dbtx, tenant_id, options)
:erlfdb.transactional(tenant, fn tx ->
{start_key, end_key} = Pack.adapter_repo_range()
:erlfdb.clear_range(tx, start_key, end_key)
end)
:ok
end
def clear_tenant(dbtx, tenant_id, options) do
tenant = open_tenant(dbtx, tenant_id, options)
:erlfdb.transactional(tenant, fn tx ->
:erlfdb.clear_range(tx, @all_data_start_key, @all_data_end_key)
end)
:ok
end
@impl true
def storage_up(options) do
db = open_db(options)
tenant_name = get_storage_tenant_name(options)
case get_named_tenant(db, tenant_name) do
{:error, :tenant_does_not_exist} ->
:ok = :erlfdb_tenant_management.create_tenant(db, tenant_name)
:ok
{:ok, _} ->
{:error, :already_up}
end
end
@impl true
def storage_down(options) do
db = open_db(options)
tenant_name = get_storage_tenant_name(options)
case get_named_tenant(db, tenant_name) do
{:error, :tenant_does_not_exist} ->
{:error, :already_down}
{:ok, _} ->
tenant = open_named_tenant(db, tenant_name)
:erlfdb.transactional(tenant, fn tx ->
:erlfdb.clear_range(tx, @all_data_start_key, @all_data_end_key)
end)
:ok = :erlfdb_tenant_management.delete_tenant(db, tenant_name)
:ok
end
end
@impl true
def storage_status(options) do
db = open_db(options)
storage_tenant_name = get_storage_tenant_name(options)
case get_named_tenant(db, storage_tenant_name) do
{:ok, _} ->
:up
_ ->
:down
end
end
def open_db(options) do
fun = Options.get(options, :open_db)
fun.()
end
def open_storage_tenant(db, options) do
tenant_name = get_storage_tenant_name(options)
open_named_tenant(db, tenant_name)
end
defp get_storage_tenant_name(options) do
storage_id = Options.get(options, :storage_id)
"#{storage_id}"
end
defp get_tenant_name(tenant_id, options) do
storage_id = Options.get(options, :storage_id)
"#{storage_id}/" <> tenant_id
end
defp get_tenant(dbtx, tenant_id, options) do
tenant_name = get_tenant_name(tenant_id, options)
get_named_tenant(dbtx, tenant_name)
end
defp get_named_tenant(dbtx, tenant_name) do
case :erlfdb_tenant_management.get_tenant(dbtx, tenant_name) do
:not_found ->
{:error, :tenant_does_not_exist}
tenant ->
{:ok, tenant}
end
end
defp open_named_tenant(db, tenant_name) do
:erlfdb.open_tenant(db, tenant_name)
end
end