From f9eeecf91415040762ed874a5708e42e0a0a73b8 Mon Sep 17 00:00:00 2001 From: v0idpwn Date: Mon, 22 Apr 2024 21:03:06 -0300 Subject: [PATCH 1/3] Propagate parent process logger level to preloader Closes https://github.com/elixir-ecto/ecto_sql/issues/602 --- lib/ecto/repo/preloader.ex | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/ecto/repo/preloader.ex b/lib/ecto/repo/preloader.ex index 23d3902966..2df9b28eaf 100644 --- a/lib/ecto/repo/preloader.ex +++ b/lib/ecto/repo/preloader.ex @@ -136,9 +136,11 @@ defmodule Ecto.Repo.Preloader do # still necessary. opts = Keyword.put_new(opts, :caller, self()) on_preloader_spawn = Keyword.get(opts, :on_preloader_spawn, fn -> :ok end) + parent_logger_level = Logger.get_process_level(self()) preloaders |> Task.async_stream(fn preloader -> + Logger.put_process_level(self(), parent_logger_level) on_preloader_spawn.() preloader.({adapter_meta, opts}) end, timeout: :infinity) From 3e7d9bb73e47aa85fda93f51637a34c5301c49cf Mon Sep 17 00:00:00 2001 From: v0idpwn Date: Tue, 23 Apr 2024 00:09:03 -0300 Subject: [PATCH 2/3] Alternative implementation for Elixir pre-1.15.0 --- lib/ecto/repo/preloader.ex | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/lib/ecto/repo/preloader.ex b/lib/ecto/repo/preloader.ex index 2df9b28eaf..2b7a780780 100644 --- a/lib/ecto/repo/preloader.ex +++ b/lib/ecto/repo/preloader.ex @@ -129,15 +129,29 @@ defmodule Ecto.Repo.Preloader do defp maybe_pmap(preloaders, _repo_name, {adapter_meta, opts}) do if match?([_, _ | _] , preloaders) and not adapter_meta.adapter.checked_out?(adapter_meta) and Keyword.get(opts, :in_parallel, true) do + pmap(preloaders, {adapter_meta, opts}) + else + Enum.map(preloaders, &(&1.({adapter_meta, opts}))) + end + end + + # TODO: once we require Elixir >= v1.15.0, we can remove the alternative + # function implementations + if Version.match?(System.version(), ">= 1.15.0") do + defp pmap(preloaders, {adapter_meta, opts}) do # We pass caller: self() so the ownership pool knows where # to fetch the connection from and set the proper timeouts. # Note while the ownership pool uses '$callers' from pdict, # it does not do so in automatic mode, hence this line is # still necessary. opts = Keyword.put_new(opts, :caller, self()) - on_preloader_spawn = Keyword.get(opts, :on_preloader_spawn, fn -> :ok end) + + # Additionally, we propagate the caller's logger level to + # the spawned tasks parent_logger_level = Logger.get_process_level(self()) + on_preloader_spawn = Keyword.get(opts, :on_preloader_spawn, fn -> :ok end) + preloaders |> Task.async_stream(fn preloader -> Logger.put_process_level(self(), parent_logger_level) @@ -148,9 +162,23 @@ defmodule Ecto.Repo.Preloader do {:ok, assoc} -> assoc {:exit, reason} -> exit(reason) end) - else - Enum.map(preloaders, &(&1.({adapter_meta, opts}))) end + else + defp pmap(preloaders, {adapter_meta, opts}) do + opts = Keyword.put_new(opts, :caller, self()) + on_preloader_spawn = Keyword.get(opts, :on_preloader_spawn, fn -> :ok end) + + preloaders + |> Task.async_stream(fn preloader -> + on_preloader_spawn.() + preloader.({adapter_meta, opts}) + end, timeout: :infinity) + |> Enum.map(fn + {:ok, assoc} -> assoc + {:exit, reason} -> exit(reason) + end) + end + end # Then we unpack the query results, merge them, and preload recursively From 39237937b46d34cbf8ab357581086b277b3c704e Mon Sep 17 00:00:00 2001 From: v0idpwn Date: Tue, 23 Apr 2024 00:16:46 -0300 Subject: [PATCH 3/3] Add note regarding alterantive pmap versions --- lib/ecto/repo/preloader.ex | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/ecto/repo/preloader.ex b/lib/ecto/repo/preloader.ex index 2b7a780780..e1f6698418 100644 --- a/lib/ecto/repo/preloader.ex +++ b/lib/ecto/repo/preloader.ex @@ -135,8 +135,14 @@ defmodule Ecto.Repo.Preloader do end end - # TODO: once we require Elixir >= v1.15.0, we can remove the alternative - # function implementations + # TODO: once we require Elixir >= v1.15.0, we can remove the + # alternative function implementations. + # + # Elixir v1.15.0 included the `Logger.get_process_level/2` and + # `Logger.put_process_level` functions, which are used here + # to guarantee that the preloader processes inherit the parent's + # logger level. + # if Version.match?(System.version(), ">= 1.15.0") do defp pmap(preloaders, {adapter_meta, opts}) do # We pass caller: self() so the ownership pool knows where @@ -178,7 +184,6 @@ defmodule Ecto.Repo.Preloader do {:exit, reason} -> exit(reason) end) end - end # Then we unpack the query results, merge them, and preload recursively