Elixir v1.15.0 released

We have a pretty standard Phoenix app here which uses a bin/migrate file (basically exec ./myapp eval Myapp.Release.migrate) to run migrations before releasing a new version.

For some reason I am getting this error when starting the migration (after the update to Elixir 1.15.0):

ERROR! Config provider Config.Reader failed with:
** (ArgumentError) could not fetch application environment :always_evaluate_messages for application :logger because the application was not loaded nor configured
    (elixir 1.15.0) lib/application.ex:775: Application.fetch_env!/2
    lib/logger.ex:1012: Logger.macro_log/4
    expanding macro: Logger.warning/1
    /app/releases/0.1.0/runtime.exs:212: (file)
    (elixir 1.15.0) expanding macro: Kernel.if/2
    /app/releases/0.1.0/runtime.exs:211: (file)
    (elixir 1.15.0) expanding macro: Kernel.if/2
    /app/releases/0.1.0/runtime.exs:48: (file)

{"init terminating in do_boot",{#{'__exception__'=>true,'__struct__'=>'Elixir.ArgumentError',message=><<"could not fetch application environment :always_evaluate_messages for application :logger because the application was not loaded nor configured">>},[{'Elixir.Application','fetch_env!',2,[{file,"lib/application.ex"},{line,775}]},{'Elixir.Logger',macro_log,4,[{file,"lib/logger.ex"},{line,1012}]},{'Elixir.Logger',warning,1,[{file,"expanding macro"}]},{elixir_compiler_0,'__FILE__',1,[{file,"/app/releases/0.1.0/runtime.exs"},{line,212}]},{'Elixir.Kernel',if,2,[{file,"expanding macro"}]},{elixir_compiler_0,'__FILE__',1,[{file,"/app/releases/0.1.0/runtime.exs"},{line,211}]},{'Elixir.Kernel',if,2,[{file,"expanding macro"}]},{elixir_compiler_0,'__FILE__',1,[{file,"/app/releases/0.1.0/runtime.exs"},{line,48}]}]}}
init terminating in do_boot ({,[{Elixir.Application,fetch_env!,2,[{_},{_}]},{Elixir.Logger,macro_log,4,[{_},{_}]},{Elixir.Logger,warning,1,[{_}]},{elixir_compiler_0,__FILE__,1,[{_},{_}]},{Elixir.Kernel,if,2,[{_}]},{elixir_compiler_0,__FILE__,1,[{_},{_}]},{Elixir.Kernel,if,2,[{_}]},{elixir_compiler_0,__FILE__,1,[{_},{_}]}]})

The Myapp.Release module looks like this:

defmodule Myapp.Release do
  @moduledoc """
  Used for executing DB release tasks when run in production without Mix
  installed.
  """
  @app :myapp

  require Logger

  def migrate do
    load_app()

    for repo <- repos() do
      # Ensure the repo is started. Then, if the "schema_migrations"
      # table does not exist, run the `structure.sql` file for the repo.
      {:ok, _, _} = Ecto.Migrator.with_repo(repo, &check_and_execute_structure_sql(&1))

      {:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :up, all: true))
    end
  end

  def rollback(repo, version) do
    load_app()
    {:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :down, to: version))
  end

  defp check_and_execute_structure_sql(repo) do
    config = repo.config()
    app_name = Keyword.fetch!(config, :otp_app)

    if Ecto.Adapters.SQL.table_exists?(repo, "schema_migrations") do
      Logger.info("schema_migrations table already exists")
      :ok
    else
      case repo.__adapter__().structure_load(
             Application.app_dir(app_name, "priv/repo"),
             repo.config()
           ) do
        {:ok, location} = success ->
          Logger.info("The structure for #{inspect(repo)} has been loaded from #{location}")
          success

        {:error, term} when is_binary(term) ->
          Logger.error("The structure for #{inspect(repo)} couldn't be loaded: #{term}")
          {:error, inspect(term)}

        {:error, term} ->
          Logger.error("The structure for #{inspect(repo)} couldn't be loaded: #{inspect(term)}")
          {:error, inspect(term)}
      end
    end
  end

  defp repos do
    Application.fetch_env!(@app, :ecto_repos)
  end

  defp load_app do
    Application.load(@app)
  end
end

I seem to understand what the error is, but I have no clue what to do about it. Can somebody give me a nudge in the right direction?

Fixed in Elixir main and v1.15 branches.

2 Likes

Nice, thank you :pray:

Thanks. Youā€™re quick! :slight_smile:

So on the switch to R26 and 1.15.0 is faster for the most part, with the exception of unit tests. These are orders of magnitude slower to compile. One test in particular.

As a test I compiled using:

iex(cs@localhost)7>  Kernel.ParallelCompiler.compile(["test/api/org_test.exs"], profile: :time)

Results for R25/1.14.2

[profile]  11588ms compiling +      0ms waiting for test/api/org_test.exs
[profile] Finished compilation cycle of 1 modules in 11589ms
[profile] Finished group pass check of 1 modules in 82ms

Results for R25/1.15.0

[profile] 213876ms compiling +      0ms waiting while compiling test/api/org_test.exs
[profile] Finished compilation cycle of 1 modules in 213876ms
[profile] Finished group pass check of 1 modules in 88ms

This is on a Mac (x86).

The test does a lot of creation of and matching of maps.

Continuing to debug.

So it appears to be a single test case thatā€™s about 1500 lines long causing issues. Without that test:

[profile]  18699ms compiling +      0ms waiting while compiling test/api/org_test.exs
[profile] Finished compilation cycle of 1 modules in 18700ms
[profile] Finished group pass check of 1 modules in 33ms

I have done a bug report to the OTP team about this. I am on my phone but you can check my submissions on the issues tracker. :slight_smile:

Btw, if you can isolate that single test and share it, we can submit it upstream too as another example of the slow down.

Will doā€¦thanks

Iā€™m investigating a bug related to gettext: Running `mix gettext.extract` doesn't extract new messages with Elixir 1.15 Ā· Issue #367 Ā· elixir-gettext/gettext Ā· GitHub

It seems that the mix gettext.extract task doesnā€™t trigger a forced compilation anymore. It depends on a ā€œtrickā€ with making the compiler manifest file old (settings its change-date to the year 2000), and then running the ā€œcompileā€ or ā€œcompile.elixirā€ mix tasks.

When I try to emulate these steps, I see different behaviour in Elixir 1.14 vs 1.15. In both cases I first make the manifest file old (e.g. run touch -a -m -t 200001010000.00 _build/dev/lib/my_app/.mix/compile.elixir). The manifest file is located in _build/dev/lib/my_app/.mix/compile.elixir. And then I trigger a compilation with mix compile. With Elixir 1.14 a recompilation is triggered (I see the message Compiling 4 files (.ex)). While with Elixir 1.15 the same steps donā€™t trigger a compilation (no message is printed).

If this to be expected to work like this, Iā€™d like to understand better how this changed from Elixir 1.14 to 1.15. Depending on the answer, the way to fix the aforementioned bug in gettext will be different.

Follow up: Iā€™m suspecting this has something to do with this change: Store timestamps in manifests Ā· elixir-lang/elixir@ce67dd6 Ā· GitHub
I guess manipulating the last-modified date of the manifest file alone isnā€™t enough anymore, as there are now timestamps being stored in the manifest file itself.

Hi @linusdm! You can write a random number to the manifest instead. A mixture of System.os_time and :crypto.strong_rand_bytes should do the trick.

1 Like

Elixir v1.15.1 has been released with fixes for the regressions reported here. Announcement: Elixir v1.15.1 released

2 Likes

Hereā€™s the GH issue tracking problems with dialyzer in umbrella apps with Elixir v1.15 OTP 26.

How have people solved conditional compilation based on the parent applicationā€™s dependencies?

Hereā€™s a good example (original post)

prune_code_paths: false fixes the issue, but you lose the benefit of the compilation stuff.

The previous code should still work, no changes necessary. There was a bug in Elixir, but it has been addressed in later Elixir v1.15.x versions. It is definitely fixed on latest, otherwise please let me know how to reproduce it!

I donā€™t have a full reproducible case yet, but Iā€™m definitely seeing weird behavior here on 1.15.4:

  1. Update mix.exs dependency entry to use a local fork path: "~/the/fork"
  2. Run mix deps.get
  3. Code.ensure_loaded? is not properly reporting true on the forked extension (feat: add geo.point support by iloveitaly Ā· Pull Request #24 Ā· fuelen/ecto_dev_logger Ā· GitHub)
  4. rm -rf _build deps && mix deps.get && mix compile --force fixes the issue

Iā€™m not sure if this is the exact steps, but itā€™s pretty close! Iā€™ll try to get better instructions if I run into it again. Thanks, Jose!

1 Like