summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Strizhakov <alex.strizhakov@gmail.com>2020-07-24 16:21:48 +0300
committerAlexander Strizhakov <alex.strizhakov@gmail.com>2020-09-24 10:12:05 +0300
commit56ff851d5942989215549f2b34e2923f9963f9bd (patch)
tree20044d7d2959995b4b4aaa04f7af13e4e2e72fdf
parent27482d917b27a01f3fe5a8df4d915cefafd157fb (diff)
some refactor
-rw-r--r--lib/mix/pleroma.ex2
-rw-r--r--lib/pleroma/application.ex19
-rw-r--r--lib/pleroma/application/dependencies.ex163
-rw-r--r--lib/pleroma/application/dependencies_state.ex (renamed from lib/pleroma/application/agent.ex)2
-rw-r--r--lib/pleroma/application/dependencies_supervisor.ex155
-rw-r--r--lib/pleroma/application/dynamic_supervisor.ex202
-rw-r--r--lib/pleroma/application/static.ex77
-rw-r--r--lib/pleroma/web/admin_api/controllers/admin_api_controller.ex4
-rw-r--r--lib/pleroma/web/admin_api/controllers/config_controller.ex16
-rw-r--r--mix.exs2
-rw-r--r--test/application/dynamic_supervisor_test.exs193
-rw-r--r--test/web/admin_api/controllers/admin_api_controller_test.exs2
12 files changed, 542 insertions, 295 deletions
diff --git a/lib/mix/pleroma.ex b/lib/mix/pleroma.ex
index f91c66b30..301fef8b9 100644
--- a/lib/mix/pleroma.ex
+++ b/lib/mix/pleroma.ex
@@ -52,7 +52,7 @@ defmodule Mix.Pleroma do
end
cachex_children =
- Enum.map(@cachex_children, &Pleroma.Application.Static.build_cachex({&1, []}))
+ Enum.map(@cachex_children, &Pleroma.Application.Dependencies.cachex_spec({&1, []}))
Supervisor.start_link(children ++ cachex_children,
strategy: :one_for_one,
diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex
index b1ad67ee8..1736aac23 100644
--- a/lib/pleroma/application.ex
+++ b/lib/pleroma/application.ex
@@ -54,6 +54,7 @@ defmodule Pleroma.Application do
Config.DeprecationWarnings.warn()
Pleroma.Plugs.HTTPSecurityPlug.warn_if_disabled()
Pleroma.Application.Requirements.verify!()
+ Pleroma.Application.Dependencies.save_dynamic_children_config_relations()
setup_instrumenters()
load_custom_modules()
check_system_commands()
@@ -84,8 +85,8 @@ defmodule Pleroma.Application do
# Define workers and child supervisors to be supervised
children = [
Pleroma.Repo,
- Pleroma.Application.DynamicSupervisor,
- Pleroma.Application.Agent
+ Pleroma.Application.DependenciesSupervisor,
+ Pleroma.Application.DependenciesState
]
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
@@ -98,16 +99,22 @@ defmodule Pleroma.Application do
Pleroma.Config.Environment.load_and_update()
end
- def start_phase(:static_children, :normal, _args) do
+ def start_phase(:static_deps, :normal, _args) do
# Start static children,
# which don't require any configuration or can be configured in runtime
- Pleroma.Application.Static.start_children(@env)
+
+ @env
+ |> Pleroma.Application.Dependencies.static()
+ |> Pleroma.Application.DependenciesSupervisor.start_children(:static)
end
- def start_phase(:dynamic_children, :normal, _args) do
+ def start_phase(:dynamic_deps, :normal, _args) do
# Start dynamic children,
# which require restart after some config changes
- Pleroma.Application.DynamicSupervisor.start_children(@env)
+
+ @env
+ |> Pleroma.Application.Dependencies.dynamic()
+ |> Pleroma.Application.DependenciesSupervisor.start_children(:dynamic)
end
def load_custom_modules do
diff --git a/lib/pleroma/application/dependencies.ex b/lib/pleroma/application/dependencies.ex
new file mode 100644
index 000000000..d796a0473
--- /dev/null
+++ b/lib/pleroma/application/dependencies.ex
@@ -0,0 +1,163 @@
+# # Pleroma: A lightweight social networking server
+# # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# # SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Application.Dependencies do
+ alias Pleroma.Application.GunSupervisor
+ alias Pleroma.Application.HackneySupervisor
+ alias Pleroma.Config
+ alias Pleroma.Web.Endpoint
+
+ require Cachex.Spec
+
+ @type config_path() :: {atom(), atom()} | {atom(), atom(), [atom()]}
+ @type relation() :: {config_path(), module()}
+
+ @spec static(Pleroma.Application.env()) :: [:supervisor.child_spec() | module()]
+ def static(env) do
+ [
+ Pleroma.Emoji,
+ Pleroma.Stats,
+ Pleroma.JobQueueMonitor,
+ %{
+ id: :web_push_init,
+ start: {Task, :start_link, [&Pleroma.Web.Push.init/0]},
+ restart: :temporary
+ }
+ ]
+ |> add_cachex_children()
+ |> maybe_add_init_internal_fetch_actor_task(env)
+ |> maybe_add(:sockets, env)
+ end
+
+ @spec dynamic(Pleroma.Application.env()) :: [:supervisor.child_spec() | module()]
+ def dynamic(env) do
+ [
+ Pleroma.Plugs.RateLimiter.Supervisor,
+ Oban,
+ Endpoint,
+ Pleroma.Gopher.Server,
+ Pleroma.Web.ChatChannel.ChatChannelState
+ ]
+ |> add_http_children(env)
+ |> maybe_add(:streamer, env)
+ end
+
+ @spec cachex_spec({String.t(), keyword()}) :: :supervisor.child_spec()
+ def cachex_spec({type, opts}) do
+ %{
+ id: String.to_atom("cachex_" <> type),
+ start: {Cachex, :start_link, [String.to_atom(type <> "_cache"), opts]},
+ type: :worker
+ }
+ end
+
+ defp add_cachex_children(children) do
+ cachex_children =
+ [
+ {"used_captcha", ttl_interval: seconds_valid_interval()},
+ {"user", default_ttl: 25_000, ttl_interval: 1000, limit: 2500},
+ {"object", default_ttl: 25_000, ttl_interval: 1000, limit: 2500},
+ {"rich_media", default_ttl: :timer.minutes(120), limit: 5000},
+ {"scrubber", limit: 2500},
+ {"idempotency", expiration: cachex_expiration(6 * 60 * 60, 60), limit: 2500},
+ {"web_resp", limit: 2500},
+ {"emoji_packs", expiration: cachex_expiration(5 * 60, 60), limit: 10},
+ {"failed_proxy_url", limit: 2500},
+ {"banned_urls", default_ttl: :timer.hours(24 * 30), limit: 5_000}
+ ]
+ |> Enum.map(&cachex_spec/1)
+
+ children ++ cachex_children
+ end
+
+ defp cachex_expiration(default, interval) do
+ Cachex.Spec.expiration(default: :timer.seconds(default), interval: :timer.seconds(interval))
+ end
+
+ defp seconds_valid_interval do
+ [Pleroma.Captcha, :seconds_valid]
+ |> Config.get!()
+ |> :timer.seconds()
+ end
+
+ defp maybe_add_init_internal_fetch_actor_task(children, :test), do: children
+
+ defp maybe_add_init_internal_fetch_actor_task(children, _) do
+ children ++
+ [
+ %{
+ id: :internal_fetch_init,
+ start: {Task, :start_link, [&Pleroma.Web.ActivityPub.InternalFetchActor.init/0]},
+ restart: :temporary
+ }
+ ]
+ end
+
+ defp maybe_add(children, _, env) when env in [:test, :benchmark], do: children
+ defp maybe_add(children, :sockets, _), do: [Pleroma.Web.FedSockets.Supervisor | children]
+ defp maybe_add(children, :streamer, _), do: [Pleroma.Web.Streamer.registry() | children]
+
+ defp add_http_children(children, :test) do
+ [HackneySupervisor, GunSupervisor | children]
+ end
+
+ defp add_http_children(children, _), do: [adapter_module() | children]
+
+ defp adapter_module do
+ if Application.get_env(:tesla, :adapter) == Tesla.Adapter.Gun do
+ GunSupervisor
+ else
+ HackneySupervisor
+ end
+ end
+
+ @spec save_dynamic_children_config_relations() :: :ok
+ def save_dynamic_children_config_relations do
+ relations = [
+ {{:pleroma, :chat}, Pleroma.Web.ChatChannel.ChatChannelState},
+ {{:pleroma, Oban}, Oban},
+ {{:pleroma, :rate_limit}, Pleroma.Plugs.RateLimiter.Supervisor},
+ {{:pleroma, :streamer}, Pleroma.Web.Streamer.registry()},
+ {{:pleroma, :pools}, GunSupervisor},
+ {{:pleroma, :connections_pool}, GunSupervisor},
+ {{:pleroma, :hackney_pools}, HackneySupervisor},
+ {{:pleroma, :gopher}, Pleroma.Gopher.Server},
+ {{:pleroma, Pleroma.Captcha, [:seconds_valid]}, Endpoint},
+ {{:pleroma, Pleroma.Upload, [:proxy_remote]}, adapter_module()},
+ {{:pleroma, :instance, [:upload_limit]}, Endpoint},
+ {{:pleroma, :fed_sockets, [:enabled]}, Endpoint}
+ ]
+
+ Config.put(:config_relations, relations)
+ end
+
+ @spec config_relations() :: [relation()]
+ def config_relations, do: Config.get(:config_relations)
+
+ @spec find_relation(config_path()) :: relation() | {:error, :relation_not_found}
+ def find_relation(path) do
+ with nil <- Enum.find(config_relations(), fn {key, _} -> key == path end) do
+ {:error, :relation_not_found}
+ end
+ end
+
+ @spec find_relation(atom(), atom(), any()) :: relation() | nil
+ def find_relation(group, key, value) do
+ Enum.find(config_relations(), fn
+ {{g, k}, _} ->
+ g == group and k == key
+
+ {{g, k, subkeys}, _} ->
+ Keyword.keyword?(value) and g == group and k == key and
+ Enum.any?(Keyword.keys(value), &(&1 in subkeys))
+ end)
+ end
+
+ @spec find_relations(module()) :: [relation()] | {:error, :relations_not_found}
+ def find_relations(module) do
+ with [] <- Enum.filter(config_relations(), fn {_, m} -> m == module end) do
+ {:error, :relations_not_found}
+ end
+ end
+end
diff --git a/lib/pleroma/application/agent.ex b/lib/pleroma/application/dependencies_state.ex
index 0efb8ae33..81ea76a22 100644
--- a/lib/pleroma/application/agent.ex
+++ b/lib/pleroma/application/dependencies_state.ex
@@ -2,7 +2,7 @@
# # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# # SPDX-License-Identifier: AGPL-3.0-only
-defmodule Pleroma.Application.Agent do
+defmodule Pleroma.Application.DependenciesState do
use Agent
def start_link(_) do
diff --git a/lib/pleroma/application/dependencies_supervisor.ex b/lib/pleroma/application/dependencies_supervisor.ex
new file mode 100644
index 000000000..cca7a0ba9
--- /dev/null
+++ b/lib/pleroma/application/dependencies_supervisor.ex
@@ -0,0 +1,155 @@
+# # Pleroma: A lightweight social networking server
+# # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# # SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Application.DependenciesSupervisor do
+ use DynamicSupervisor
+
+ alias Pleroma.Application.Dependencies
+ alias Pleroma.Application.DependenciesState
+
+ require Logger
+
+ @type child() ::
+ Supervisor.child_spec()
+ | {module(), term()}
+ | module()
+
+ @type child_type() :: :static | :dynamic
+
+ def start_link(_), do: DynamicSupervisor.start_link(__MODULE__, :no_arg, name: __MODULE__)
+
+ @impl true
+ def init(_), do: DynamicSupervisor.init(strategy: :one_for_one)
+
+ @spec start_children([:supervisor.child_spec() | module()], child_type()) ::
+ :ok | {:error, {:already_started, pid()} | :max_children | term()}
+ def start_children(children, :static) do
+ start_while(children, &start_static_child/1)
+ end
+
+ def start_children(children, :dynamic) do
+ start_while(children, &start_dynamic_child/1)
+ end
+
+ defp start_while(children, fun) do
+ Enum.reduce_while(children, :ok, fn child, acc ->
+ with {:ok, _} <- fun.(child) do
+ {:cont, acc}
+ else
+ :ignore ->
+ # consider this behavior is normal
+ Logger.info("#{inspect(child)} is ignored.")
+ {:cont, acc}
+
+ error ->
+ Logger.error("Child #{inspect(child)} can't be started. #{inspect(error)}")
+ {:halt, error}
+ end
+ end)
+ end
+
+ defp start_static_child(child) do
+ DynamicSupervisor.start_child(__MODULE__, child)
+ end
+
+ defp start_dynamic_child(child) do
+ with {:ok, pid} = result <- dynamic_child(child),
+ relations when is_list(relations) <- Dependencies.find_relations(child) do
+ Enum.each(relations, fn {key, _} ->
+ DependenciesState.put_pid(key, pid)
+ end)
+
+ result
+ end
+ end
+
+ defp dynamic_child(child) do
+ with {:error, _} = error <- DynamicSupervisor.start_child(__MODULE__, spec(child)) do
+ error
+ end
+ end
+
+ defp spec(Oban), do: {Oban, Pleroma.Config.get(Oban)}
+
+ defp spec(Pleroma.Web.StreamerRegistry) do
+ {Registry,
+ [
+ name: Pleroma.Web.Streamer.registry(),
+ keys: :duplicate,
+ partitions: System.schedulers_online()
+ ]}
+ end
+
+ defp spec(child), do: child
+
+ @spec put_paths([any()]) :: :ok
+ def put_paths([]), do: :ok
+
+ def put_paths(paths), do: DependenciesState.put_paths(paths)
+
+ @spec need_reboot?() :: boolean()
+ def need_reboot?, do: DependenciesState.paths() != []
+
+ @spec restart_children() :: :ok
+ def restart_children do
+ DependenciesState.get_and_reset_paths()
+ |> Enum.reduce_while(:ok, fn path, acc ->
+ with {:ok, _} <- restart_child(path) do
+ {:cont, acc}
+ else
+ :ignore ->
+ Logger.info("path #{inspect(path)} is ignored.")
+ {:cont, acc}
+
+ error ->
+ {:halt, error}
+ end
+ end)
+ end
+
+ defp restart_child(path) do
+ path
+ |> DependenciesState.pid()
+ |> restart_child(path)
+ end
+
+ defp restart_child(nil, path) do
+ # child wasn't started yet
+ with {_, module} <- Dependencies.find_relation(path) do
+ start_dynamic_child(module)
+ end
+ end
+
+ defp restart_child(pid, path) when is_pid(pid) do
+ # main module can have multiple keys
+ # first we search for main module
+ with {_, module} <- Dependencies.find_relation(path),
+ :ok <- terminate(pid, module),
+ # then we search for mappings, which depends on this main module
+ relations when is_list(relations) <- Dependencies.find_relations(module) do
+ Enum.each(relations, fn {key, _} ->
+ DependenciesState.delete_pid(key)
+ end)
+
+ start_dynamic_child(module)
+ else
+ error ->
+ Logger.warn(
+ "Child can't be restarted. PID - #{inspect(pid)}, path - #{inspect(path)} #{
+ inspect(error)
+ }"
+ )
+
+ error
+ end
+ end
+
+ defp terminate(pid, module) do
+ with {:error, :not_found} <- DynamicSupervisor.terminate_child(__MODULE__, pid),
+ # maybe child was restarted and pid wasn't updated in Agent, trying to find by module
+ pid when not is_nil(pid) <- Process.whereis(module) do
+ DynamicSupervisor.terminate_child(__MODULE__, pid)
+ end
+ end
+end
diff --git a/lib/pleroma/application/dynamic_supervisor.ex b/lib/pleroma/application/dynamic_supervisor.ex
deleted file mode 100644
index a488abffd..000000000
--- a/lib/pleroma/application/dynamic_supervisor.ex
+++ /dev/null
@@ -1,202 +0,0 @@
-# # Pleroma: A lightweight social networking server
-# # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# # SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Application.DynamicSupervisor do
- use DynamicSupervisor
-
- require Logger
-
- @type child() ::
- Supervisor.child_spec()
- | {module(), term()}
- | module()
-
- def start_link(_), do: DynamicSupervisor.start_link(__MODULE__, :no_arg, name: __MODULE__)
-
- @impl true
- def init(_), do: DynamicSupervisor.init(strategy: :one_for_one)
-
- @spec start_child(child()) :: DynamicSupervisor.on_start_child()
- def start_child(child), do: DynamicSupervisor.start_child(__MODULE__, child)
-
- @spec start_children(Pleroma.Application.env()) :: :ok
- def start_children(env) do
- [
- Pleroma.Plugs.RateLimiter.Supervisor,
- Oban,
- Pleroma.Web.Endpoint,
- Pleroma.Gopher.Server,
- Pleroma.Web.ChatChannel.ChatChannelState,
- Pleroma.Web.FedSockets.Supervisor
- ]
- |> add_http_children(env)
- |> add_streamer(env)
- |> Enum.each(&start_dynamic_child/1)
- end
-
- defp add_http_children(children, :test) do
- [Pleroma.Application.HackneySupervisor, Pleroma.Application.GunSupervisor | children]
- end
-
- defp add_http_children(children, _) do
- adapter = Application.get_env(:tesla, :adapter)
-
- child =
- if adapter == Tesla.Adapter.Gun do
- Pleroma.Application.GunSupervisor
- else
- Pleroma.Application.HackneySupervisor
- end
-
- [child | children]
- end
-
- defp add_streamer(children, env) when env in [:test, :benchmark], do: children
- defp add_streamer(children, _), do: [Pleroma.Web.StreamerRegistry | children]
-
- defp start_dynamic_child(child) do
- with {:ok, pid} <- dynamic_child(child),
- mappings <- find_mappings(child) do
- Enum.each(mappings, fn {key, _} ->
- Pleroma.Application.Agent.put_pid(key, pid)
- end)
- else
- :ignore ->
- # consider this behavior is normal
- Logger.info("#{inspect(child)} is ignored.")
-
- error ->
- Logger.warn(inspect(error))
- end
- end
-
- defp dynamic_child(child) do
- with {:error, _} = error <- DynamicSupervisor.start_child(__MODULE__, spec(child)) do
- error
- end
- end
-
- defp spec(Oban), do: {Oban, Pleroma.Config.get(Oban)}
-
- defp spec(Pleroma.Web.StreamerRegistry) do
- {Registry,
- [
- name: Pleroma.Web.Streamer.registry(),
- keys: :duplicate,
- partitions: System.schedulers_online()
- ]}
- end
-
- defp spec(child), do: child
-
- defp config_path_mappings do
- adapter_module =
- if Application.get_env(:tesla, :adapter) == Tesla.Adapter.Gun do
- Pleroma.Application.GunSupervisor
- else
- Pleroma.Application.HackneySupervisor
- end
-
- [
- {{:pleroma, :chat}, Pleroma.Web.ChatChannel.ChatChannelState},
- {{:pleroma, Oban}, Oban},
- {{:pleroma, :rate_limit}, Pleroma.Plugs.RateLimiter.Supervisor},
- {{:pleroma, :streamer}, Pleroma.Web.Streamer.registry()},
- {{:pleroma, :pools}, Pleroma.Application.GunSupervisor},
- {{:pleroma, :connections_pool}, Pleroma.Application.GunSupervisor},
- {{:pleroma, :hackney_pools}, Pleroma.Application.HackneySupervisor},
- {{:pleroma, :gopher}, Pleroma.Gopher.Server},
- {{:pleroma, Pleroma.Captcha, [:seconds_valid]}, Pleroma.Web.Endpoint},
- {{:pleroma, Pleroma.Upload, [:proxy_remote]}, adapter_module},
- {{:pleroma, :instance, [:upload_limit]}, Pleroma.Web.Endpoint},
- {{:pleroma, :fed_sockets, [:enabled]}, Pleroma.Web.Endpoint}
- ]
- end
-
- @spec save_need_reboot_paths([Pleroma.ConfigDB.t()]) :: :ok
- def save_need_reboot_paths([]), do: :ok
-
- def save_need_reboot_paths(configs) do
- configs
- |> Enum.map(&find_path(&1.group, &1.key, &1.value))
- |> Enum.filter(& &1)
- |> save_paths()
- end
-
- defp find_path(group, key, value) do
- with {path, _} <-
- Enum.find(config_path_mappings(), fn
- {{g, k}, _} ->
- g == group and k == key
-
- {{g, k, subkeys}, _} ->
- Keyword.keyword?(value) and g == group and k == key and
- Enum.any?(Keyword.keys(value), &(&1 in subkeys))
- end) do
- path
- end
- end
-
- defp save_paths([]), do: :ok
-
- defp save_paths(paths), do: Pleroma.Application.Agent.put_paths(paths)
-
- @spec need_reboot?() :: boolean()
- def need_reboot?, do: Pleroma.Application.Agent.paths() != []
-
- @spec restart_children() :: :ok
- def restart_children do
- Pleroma.Application.Agent.get_and_reset_paths()
- |> Enum.each(&restart_child/1)
- end
-
- defp restart_child(path) do
- path
- |> Pleroma.Application.Agent.pid()
- |> restart_child(path)
- end
-
- defp restart_child(nil, path) do
- # child wasn't started yet
- with {_, module} <- find_mapping(path) do
- start_dynamic_child(module)
- end
- end
-
- defp restart_child(pid, path) when is_pid(pid) do
- # main module can have multiple keys
- # first we search for main module
- with {_, module} <- find_mapping(path),
- :ok <- terminate(pid),
- # then we search for mappings, which depends on this main module
- mappings <- find_mappings(module) do
- Enum.each(mappings, fn {key, _} ->
- Pleroma.Application.Agent.delete_pid(key)
- end)
-
- start_dynamic_child(module)
- else
- error ->
- Logger.warn(inspect(error))
- end
- end
-
- defp find_mapping(path) do
- with nil <- Enum.find(config_path_mappings(), fn {key, _} -> key == path end) do
- {:error, :mapping_not_found}
- end
- end
-
- defp find_mappings(module) do
- with [] <- Enum.filter(config_path_mappings(), fn {_, m} -> m == module end) do
- {:error, :empty_mappings}
- end
- end
-
- defp terminate(pid) do
- with {:error, :not_found} = error <- DynamicSupervisor.terminate_child(__MODULE__, pid) do
- error
- end
- end
-end
diff --git a/lib/pleroma/application/static.ex b/lib/pleroma/application/static.ex
deleted file mode 100644
index 56667c22c..000000000
--- a/lib/pleroma/application/static.ex
+++ /dev/null
@@ -1,77 +0,0 @@
-# # Pleroma: A lightweight social networking server
-# # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# # SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Application.Static do
- require Cachex.Spec
-
- @spec start_children(Pleroma.Application.env()) :: :ok
- def start_children(env) do
- children =
- [
- Pleroma.Emoji,
- Pleroma.Stats,
- Pleroma.JobQueueMonitor,
- %{
- id: :web_push_init,
- start: {Task, :start_link, [&Pleroma.Web.Push.init/0]},
- restart: :temporary
- }
- ]
- |> add_cachex_children()
- |> add_init_internal_fetch_actor_task(env)
-
- Enum.each(children, &Pleroma.Application.DynamicSupervisor.start_child/1)
- end
-
- @spec build_cachex({String.t(), keyword()}) :: map()
- def build_cachex({type, opts}) do
- %{
- id: String.to_atom("cachex_" <> type),
- start: {Cachex, :start_link, [String.to_atom(type <> "_cache"), opts]},
- type: :worker
- }
- end
-
- defp add_cachex_children(children) do
- cachex_children =
- [
- {"used_captcha", ttl_interval: seconds_valid_interval()},
- {"user", default_ttl: 25_000, ttl_interval: 1000, limit: 2500},
- {"object", default_ttl: 25_000, ttl_interval: 1000, limit: 2500},
- {"rich_media", default_ttl: :timer.minutes(120), limit: 5000},
- {"scrubber", limit: 2500},
- {"idempotency", expiration: cachex_expiration(6 * 60 * 60, 60), limit: 2500},
- {"web_resp", limit: 2500},
- {"emoji_packs", expiration: cachex_expiration(5 * 60, 60), limit: 10},
- {"failed_proxy_url", limit: 2500},
- {"banned_urls", default_ttl: :timer.hours(24 * 30), limit: 5_000}
- ]
- |> Enum.map(&build_cachex/1)
-
- children ++ cachex_children
- end
-
- defp cachex_expiration(default, interval) do
- Cachex.Spec.expiration(default: :timer.seconds(default), interval: :timer.seconds(interval))
- end
-
- defp seconds_valid_interval do
- [Pleroma.Captcha, :seconds_valid]
- |> Pleroma.Config.get!()
- |> :timer.seconds()
- end
-
- defp add_init_internal_fetch_actor_task(children, :test), do: children
-
- defp add_init_internal_fetch_actor_task(children, _) do
- children ++
- [
- %{
- id: :internal_fetch_init,
- start: {Task, :start_link, [&Pleroma.Web.ActivityPub.InternalFetchActor.init/0]},
- restart: :temporary
- }
- ]
- end
-end
diff --git a/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex b/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex
index ea14894c7..fea22070f 100644
--- a/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex
+++ b/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex
@@ -628,14 +628,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
def restart(conn, _params) do
with :ok <- configurable_from_database() do
- Task.start(Pleroma.Application.DynamicSupervisor, :restart_children, [])
+ Task.start(Pleroma.Application.DependenciesSupervisor, :restart_children, [])
json(conn, %{})
end
end
def need_reboot(conn, _params) do
- json(conn, %{need_reboot: Pleroma.Application.DynamicSupervisor.need_reboot?()})
+ json(conn, %{need_reboot: Pleroma.Application.DependenciesSupervisor.need_reboot?()})
end
defp configurable_from_database do
diff --git a/lib/pleroma/web/admin_api/controllers/config_controller.ex b/lib/pleroma/web/admin_api/controllers/config_controller.ex
index 25086172c..f53e85fff 100644
--- a/lib/pleroma/web/admin_api/controllers/config_controller.ex
+++ b/lib/pleroma/web/admin_api/controllers/config_controller.ex
@@ -5,6 +5,7 @@
defmodule Pleroma.Web.AdminAPI.ConfigController do
use Pleroma.Web, :controller
+ alias Pleroma.Application.DependenciesSupervisor
alias Pleroma.Config
alias Pleroma.ConfigDB
alias Pleroma.Plugs.OAuthScopesPlug
@@ -34,7 +35,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigController do
render(conn, "index.json", %{
configs: configs,
- need_reboot: Pleroma.Application.DynamicSupervisor.need_reboot?()
+ need_reboot: DependenciesSupervisor.need_reboot?()
})
end
end
@@ -75,7 +76,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigController do
render(conn, "index.json", %{
configs: merged,
- need_reboot: Pleroma.Application.DynamicSupervisor.need_reboot?()
+ need_reboot: DependenciesSupervisor.need_reboot?()
})
end
end
@@ -103,11 +104,18 @@ defmodule Pleroma.Web.AdminAPI.ConfigController do
Config.Environment.load_and_update(deleted)
- Pleroma.Application.DynamicSupervisor.save_need_reboot_paths(updated ++ deleted)
+ (updated ++ deleted)
+ |> Enum.map(fn %{group: group, key: key, value: value} ->
+ with {path, _} <- Pleroma.Application.Dependencies.find_relation(group, key, value) do
+ path
+ end
+ end)
+ |> Enum.filter(& &1)
+ |> DependenciesSupervisor.put_paths()
render(conn, "index.json", %{
configs: updated,
- need_reboot: Pleroma.Application.DynamicSupervisor.need_reboot?()
+ need_reboot: DependenciesSupervisor.need_reboot?()
})
end
end
diff --git a/mix.exs b/mix.exs
index 0e48bbdea..258a14bf0 100644
--- a/mix.exs
+++ b/mix.exs
@@ -78,7 +78,7 @@ defmodule Pleroma.Mixfile do
:fast_sanitize
],
included_applications: [:ex_syslogger],
- start_phases: [update_env: [], static_children: [], dynamic_children: []]
+ start_phases: [update_env: [], static_deps: [], dynamic_deps: []]
]
end
diff --git a/test/application/dynamic_supervisor_test.exs b/test/application/dynamic_supervisor_test.exs
new file mode 100644
index 000000000..839327e28
--- /dev/null
+++ b/test/application/dynamic_supervisor_test.exs
@@ -0,0 +1,193 @@
+defmodule Pleroma.Application.DependenciesSupervisorTest do
+ use ExUnit.Case
+ use Pleroma.Tests.Helpers
+
+ import ExUnit.CaptureLog
+
+ alias Pleroma.Application.DependenciesState
+ alias Pleroma.Application.DependenciesSupervisor
+
+ defp update_relations(new_relations) do
+ relations = Pleroma.Config.get(:config_relations)
+
+ relations = Enum.reduce(new_relations, relations, fn relation, acc -> [relation | acc] end)
+
+ Pleroma.Config.put(:config_relations, relations)
+ end
+
+ setup do: clear_config(:config_relations)
+
+ test "starts and restart dynamic childs" do
+ path1 = {:pleroma, :dummy_module1}
+ path2 = {:pleroma, :dummy_module2, [:subkey]}
+ path3 = {:pleroma, :dummy_module3}
+ module1 = Pleroma.DummyModule1
+ module2 = Pleroma.DummyModule2
+
+ update_relations([{path1, module1}, {path2, module2}, {path3, module1}])
+
+ DependenciesSupervisor.start_children([module1, module2], :dynamic)
+ pid1 = Process.whereis(module1)
+ pid2 = Process.whereis(module2)
+
+ # all relations where saved in state
+ assert DependenciesState.pid(path1) == pid1
+ assert DependenciesState.pid(path2) == pid2
+ assert DependenciesState.pid(path3) == pid1
+
+ refute DependenciesSupervisor.need_reboot?()
+ # config was changed only for 2 paths
+ DependenciesSupervisor.put_paths([path1, path2])
+
+ assert DependenciesSupervisor.need_reboot?()
+ # check that only changed paths are in state
+ paths = DependenciesState.paths()
+ assert path1 in paths
+ assert path2 in paths
+ refute path3 in paths
+
+ DependenciesSupervisor.restart_children()
+
+ # check that pids for dummy modules were updated and saved correctly after restart
+ refute DependenciesState.pid(path1) == pid1
+ refute DependenciesState.pid(path2) == pid2
+ refute DependenciesState.pid(path3) == pid1
+
+ pid1 = Process.whereis(module1)
+ pid2 = Process.whereis(module2)
+
+ # check that pid for path3 was updated too
+ assert DependenciesState.pid(path1) == pid1
+ assert DependenciesState.pid(path2) == pid2
+ assert DependenciesState.pid(path3) == pid1
+
+ assert DynamicSupervisor.terminate_child(DependenciesSupervisor, pid1) ==
+ :ok
+
+ assert DynamicSupervisor.terminate_child(DependenciesSupervisor, pid2) ==
+ :ok
+
+ Enum.each([path1, path2, path3], &DependenciesState.delete_pid/1)
+ end
+
+ test "if static child is configured to be ignored" do
+ assert DependenciesSupervisor.start_children(
+ [Pleroma.DummyModule3],
+ :static
+ ) ==
+ :ok
+ end
+
+ test "dynamic child is ignored on restart" do
+ path = {:pleroma, :dummy_module3}
+ update_relations([{path, Pleroma.DummyModule3}])
+ DependenciesSupervisor.put_paths([path])
+ assert DependenciesSupervisor.restart_children() == :ok
+ assert DependenciesState.get_and_reset_paths() == []
+ end
+
+ test "error if child was already started" do
+ path = {:pleroma, :dummy_module1}
+ module = Pleroma.DummyModule1
+ update_relations([{path, module}])
+
+ capture_log(fn ->
+ assert {:error, {:already_started, _}} =
+ DependenciesSupervisor.start_children(
+ [
+ module,
+ module
+ ],
+ :dynamic
+ )
+ end) =~ "{:error, {:already_started"
+
+ pid = Process.whereis(module)
+
+ assert DynamicSupervisor.terminate_child(DependenciesSupervisor, pid) ==
+ :ok
+
+ DependenciesState.delete_pid(path)
+ DependenciesSupervisor.put_paths([])
+ end
+
+ test "restart for child, which wasn't started yet" do
+ path = {:pleroma, :dummy_module1}
+ module = Pleroma.DummyModule1
+ update_relations([{path, module}])
+
+ DependenciesSupervisor.put_paths([path])
+
+ assert DependenciesSupervisor.restart_children() == :ok
+
+ pid = Process.whereis(module)
+
+ assert DynamicSupervisor.terminate_child(DependenciesSupervisor, pid) ==
+ :ok
+
+ DependenciesState.delete_pid(path)
+ end
+
+ test "restart child, which crashed and update Agent" do
+ path = {:pleroma, :dummy_module1}
+ module = Pleroma.DummyModule1
+
+ update_relations([{path, module}])
+
+ DependenciesSupervisor.start_children([module], :dynamic)
+
+ pid = Process.whereis(module)
+
+ DependenciesSupervisor.put_paths([path])
+
+ Process.exit(pid, :kill)
+ Process.sleep(5)
+ pid_after_crash = Process.whereis(module)
+ refute pid == pid_after_crash
+ # State wasn't updated yet, so it's normal
+ assert DependenciesState.pid(path) == pid
+ assert DependenciesSupervisor.restart_children() == :ok
+
+ updated_pid = Process.whereis(module)
+ refute updated_pid == pid_after_crash
+
+ assert DependenciesState.pid(path) == updated_pid
+
+ assert DynamicSupervisor.terminate_child(
+ DependenciesSupervisor,
+ updated_pid
+ ) ==
+ :ok
+
+ DependenciesState.delete_pid(path)
+ end
+
+ test "restart by path without child" do
+ path = {:pleroma, :dummy_module1}
+ module = Pleroma.DummyModule1
+ update_relations([{path, module}])
+ assert DependenciesSupervisor.restart_children() == :ok
+ end
+end
+
+defmodule Pleroma.DummyModule1 do
+ use Agent
+
+ def start_link(_) do
+ Agent.start_link(fn -> nil end, name: __MODULE__)
+ end
+end
+
+defmodule Pleroma.DummyModule2 do
+ use Agent
+
+ def start_link(_) do
+ Agent.start_link(fn -> nil end, name: __MODULE__)
+ end
+end
+
+defmodule Pleroma.DummyModule3 do
+ use Agent
+
+ def start_link(_), do: :ignore
+end
diff --git a/test/web/admin_api/controllers/admin_api_controller_test.exs b/test/web/admin_api/controllers/admin_api_controller_test.exs
index 229b262b3..b9a012e8a 100644
--- a/test/web/admin_api/controllers/admin_api_controller_test.exs
+++ b/test/web/admin_api/controllers/admin_api_controller_test.exs
@@ -1427,7 +1427,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
test "pleroma restarts", %{conn: conn} do
assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
- refute Pleroma.Application.DynamicSupervisor.need_reboot?()
+ refute Pleroma.Application.DependenciesSupervisor.need_reboot?()
end
end