Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/supavisor/client_handler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ defmodule Supavisor.ClientHandler do
Logger.metadata(state: :exchange)
sni_hostname = HandlerHelpers.try_get_sni(sock)

case Tenants.get_user_cache(type, user, tenant_or_alias, sni_hostname) do
case Tenants.fetch_user_cache(type, user, tenant_or_alias, sni_hostname) do
{:ok, info} ->
db_name = db_name || info.tenant.db_database

Expand Down
37 changes: 17 additions & 20 deletions lib/supavisor/tenants.ex
Original file line number Diff line number Diff line change
Expand Up @@ -79,51 +79,48 @@
end

def get_tenant(external_id, _) when external_id != nil do
Tenant |> Repo.get_by(external_id: external_id)
end

def get_tenant(_, _), do: nil

@spec get_user_cache(:single | :cluster, String.t(), String.t() | nil, String.t() | nil) ::
@spec fetch_user_cache(:single | :cluster, String.t(), String.t() | nil, String.t() | nil) ::
{:ok, map()} | {:error, any()}
def get_user_cache(type, user, external_id, sni_hostname) do
def fetch_user_cache(type, user, external_id, sni_hostname) do
cache_key = {:user_cache, type, user, external_id, sni_hostname}

case Cachex.fetch(Supavisor.Cache, cache_key, fn _key ->
{:commit, {:cached, get_user(type, user, external_id, sni_hostname)},
ttl: :timer.hours(24)}
case fetch_user(type, user, external_id, sni_hostname) do
{:ok, value} -> {:commit, {:cached, value}, ttl: :timer.hours(24)}
{:error, reason} -> {:ignore, reason}
end
end) do
{_, {:cached, value}} -> value
{_, {:cached, value}, _} -> value
{:ok, {:cached, value}} -> {:ok, value}
{:commit, {:cached, value}, _} -> {:ok, value}
{:ignore, error} -> {:error, error}
end
end

@spec get_user(atom(), String.t(), String.t() | nil, String.t() | nil) ::
@spec fetch_user(atom(), String.t(), String.t() | nil, String.t() | nil) ::
{:ok, map()} | {:error, any()}
def get_user(_, _, nil, nil) do
def fetch_user(_, _, nil, nil) do
{:error, "Either external_id or sni_hostname must be provided"}
end

def get_user(:cluster, user, external_id, sni_hostname) do
def fetch_user(:cluster, user, external_id, sni_hostname) do
query =
from(ct in ClusterTenants,
where: ct.cluster_alias == ^external_id and ct.active == true,
limit: 1
)
from(ct in ClusterTenants, where: ct.cluster_alias == ^external_id and ct.active == true)

case Repo.all(query) do
[%ClusterTenants{} = ct] ->
get_user(:single, user, ct.tenant_external_id, sni_hostname)

[_ | _] ->
{:error, :multiple_results}
case Repo.one(query) do
%ClusterTenants{} = ct ->
fetch_user(:single, user, ct.tenant_external_id, sni_hostname)

_ ->
{:error, :not_found}
end
end

def get_user(:single, user, external_id, sni_hostname) do
def fetch_user(:single, user, external_id, sni_hostname) do
query = build_user_query(user, external_id, sni_hostname)

case Repo.all(query) do
Expand Down
71 changes: 66 additions & 5 deletions test/supavisor/tenants_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,73 @@ defmodule Supavisor.TenantsTest do
assert %Ecto.Changeset{} = Tenants.change_tenant(tenant)
end

test "get_user/4" do
_tenant = tenant_fixture()
assert {:error, :not_found} = Tenants.get_user(:single, "no_user", "no_tenant", "")
test "fetch_user_cache/4 returns a user from cache" do
tenant =
tenant_fixture(%{external_id: "user_cache_tenant", sni_hostname: "user.example.com"})

user = List.first(tenant.users)

assert {:error, "Either external_id or sni_hostname must be provided"} ==
Tenants.fetch_user_cache(:single, user.db_user, nil, nil)
end

test "fetch_user/4 returns :multiple_results when more than one user matches" do
users_attrs = [
%{
"db_user" => "postgres",
"db_password" => "postgres",
"pool_size" => 3,
"mode_type" => "session"
},
%{
"db_user" => "postgres",
"db_password" => "postgres",
"pool_size" => 3,
"mode_type" => "transaction"
}
]

_tenant = tenant_fixture(%{users: users_attrs})

assert {:error, :multiple_results} =
Tenants.fetch_user(:single, "postgres", "dev_tenant", "")
end

test "fetch_user/4 returns :not_found for missing user or tenant, and :ok for valid input" do
tenant = tenant_fixture()
user = List.first(tenant.users)

assert {:error, :not_found} = Tenants.fetch_user(:single, "no_user", "no_tenant", "")
assert {:error, :not_found} = Tenants.fetch_user(:single, "postgres", nil, "")

assert {:ok, %{user: fetched_user, tenant: fetched_tenant}} =
Tenants.fetch_user(:single, "postgres", "dev_tenant", "")

assert fetched_user.id == user.id
assert fetched_tenant.id == tenant.id
end

test "fetch_user/4 with :cluster mode returns :not_found for invalid tenant alias and :ok for valid alias" do
cluster_tenant_attrs = %{
type: "write",
cluster_alias: "cluster_alias",
tenant_external_id: "dev_tenant",
active: true
}

tenant = tenant_fixture()
user = List.first(tenant.users)

_cluster =
cluster_fixture(%{alias: "cluster_alias", cluster_tenants: [cluster_tenant_attrs]})

assert {:error, :not_found} == Tenants.fetch_user(:cluster, "postgres", "cluster", "")

assert {:ok, %{user: fetched_user, tenant: fetched_tenant}} =
Tenants.fetch_user(:cluster, "postgres", "cluster_alias", "")

assert {:ok, %{tenant: _, user: _}} =
Tenants.get_user(:single, "postgres", "dev_tenant", "")
assert fetched_user.id == user.id
assert fetched_tenant.id == tenant.id
end

test "update_tenant_ps/2 updates the tenant's default_parameter_status" do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ defmodule SupavisorWeb.TenantControllerTest do
end

defp set_cache(external_id) do
Supavisor.Tenants.get_user_cache(:single, "user", external_id, nil)
Supavisor.Tenants.fetch_user_cache(:single, "user", external_id, nil)
Supavisor.Tenants.get_tenant_cache(external_id, nil)
end

Expand Down
Loading