Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Protocols no longer consolidated after CodeReloader executes #5831

Open
sb8244 opened this issue Jun 3, 2024 · 2 comments
Open

Protocols no longer consolidated after CodeReloader executes #5831

sb8244 opened this issue Jun 3, 2024 · 2 comments

Comments

@sb8244
Copy link
Contributor

sb8244 commented Jun 3, 2024

Environment

  • Elixir version (elixir -v):
Erlang/OTP 26 [erts-14.1.1] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit]

Elixir 1.15.7 (compiled with Erlang/OTP 26)
  • Phoenix version (mix deps): 1.7.10
  • Operating system: Mac OSX

Actual behavior

I utilize SomeProtocol.__protocol__(:impls) to retrieve the implementations of a protocol in my code base. This is to provide an automatic mapping of protocols for certain features. It works great, except when the CodeReloader runs.

When the CodeReloader runs, the protocols in my app become unconsolidated and I have to restart the dev server.

To trigger the CodeReloader, I make an update to an Elixir file and then refresh my app frontend.

I know there's a mix.exs setting regarding consolidation, but it is not present in my mix.exs file.

Expected behavior

Reloading the code should not result in protocols not being consolidated.

Workaround

I have been struggling with a workaround for this but finally decided to figure something out for it. I ended up with the following (very hacky) solution that does seem to work.

Effectively, I'm running the same code as the CodeReloader to consolidate protocols, but I have it in force mode so it doesn't noop.

defmodule Super.Util.Protocol do
  require Logger

  def impls_for(protocol_mod, opts \\ []) do
    case protocol_mod.__protocol__(:impls) do
      {:consolidated, impls} ->
        impls

      :not_consolidated ->
        if Keyword.get(opts, :raise) do
          raise "Protocols not consolidated. This can happen in dev (restart server). In production, this is not expected."
        else
          recompile_protocols(protocol_mod)
          impls_for(protocol_mod, Keyword.put(opts, :raise, true))
        end
    end
  end

  if Mix.env() == :dev do
    defp recompile_protocols(mod) do
      Logger.info("Recompiling protocols due to missing consolidation: #{mod}")
      Mix.Task.reenable("compile.protocols")
      Mix.Task.run("compile.protocols", ["--force"])
    end
  else
    defp recompile_protocols(_mod) do
      :ok
    end
  end
end
@josevalim
Copy link
Member

Hi @sb8244! I cannot reproduce this issue. Here is what I did:

  1. Cloned latest Phoenix
  2. cd installer
  3. mix new demo --dev
  4. Added the following to lib/demo.ex:
defprotocol Foo do
  def omg(foo)
end

Now I started the server and whenever I change a file, I reload the page, triggering the code reloader. Both Foo and Enumerable are still consolidated.

Besides using latest Phoenix (main), I am also using Elixir v1.17.0.

@sb8244
Copy link
Contributor Author

sb8244 commented Jun 17, 2024

@josevalim I will see if I can reproduce it on a clean repo. I don't think we're doing anything crazy (other than inspecting the protocol), but it's also a decently complex repo so there's a chance something is throwing it off.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants