Skip to content

Commit

Permalink
feat: clear tenant's cache on update or delete (#309)
Browse files Browse the repository at this point in the history
* feat: clear tenant's cache on update or delete

* delete keys with Cachex.stream! and don't delete the cache upon pool termination
  • Loading branch information
abc3 authored Feb 20, 2024
1 parent d155590 commit 7281330
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 3 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.1.38
1.1.39
32 changes: 32 additions & 0 deletions lib/supavisor.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
defmodule Supavisor do
@moduledoc false
require Logger
import Cachex.Spec
alias Supavisor.Helpers, as: H
alias Supavisor.Tenants, as: T
alias Supavisor.Manager
Expand Down Expand Up @@ -160,6 +161,37 @@ defmodule Supavisor do
end)
end

@spec del_all_cache(String.t()) :: [map()]
def del_all_cache(tenant) do
Logger.info("Deleting all cache for tenant #{tenant}")

del = fn key, acc ->
result = Cachex.del(Supavisor.Cache, key)
[%{inspect(key) => inspect(result)} | acc]
end

Supavisor.Cache
|> Cachex.stream!()
|> Enum.reduce([], fn entry(key: key), acc ->
case key do
{:metrics, ^tenant} -> del.(key, acc)
{:secrets, ^tenant, _} -> del.(key, acc)
{:user_cache, _, _, ^tenant, _} -> del.(key, acc)
{:tenant_cache, ^tenant, _} -> del.(key, acc)
_ -> acc
end
end)
end

@spec del_all_cache_dist(String.t(), pos_integer()) :: [map()]
def del_all_cache_dist(tenant, timeout \\ 15_000) do
Logger.info("Deleting all dist cache for tenant #{tenant}")

for node <- [node() | Node.list()] do
%{to_string(node) => :erpc.call(node, Supavisor, :del_all_cache, [tenant], timeout)}
end
end

@spec get_local_pool(id) :: map | pid | nil
def get_local_pool(id) do
match = {{:pool, :_, :_, id}, :"$2", :"$3"}
Expand Down
3 changes: 1 addition & 2 deletions lib/supavisor/syn_handler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ defmodule Supavisor.SynHandler do

def on_process_unregistered(
:tenants,
{{_type, tenant}, user, _mode, _db_name} = id,
{{_type, _tenant}, _user, _mode, _db_name} = id,
_pid,
_meta,
reason
Expand All @@ -16,7 +16,6 @@ defmodule Supavisor.SynHandler do

# remove all Prometheus metrics for the specified tenant
PromEx.remove_metrics(id)
Supavisor.del_all_cache(tenant, user)
end

def resolve_registry_conflict(
Expand Down
8 changes: 8 additions & 0 deletions lib/supavisor_web/controllers/tenant_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ defmodule SupavisorWeb.TenantController do
end

def update(conn, %{"external_id" => id, "tenant" => params}) do
Logger.info("Delete cache dist #{id}: #{inspect(Supavisor.del_all_cache_dist(id))}")
cert = H.upstream_cert(params["upstream_tls_ca"])

if params["upstream_ssl"] && params["upstream_verify"] == "peer" && !cert do
Expand Down Expand Up @@ -169,6 +170,8 @@ defmodule SupavisorWeb.TenantController do
def delete(conn, %{"external_id" => id}) do
code = if Tenants.delete_tenant_by_external_id(id), do: 204, else: 404

Logger.info("Delete cache dist #{id}: #{inspect(Supavisor.del_all_cache_dist(id))}")

send_resp(conn, code, "")
end

Expand All @@ -188,6 +191,11 @@ defmodule SupavisorWeb.TenantController do
Logger.metadata(project: external_id)
result = Supavisor.terminate_global(external_id) |> inspect()
Logger.warning("Terminate #{external_id}: #{result}")

Logger.info(
"Delete cache dist #{external_id}: #{inspect(Supavisor.del_all_cache_dist(external_id))}"
)

render(conn, "show_terminate.json", result: result)
end

Expand Down
16 changes: 16 additions & 0 deletions test/supavisor_web/controllers/tenant_controller_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,10 @@ defmodule SupavisorWeb.TenantControllerTest do
conn: conn,
tenant: %Tenant{external_id: external_id} = _tenant
} do
set_cache(external_id)
conn = put(conn, Routes.tenant_path(conn, :update, external_id), tenant: @update_attrs)
assert %{"external_id" => ^external_id} = json_response(conn, 200)["data"]
check_cache(external_id)

conn = get(conn, Routes.tenant_path(conn, :show, external_id))

Expand Down Expand Up @@ -108,7 +110,9 @@ defmodule SupavisorWeb.TenantControllerTest do
setup [:create_tenant]

test "deletes chosen tenant", %{conn: conn, tenant: %Tenant{external_id: external_id}} do
set_cache(external_id)
conn = delete(conn, Routes.tenant_path(conn, :delete, external_id))
check_cache(external_id)
assert response(conn, 204)
end
end
Expand All @@ -128,4 +132,16 @@ defmodule SupavisorWeb.TenantControllerTest do
tenant = tenant_fixture()
%{tenant: tenant}
end

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

defp check_cache(external_id) do
assert {:ok, nil} =
Cachex.get(Supavisor.Cache, {:user_cache, :single, "user", external_id, nil})

assert {:ok, nil} = Cachex.get(Supavisor.Cache, {:tenant_cache, external_id, nil})
end
end

0 comments on commit 7281330

Please sign in to comment.