diff --git a/lib/realtime/api.ex b/lib/realtime/api.ex index f57cdf934..79850d0f5 100644 --- a/lib/realtime/api.ex +++ b/lib/realtime/api.ex @@ -115,26 +115,32 @@ defmodule Realtime.Api do Logger.debug("create_tenant #{inspect(attrs, pretty: true)}") tenant_id = Map.get(attrs, :external_id) || Map.get(attrs, "external_id") - %Tenant{} - |> Tenant.changeset(attrs) - |> region_aware_write(:insert, tenant_id) + if master_region?() do + %Tenant{} + |> Tenant.changeset(attrs) + |> Repo.insert() + else + call(:create_tenant, [attrs], tenant_id) + end end @doc """ Updates a tenant. - - ## Examples - - iex> update_tenant(tenant, %{field: new_value}) - {:ok, %Tenant{}} - - iex> update_tenant(tenant, %{field: bad_value}) - {:error, %Ecto.Changeset{}} - """ - def update_tenant(%Tenant{} = tenant, attrs) do + @spec update_tenant_by_external_id(binary(), map()) :: {:ok, Tenant.t()} | {:error, term()} + def update_tenant_by_external_id(tenant_id, attrs) when is_binary(tenant_id) do + if master_region?() do + tenant_id + |> get_tenant_by_external_id(use_replica?: false) + |> update_tenant(attrs) + else + call(:update_tenant_by_external_id, [tenant_id, attrs], tenant_id) + end + end + + defp update_tenant(%Tenant{} = tenant, attrs) do changeset = Tenant.changeset(tenant, attrs) - updated = region_aware_write(changeset, :update, tenant.external_id) + updated = Repo.update(changeset) case updated do {:ok, tenant} -> @@ -150,42 +156,31 @@ defmodule Realtime.Api do updated end - @doc """ - Deletes a tenant. - - ## Examples - - iex> delete_tenant(tenant) - {:ok, %Tenant{}} - - iex> delete_tenant(tenant) - {:error, %Ecto.Changeset{}} - - """ - def delete_tenant(%Tenant{} = tenant), do: Repo.delete(tenant) - @spec delete_tenant_by_external_id(String.t()) :: boolean() def delete_tenant_by_external_id(id) do - from(t in Tenant, where: t.external_id == ^id) - |> region_aware_write(:delete_all, id) - |> case do - {num, _} when num > 0 -> true - _ -> false + if master_region?() do + query = from(t in Tenant, where: t.external_id == ^id) + {num, _} = Repo.delete_all(query) + num > 0 + else + call(:delete_tenant_by_external_id, [id], id) end end - @spec get_tenant_by_external_id(String.t(), atom()) :: Tenant.t() | nil - def get_tenant_by_external_id(external_id, repo \\ :replica) - when repo in [:primary, :replica] do - repo = - case repo do - :primary -> Repo - :replica -> Replica.replica() - end + @spec get_tenant_by_external_id(String.t(), Keyword.t()) :: Tenant.t() | nil + def get_tenant_by_external_id(external_id, opts \\ []) do + use_replica? = Keyword.get(opts, :use_replica?, true) - Tenant - |> repo.get_by(external_id: external_id) - |> repo.preload(:extensions) + cond do + use_replica? -> + Replica.replica().get_by(Tenant, external_id: external_id) |> Replica.replica().preload(:extensions) + + !use_replica? and master_region?() -> + Repo.get_by(Tenant, external_id: external_id) |> Repo.preload(:extensions) + + true -> + call(:get_tenant_by_external_id, [external_id, opts], external_id) + end end defp list_extensions(type) do @@ -195,26 +190,36 @@ defmodule Realtime.Api do end def rename_settings_field(from, to) do - for extension <- list_extensions("postgres_cdc_rls") do - {value, settings} = Map.pop(extension.settings, from) - new_settings = Map.put(settings, to, value) - - extension - |> Changeset.cast(%{settings: new_settings}, [:settings]) - |> region_aware_write(:update!, extension.external_id) + if master_region?() do + for extension <- list_extensions("postgres_cdc_rls") do + {value, settings} = Map.pop(extension.settings, from) + new_settings = Map.put(settings, to, value) + + extension + |> Changeset.cast(%{settings: new_settings}, [:settings]) + |> Repo.update() + end + else + call(:rename_settings_field, [from, to], from) end end + @spec preload_counters(nil | Realtime.Api.Tenant.t(), any()) :: nil | Realtime.Api.Tenant.t() @doc """ Updates the migrations_ran field for a tenant. """ @spec update_migrations_ran(binary(), integer()) :: {:ok, Tenant.t()} | {:error, term()} def update_migrations_ran(external_id, count) do - external_id - |> Cache.get_tenant_by_external_id() - |> Tenant.changeset(%{migrations_ran: count}) - |> region_aware_write(:update!, external_id) - |> tap(fn _ -> Cache.distributed_invalidate_tenant_cache(external_id) end) + if master_region?() do + tenant = get_tenant_by_external_id(external_id, use_replica?: false) + + tenant + |> Tenant.changeset(%{migrations_ran: count}) + |> Repo.update() + |> tap(fn _ -> Cache.distributed_invalidate_tenant_cache(external_id) end) + else + call(:update_migrations_ran, [external_id, count], external_id) + end end def preload_counters(nil), do: nil @@ -257,21 +262,13 @@ defmodule Realtime.Api do defp maybe_restart_db_connection(_changeset), do: nil - defp local_call? do + defp master_region? do region = Application.get_env(:realtime, :region) master_region = Application.get_env(:realtime, :master_region) || region region == master_region end - defp region_aware_write(%struct{} = argument, operation, tenant_id) when struct in [Changeset, Ecto.Query] do - if local_call?(), - do: local_call(operation, [argument], tenant_id), - else: remote_call(operation, [argument], tenant_id) - end - - defp local_call(operation, args, _tenant_id), do: apply(Realtime.Repo, operation, args) - - defp remote_call(operation, args, tenant_id) do + defp call(operation, args, tenant_id) do master_region = Application.get_env(:realtime, :master_region) with {:ok, master_node} <- Nodes.node_from_region(master_region, self()), @@ -281,7 +278,7 @@ defmodule Realtime.Api do end defp wrapped_call(master_node, operation, args, tenant_id) do - case GenRpc.call(master_node, Realtime.Repo, operation, args, tenant_id: tenant_id) do + case GenRpc.call(master_node, __MODULE__, operation, args, tenant_id: tenant_id) do {:error, :rpc_error, reason} -> {:error, reason} {:error, reason} -> {:error, reason} result -> {:ok, result} diff --git a/lib/realtime/repo_replica.ex b/lib/realtime/repo_replica.ex index 8079ccb8e..9d3c10de8 100644 --- a/lib/realtime/repo_replica.ex +++ b/lib/realtime/repo_replica.ex @@ -51,11 +51,9 @@ defmodule Realtime.Repo.Replica do # Do not create module if replica isn't set or configuration is not present cond do is_nil(replica) -> - Logger.info("Replica region not found, defaulting to Realtime.Repo") Realtime.Repo is_nil(replica_conf) -> - Logger.info("Replica config not found for #{region} region") Realtime.Repo true -> diff --git a/lib/realtime/tenants.ex b/lib/realtime/tenants.ex index acc388f72..87d19cd65 100644 --- a/lib/realtime/tenants.ex +++ b/lib/realtime/tenants.ex @@ -458,8 +458,7 @@ defmodule Realtime.Tenants do @spec suspend_tenant_by_external_id(String.t()) :: {:ok, Tenant.t()} | {:error, term()} def suspend_tenant_by_external_id(external_id) do external_id - |> Cache.get_tenant_by_external_id() - |> Api.update_tenant(%{suspend: true}) + |> Api.update_tenant_by_external_id(%{suspend: true}) |> tap(fn _ -> broadcast_operation_event(:suspend_tenant, external_id) end) end @@ -469,8 +468,7 @@ defmodule Realtime.Tenants do @spec unsuspend_tenant_by_external_id(String.t()) :: {:ok, Tenant.t()} | {:error, term()} def unsuspend_tenant_by_external_id(external_id) do external_id - |> Cache.get_tenant_by_external_id() - |> Api.update_tenant(%{suspend: false}) + |> Api.update_tenant_by_external_id(%{suspend: false}) |> tap(fn _ -> broadcast_operation_event(:unsuspend_tenant, external_id) end) end diff --git a/lib/realtime_web/controllers/tenant_controller.ex b/lib/realtime_web/controllers/tenant_controller.ex index 4beb6f209..2b8474800 100644 --- a/lib/realtime_web/controllers/tenant_controller.ex +++ b/lib/realtime_web/controllers/tenant_controller.ex @@ -137,7 +137,7 @@ defmodule RealtimeWeb.TenantController do ) def update(conn, %{"tenant_id" => external_id, "tenant" => tenant_params}) do - tenant = Api.get_tenant_by_external_id(external_id) + tenant = Api.get_tenant_by_external_id(external_id, use_replica?: false) case tenant do nil -> @@ -160,7 +160,7 @@ defmodule RealtimeWeb.TenantController do end tenant -> - with {:ok, %Tenant{} = tenant} <- Api.update_tenant(tenant, tenant_params) do + with {:ok, %Tenant{} = tenant} <- Api.update_tenant_by_external_id(tenant.external_id, tenant_params) do conn |> put_status(:ok) |> put_resp_header("location", Routes.tenant_path(conn, :show, tenant)) @@ -192,7 +192,7 @@ defmodule RealtimeWeb.TenantController do def delete(conn, %{"tenant_id" => tenant_id}) do stop_all_timeout = Enum.count(PostgresCdc.available_drivers()) * 1_000 - with %Tenant{} = tenant <- Api.get_tenant_by_external_id(tenant_id, :primary), + with %Tenant{} = tenant <- Api.get_tenant_by_external_id(tenant_id, use_replica: false), _ <- Tenants.suspend_tenant_by_external_id(tenant_id), true <- Api.delete_tenant_by_external_id(tenant_id), true <- Cache.distributed_invalidate_tenant_cache(tenant_id), @@ -231,7 +231,7 @@ defmodule RealtimeWeb.TenantController do ) def reload(conn, %{"tenant_id" => tenant_id}) do - case Tenants.get_tenant_by_external_id(tenant_id) do + case Api.get_tenant_by_external_id(tenant_id, use_replica?: false) do nil -> log_error("TenantNotFound", "Tenant not found") diff --git a/lib/realtime_web/plugs/assign_tenant.ex b/lib/realtime_web/plugs/assign_tenant.ex index 69b52e8ab..b60d3e28a 100644 --- a/lib/realtime_web/plugs/assign_tenant.ex +++ b/lib/realtime_web/plugs/assign_tenant.ex @@ -20,7 +20,7 @@ defmodule RealtimeWeb.Plugs.AssignTenant do def call(%Plug.Conn{host: host} = conn, _opts) do with {:ok, external_id} <- Database.get_external_id(host), - %Tenant{} = tenant <- Api.get_tenant_by_external_id(external_id) do + %Tenant{} = tenant <- Api.get_tenant_by_external_id(external_id, use_replica?: true) do Logger.metadata(external_id: external_id, project: external_id) OpenTelemetry.Tracer.set_attributes(external_id: external_id) diff --git a/mix.exs b/mix.exs index ccbbcbd95..cc872494d 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule Realtime.MixProject do def project do [ app: :realtime, - version: "2.66.1", + version: "2.66.2", elixir: "~> 1.18", elixirc_paths: elixirc_paths(Mix.env()), start_permanent: Mix.env() == :prod, diff --git a/test/integration/region_aware_routing_test.exs b/test/integration/region_aware_routing_test.exs index a22b73313..f9f5178f2 100644 --- a/test/integration/region_aware_routing_test.exs +++ b/test/integration/region_aware_routing_test.exs @@ -49,8 +49,8 @@ defmodule Realtime.Integration.RegionAwareRoutingTest do Mimic.expect(Realtime.GenRpc, :call, fn node, mod, func, args, opts -> assert node == master_node - assert mod == Realtime.Repo - assert func == :insert + assert mod == Realtime.Api + assert func == :create_tenant assert opts[:tenant_id] == external_id call_original(GenRpc, :call, [node, mod, func, args, opts]) @@ -80,16 +80,16 @@ defmodule Realtime.Integration.RegionAwareRoutingTest do Realtime.GenRpc |> Mimic.expect(:call, fn node, mod, func, args, opts -> assert node == master_node - assert mod == Realtime.Repo - assert func == :insert + assert mod == Realtime.Api + assert func == :create_tenant assert opts[:tenant_id] == tenant_attrs["external_id"] call_original(GenRpc, :call, [node, mod, func, args, opts]) end) |> Mimic.expect(:call, fn node, mod, func, args, opts -> assert node == master_node - assert mod == Realtime.Repo - assert func == :update + assert mod == Realtime.Api + assert func == :update_tenant_by_external_id assert opts[:tenant_id] == tenant_attrs["external_id"] call_original(GenRpc, :call, [node, mod, func, args, opts]) @@ -98,7 +98,7 @@ defmodule Realtime.Integration.RegionAwareRoutingTest do tenant = tenant_fixture(tenant_attrs) new_name = "updated_via_routing" - result = Api.update_tenant(tenant, %{name: new_name}) + result = Api.update_tenant_by_external_id(tenant.external_id, %{name: new_name}) assert {:ok, %Tenant{} = updated} = result assert updated.name == new_name @@ -123,16 +123,16 @@ defmodule Realtime.Integration.RegionAwareRoutingTest do Realtime.GenRpc |> Mimic.expect(:call, fn node, mod, func, args, opts -> assert node == master_node - assert mod == Realtime.Repo - assert func == :insert + assert mod == Realtime.Api + assert func == :create_tenant assert opts[:tenant_id] == tenant_attrs["external_id"] call_original(GenRpc, :call, [node, mod, func, args, opts]) end) |> Mimic.expect(:call, fn node, mod, func, args, opts -> assert node == master_node - assert mod == Realtime.Repo - assert func == :delete_all + assert mod == Realtime.Api + assert func == :delete_tenant_by_external_id assert opts[:tenant_id] == tenant_attrs["external_id"] call_original(GenRpc, :call, [node, mod, func, args, opts]) @@ -164,16 +164,16 @@ defmodule Realtime.Integration.RegionAwareRoutingTest do Realtime.GenRpc |> Mimic.expect(:call, fn node, mod, func, args, opts -> assert node == master_node - assert mod == Realtime.Repo - assert func == :insert + assert mod == Realtime.Api + assert func == :create_tenant assert opts[:tenant_id] == tenant_attrs["external_id"] call_original(GenRpc, :call, [node, mod, func, args, opts]) end) |> Mimic.expect(:call, fn node, mod, func, args, opts -> assert node == master_node - assert mod == Realtime.Repo - assert func == :update! + assert mod == Realtime.Api + assert func == :update_migrations_ran assert opts[:tenant_id] == tenant_attrs["external_id"] call_original(GenRpc, :call, [node, mod, func, args, opts]) @@ -184,7 +184,7 @@ defmodule Realtime.Integration.RegionAwareRoutingTest do new_migrations_ran = 5 result = Api.update_migrations_ran(tenant.external_id, new_migrations_ran) - assert %Tenant{} = updated = result + assert {:ok, updated} = result assert updated.migrations_ran == new_migrations_ran reloaded = Realtime.Repo.get(Tenant, tenant.id) diff --git a/test/integration/rt_channel_test.exs b/test/integration/rt_channel_test.exs index c6e8756cc..5671b6504 100644 --- a/test/integration/rt_channel_test.exs +++ b/test/integration/rt_channel_test.exs @@ -1623,9 +1623,8 @@ defmodule Realtime.Integration.RtChannelTest do assert_receive %Message{event: "phx_reply", payload: %{"status" => "ok"}}, 500 assert_receive %Message{event: "presence_state"}, 500 - tenant = Tenants.get_tenant_by_external_id(tenant.external_id) - Realtime.Api.update_tenant(tenant, %{jwt_jwks: %{keys: ["potato"]}}) + Realtime.Api.update_tenant_by_external_id(tenant.external_id, %{jwt_jwks: %{keys: ["potato"]}}) assert_process_down(socket) end @@ -1643,9 +1642,7 @@ defmodule Realtime.Integration.RtChannelTest do assert_receive %Message{event: "phx_reply", payload: %{"status" => "ok"}}, 500 assert_receive %Message{event: "presence_state"}, 500 - tenant = Tenants.get_tenant_by_external_id(tenant.external_id) - Realtime.Api.update_tenant(tenant, %{jwt_secret: "potato"}) - + Realtime.Api.update_tenant_by_external_id(tenant.external_id, %{jwt_secret: "potato"}) assert_process_down(socket) end @@ -1663,9 +1660,7 @@ defmodule Realtime.Integration.RtChannelTest do assert_receive %Message{event: "phx_reply", payload: %{"status" => "ok"}}, 500 assert_receive %Message{event: "presence_state"}, 500 - tenant = Tenants.get_tenant_by_external_id(tenant.external_id) - Realtime.Api.update_tenant(tenant, %{private_only: true}) - + Realtime.Api.update_tenant_by_external_id(tenant.external_id, %{private_only: true}) assert_process_down(socket) end @@ -1683,8 +1678,7 @@ defmodule Realtime.Integration.RtChannelTest do assert_receive %Message{event: "phx_reply", payload: %{"status" => "ok"}}, 500 assert_receive %Message{event: "presence_state"}, 500 - tenant = Tenants.get_tenant_by_external_id(tenant.external_id) - Realtime.Api.update_tenant(tenant, %{max_concurrent_users: 100}) + Realtime.Api.update_tenant_by_external_id(tenant.external_id, %{max_concurrent_users: 100}) refute_receive %Message{ topic: ^realtime_topic, diff --git a/test/realtime/api_test.exs b/test/realtime/api_test.exs index e726b1caf..e3a78dd27 100644 --- a/test/realtime/api_test.exs +++ b/test/realtime/api_test.exs @@ -93,7 +93,7 @@ defmodule Realtime.ApiTest do end end - describe "get_tenant_by_external_id/1" do + describe "get_tenant_by_external_id/2" do setup [:create_tenants] test "fetch by external id", %{tenants: [tenant | _]} do @@ -104,19 +104,37 @@ defmodule Realtime.ApiTest do password = extension.settings["db_password"] assert ^password = "v1QVng3N+pZd/0AEObABwg==" end + + test "fetch by external id using replica", %{tenants: [tenant | _]} do + %Tenant{extensions: [%Extensions{} = extension]} = + Api.get_tenant_by_external_id(tenant.external_id, use_replica?: true) + + assert Map.has_key?(extension.settings, "db_password") + password = extension.settings["db_password"] + assert ^password = "v1QVng3N+pZd/0AEObABwg==" + end + + test "fetch by external id using no replica", %{tenants: [tenant | _]} do + %Tenant{extensions: [%Extensions{} = extension]} = + Api.get_tenant_by_external_id(tenant.external_id, use_replica?: false) + + assert Map.has_key?(extension.settings, "db_password") + password = extension.settings["db_password"] + assert ^password = "v1QVng3N+pZd/0AEObABwg==" + end end - describe "update_tenant/2" do + describe "update_tenant_by_external_id/2" do setup [:create_tenants] - test "valid data updates the tenant", %{tenants: [tenant | _]} do + test "valid data updates the tenant using external_id", %{tenants: [tenant | _]} do update_attrs = %{ external_id: tenant.external_id, jwt_secret: "some updated jwt_secret", name: "some updated name" } - assert {:ok, %Tenant{} = tenant} = Api.update_tenant(tenant, update_attrs) + assert {:ok, %Tenant{} = tenant} = Api.update_tenant_by_external_id(tenant.external_id, update_attrs) assert tenant.external_id == tenant.external_id assert tenant.jwt_secret == Crypto.encrypt!("some updated jwt_secret") @@ -124,30 +142,31 @@ defmodule Realtime.ApiTest do end test "invalid data returns error changeset", %{tenants: [tenant | _]} do - assert {:error, %Ecto.Changeset{}} = Api.update_tenant(tenant, %{external_id: nil, jwt_secret: nil, name: nil}) + assert {:error, %Ecto.Changeset{}} = + Api.update_tenant_by_external_id(tenant.external_id, %{external_id: nil, jwt_secret: nil, name: nil}) end test "valid data and jwks change will send disconnect event", %{tenants: [tenant | _]} do :ok = Phoenix.PubSub.subscribe(Realtime.PubSub, "realtime:operations:" <> tenant.external_id) - assert {:ok, %Tenant{}} = Api.update_tenant(tenant, %{jwt_jwks: %{keys: ["test"]}}) + assert {:ok, %Tenant{}} = Api.update_tenant_by_external_id(tenant.external_id, %{jwt_jwks: %{keys: ["test"]}}) assert_receive :disconnect, 500 end test "valid data and jwt_secret change will send disconnect event", %{tenants: [tenant | _]} do :ok = Phoenix.PubSub.subscribe(Realtime.PubSub, "realtime:operations:" <> tenant.external_id) - assert {:ok, %Tenant{}} = Api.update_tenant(tenant, %{jwt_secret: "potato"}) + assert {:ok, %Tenant{}} = Api.update_tenant_by_external_id(tenant.external_id, %{jwt_secret: "potato"}) assert_receive :disconnect, 500 end test "valid data and suspend change will send disconnect event", %{tenants: [tenant | _]} do :ok = Phoenix.PubSub.subscribe(Realtime.PubSub, "realtime:operations:" <> tenant.external_id) - assert {:ok, %Tenant{}} = Api.update_tenant(tenant, %{suspend: true}) + assert {:ok, %Tenant{}} = Api.update_tenant_by_external_id(tenant.external_id, %{suspend: true}) assert_receive :disconnect, 500 end test "valid data but not updating jwt_secret or jwt_jwks won't send event", %{tenants: [tenant | _]} do :ok = Phoenix.PubSub.subscribe(Realtime.PubSub, "realtime:operations:" <> tenant.external_id) - assert {:ok, %Tenant{}} = Api.update_tenant(tenant, %{max_events_per_second: 100}) + assert {:ok, %Tenant{}} = Api.update_tenant_by_external_id(tenant.external_id, %{max_events_per_second: 100}) refute_receive :disconnect, 500 end @@ -156,7 +175,7 @@ defmodule Realtime.ApiTest do {:ok, old_pid} = Connect.lookup_or_start_connection(tenant.external_id) Process.monitor(old_pid) - assert {:ok, %Tenant{}} = Api.update_tenant(tenant, %{jwt_secret: "potato"}) + assert {:ok, %Tenant{}} = Api.update_tenant_by_external_id(tenant.external_id, %{jwt_secret: "potato"}) assert_receive {:DOWN, _, :process, ^old_pid, :shutdown}, 500 refute Process.alive?(old_pid) Process.sleep(100) @@ -169,7 +188,7 @@ defmodule Realtime.ApiTest do {:ok, old_pid} = Connect.lookup_or_start_connection(tenant.external_id) Process.monitor(old_pid) - assert {:ok, %Tenant{}} = Api.update_tenant(tenant, %{suspend: true}) + assert {:ok, %Tenant{}} = Api.update_tenant_by_external_id(tenant.external_id, %{suspend: true}) assert_receive {:DOWN, _, :process, ^old_pid, :shutdown}, 500 refute Process.alive?(old_pid) Process.sleep(100) @@ -181,7 +200,7 @@ defmodule Realtime.ApiTest do expect(Realtime.Tenants.Cache, :distributed_invalidate_tenant_cache, fn _ -> :ok end) {:ok, old_pid} = Connect.lookup_or_start_connection(tenant.external_id) - assert {:ok, %Tenant{}} = Api.update_tenant(tenant, %{max_concurrent_users: 100}) + assert {:ok, %Tenant{}} = Api.update_tenant_by_external_id(tenant.external_id, %{max_concurrent_users: 100}) refute_receive {:DOWN, _, :process, ^old_pid, :shutdown}, 500 assert Process.alive?(old_pid) assert {:ok, new_pid} = Connect.lookup_or_start_connection(tenant.external_id) @@ -213,7 +232,7 @@ defmodule Realtime.ApiTest do {:ok, old_pid} = Connect.lookup_or_start_connection(tenant.external_id) Process.monitor(old_pid) - assert {:ok, %Tenant{}} = Api.update_tenant(tenant, %{extensions: extensions}) + assert {:ok, %Tenant{}} = Api.update_tenant_by_external_id(tenant.external_id, %{extensions: extensions}) assert_receive {:DOWN, _, :process, ^old_pid, :shutdown}, 500 refute Process.alive?(old_pid) Process.sleep(100) @@ -222,21 +241,13 @@ defmodule Realtime.ApiTest do end test "valid data and change to tenant data will refresh cache", %{tenants: [tenant | _]} do - assert {:ok, %Tenant{}} = Api.update_tenant(tenant, %{name: "new_name"}) + assert {:ok, %Tenant{}} = Api.update_tenant_by_external_id(tenant.external_id, %{name: "new_name"}) assert %Tenant{name: "new_name"} = Realtime.Tenants.Cache.get_tenant_by_external_id(tenant.external_id) end test "valid data and no changes to tenant will not refresh cache", %{tenants: [tenant | _]} do reject(&Realtime.Tenants.Cache.distributed_invalidate_tenant_cache/1) - assert {:ok, %Tenant{}} = Api.update_tenant(tenant, %{name: tenant.name}) - end - end - - describe "delete_tenant/1" do - test "deletes the tenant" do - tenant = tenant_fixture() - assert {:ok, %Tenant{}} = Api.delete_tenant(tenant) - assert_raise Ecto.NoResultsError, fn -> Api.get_tenant!(tenant.id) end + assert {:ok, %Tenant{}} = Api.update_tenant_by_external_id(tenant.external_id, %{name: tenant.name}) end end diff --git a/test/realtime/database_test.exs b/test/realtime/database_test.exs index 0226aa4c2..f8e8c8b86 100644 --- a/test/realtime/database_test.exs +++ b/test/realtime/database_test.exs @@ -327,6 +327,6 @@ defmodule Realtime.DatabaseTest do put_in(extension, ["settings", "db_port"], db_port) ] - Realtime.Api.update_tenant(tenant, %{extensions: extensions}) + Realtime.Api.update_tenant_by_external_id(tenant.external_id, %{extensions: extensions}) end end diff --git a/test/realtime/extensions/cdc_rls/replication_poller_test.exs b/test/realtime/extensions/cdc_rls/replication_poller_test.exs index 73ce16a86..0fba63a66 100644 --- a/test/realtime/extensions/cdc_rls/replication_poller_test.exs +++ b/test/realtime/extensions/cdc_rls/replication_poller_test.exs @@ -37,9 +37,7 @@ defmodule Realtime.Extensions.PostgresCdcRls.ReplicationPollerTest do tenant = Containers.checkout_tenant(run_migrations: true) - {:ok, tenant} = - Realtime.Api.get_tenant_by_external_id(tenant.external_id) - |> Realtime.Api.update_tenant(%{"max_events_per_second" => 123}) + {:ok, tenant} = Realtime.Api.update_tenant_by_external_id(tenant.external_id, %{"max_events_per_second" => 123}) subscribers_pids_table = :ets.new(__MODULE__, [:public, :bag]) subscribers_nodes_table = :ets.new(__MODULE__, [:public, :set]) diff --git a/test/realtime/tenants/authorization_test.exs b/test/realtime/tenants/authorization_test.exs index 48e94a31c..8b57135ef 100644 --- a/test/realtime/tenants/authorization_test.exs +++ b/test/realtime/tenants/authorization_test.exs @@ -320,7 +320,7 @@ defmodule Realtime.Tenants.AuthorizationTest do extensions = [Map.from_struct(%{extension | :settings => settings})] - {:ok, tenant} = Realtime.Api.update_tenant(tenant, %{extensions: extensions}) + {:ok, tenant} = Realtime.Api.update_tenant_by_external_id(tenant.external_id, %{extensions: extensions}) # Warm cache to avoid Cachex and Ecto.Sandbox ownership issues Cachex.put!(Realtime.Tenants.Cache, {{:get_tenant_by_external_id, 1}, [tenant.external_id]}, {:cached, tenant}) diff --git a/test/realtime/tenants/connect_test.exs b/test/realtime/tenants/connect_test.exs index 804b3018f..0b02c7b90 100644 --- a/test/realtime/tenants/connect_test.exs +++ b/test/realtime/tenants/connect_test.exs @@ -658,6 +658,6 @@ defmodule Realtime.Tenants.ConnectTest do put_in(extension, ["settings", "db_port"], db_port) ] - Realtime.Api.update_tenant(tenant, %{extensions: extensions}) + Realtime.Api.update_tenant_by_external_id(tenant.external_id, %{extensions: extensions}) end end diff --git a/test/realtime_web/channels/realtime_channel_test.exs b/test/realtime_web/channels/realtime_channel_test.exs index 7d7fbd6d2..7e165a606 100644 --- a/test/realtime_web/channels/realtime_channel_test.exs +++ b/test/realtime_web/channels/realtime_channel_test.exs @@ -1258,7 +1258,7 @@ defmodule RealtimeWeb.RealtimeChannelTest do put_in(extension, ["settings", "db_port"], db_port) ] - with {:ok, tenant} <- Realtime.Api.update_tenant(tenant, %{extensions: extensions}) do + with {:ok, tenant} <- Realtime.Api.update_tenant_by_external_id(tenant.external_id, %{extensions: extensions}) do Cachex.put!(Realtime.Tenants.Cache, {{:get_tenant_by_external_id, 1}, [tenant.external_id]}, {:cached, tenant}) {:ok, tenant} end diff --git a/test/realtime_web/plugs/rate_limiter_test.exs b/test/realtime_web/plugs/rate_limiter_test.exs index 78b22fc8f..1cca58346 100644 --- a/test/realtime_web/plugs/rate_limiter_test.exs +++ b/test/realtime_web/plugs/rate_limiter_test.exs @@ -47,9 +47,7 @@ defmodule RealtimeWeb.Plugs.RateLimiterTest do end test "serve a 200 when rate limit is set to 100", %{conn: conn} do - {:ok, _tenant} = - Api.get_tenant_by_external_id(@tenant["external_id"]) - |> Api.update_tenant(%{"max_events_per_second" => 100}) + {:ok, _tenant} = Api.update_tenant_by_external_id(@tenant["external_id"], %{"max_events_per_second" => 100}) conn = conn diff --git a/test/support/containers.ex b/test/support/containers.ex index c3eaf50a9..1f5f54ee5 100644 --- a/test/support/containers.ex +++ b/test/support/containers.ex @@ -223,7 +223,7 @@ defmodule Containers do # Avoiding to use Tenants.update_migrations_ran/2 because it touches Cachex and it doesn't play well with # Ecto Sandbox :ok = Migrations.create_partitions(conn) - {:ok, tenant} = Realtime.Api.update_tenant(tenant, %{migrations_ran: count}) + {:ok, tenant} = Realtime.Api.update_tenant_by_external_id(tenant.external_id, %{migrations_ran: count}) tenant error -> diff --git a/test/test_helper.exs b/test/test_helper.exs index 1d0f4d3ad..767212e24 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -15,7 +15,7 @@ end {:ok, _pid} = Containers.start_link(max_cases) -for tenant <- Api.list_tenants(), do: Api.delete_tenant(tenant) +for tenant <- Api.list_tenants(), do: Api.delete_tenant_by_external_id(tenant.external_id) tenant_name = "dev_tenant" tenant = Containers.initialize(tenant_name) @@ -53,6 +53,7 @@ Mimic.copy(Realtime.Database) Mimic.copy(Realtime.GenCounter) Mimic.copy(Realtime.GenRpc) Mimic.copy(Realtime.Nodes) +Mimic.copy(Realtime.Repo.Replica) Mimic.copy(Realtime.RateCounter) Mimic.copy(Realtime.Tenants.Authorization) Mimic.copy(Realtime.Tenants.Cache)