vrod

vrod

What cannot be runtime config?

Forgive me for maybe a noob question. I am reading much about configuration – maybe this in Elixir is one of the most confusing things? I read this Configuring your Elixir Application at Runtime with Vapor | AppSignal Blog and it says “if it can be set at runtime, it should be set at runtime.” This makes much sense to me – this is how other languages work with .env files – but maybe the comparison is not fair with dynamic languages?

My question is what things can not be runtime config? I imagine a very simple setup from working with PHP where there was only 1 config and 2 .env files (regular .env and a .test.env) and the test suite would know to use the .test.env. But dynamic languages do not have the same concerns. I am thinking maybe a config for a module name like saying which HTTP client the app should use. A system ENV maybe has string for “HTTPoison”, but you must convert this to atom like Elixir.HTTPoison. It is ugly, but I think this works fine. What things will not work this way and must be set in a config file for compile time config?

Thank you for your replies!

Marked As Solved

Nicd

Nicd

This suffers from being long and containing ifs, but I’m posting it anyway for reference. I went 100% on runtime.exs in my project and only have a couple of things that need to be set at compile time in the config.exs.

This has worked well for me. Even though the runtime config file is big, it’s not really that big once you get to know it, and I can have unified config for both development and release time (just using .env in development for convenience).

Also Liked

hauleth

hauleth

There are basic 2 types of config in Elixir code:

  1. Compile time configuration. Before Elixir 1.10 you could call Application.get_env/{2,3} in any part of the code, even compile time, so people often did stuff like:
    defmodule Foo do
      @option Application.get_env(:my_app, :foo_config)
    
      # …
    end
    
    This caused the @option to be set during compilation, so if someone was using releases (Distillery or Relx at the time) could be confused why @option didn’t changed when the configuration was changed on the target server. Since 1.10 we have Application.compile_env/{2,3} and Application.get_env/{2,3} will print warning when used in compile time. There are few examples of applications that use compile time env and in few situations this still sometimes makes some sense (for example poor-man dependency injection).
  2. Runtime environment, that is Application.get_env/{2,3} calls within functions body, that is it. The “funny” thing there is that runtime configuration is also divided in 2 separate things (and people often forgot about it):
    1. “Startup config” that is read only at the beginning of application lifetime (often in module that is implementing Application behaviour, or in init/1 functions of the servers). This is for example reason why simple Application.put_env(:logger, :level, :warning) will not work, and you need to use Logger.configure(level: :warning) or reason why you cannot configure :kernel and :stdlib applications via config/config.exs (so for example configuration of Erlang’s logger is currently enormous PITA in Elixir).
    2. “On-demand” configuration where Application.get_env/{2,3} is called “in-place”, where it is needed. Reason why a lot components cannot be configured that way are mainly 2:
      • Performance - reading environment is much slower than just variable from the process state
      • Data is needed at the start of the process - for example you cannot change port on which Cowboy is listening for connections after it was started (obviously)

So 1. is obviously something that you cannot configure in runtime (examples of such behaviour is :mime database). And depending when your Vapor configuration will be ran 1.1. can also be off limits, at least for some applications (notably :kernel and :stdlib are hard to configure with any Elixir tooling).

dch

dch

The Application.env is 100% ets:

iex(serenity@wintermute.skunkwerks.at)9> :ets.tab2list :ac_tab
[
  {{:application_master, :credentials_obfuscation}, #PID<0.367.0>},
  {{:env, :gettext, :default_locale}, "en"},
  {{:env, :kernel, :logger},
   [
     {:handler, :default, :logger_std_h,
      %{
        config: %{type: :standard_io},
        formatter: {:logger_formatter,
         %{legacy_header: true, single_line: false}}
      }}
   ]},
  {{:application_master, :cowboy}, #PID<0.415.0>},
  {{:env, :amqp_client, :writer_gc_threshold}, 1000000000},
  {{:env, :lager, :crash_log_count}, 5},
  {{:env, :ex_unit, :timeout}, 60000},
  {{:application_master, :goldrush}, #PID<0.326.0>},
  {{:loaded, :logger},
...

You can look in the observer app, if you select view -> ETS tables -> system tables too.

For better (read-only) performance, look at persistent_term — OTP 29.0.2 (erts 17.0.2) the persistent term cache, added in OTP21.2. Note that updating this is a java-esque stop-the-world scenario for the entire VM.

lud

lud

The first thing I can think off are modules names that macros will be used from, or will be modules generated by macros.

Basically, anything that changes the compilation result.

Last Post!

dch

dch

The Application.env is 100% ets:

iex(serenity@wintermute.skunkwerks.at)9> :ets.tab2list :ac_tab
[
  {{:application_master, :credentials_obfuscation}, #PID<0.367.0>},
  {{:env, :gettext, :default_locale}, "en"},
  {{:env, :kernel, :logger},
   [
     {:handler, :default, :logger_std_h,
      %{
        config: %{type: :standard_io},
        formatter: {:logger_formatter,
         %{legacy_header: true, single_line: false}}
      }}
   ]},
  {{:application_master, :cowboy}, #PID<0.415.0>},
  {{:env, :amqp_client, :writer_gc_threshold}, 1000000000},
  {{:env, :lager, :crash_log_count}, 5},
  {{:env, :ex_unit, :timeout}, 60000},
  {{:application_master, :goldrush}, #PID<0.326.0>},
  {{:loaded, :logger},
...

You can look in the observer app, if you select view -> ETS tables -> system tables too.

For better (read-only) performance, look at persistent_term — OTP 29.0.2 (erts 17.0.2) the persistent term cache, added in OTP21.2. Note that updating this is a java-esque stop-the-world scenario for the entire VM.

Where Next?

Popular in Questions Top

baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
marius95
Hello everyone, I try to use an Javascript Event Handler in my root.html.leex file. Therefore I created a function in the app.js file: ...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New

Other popular topics Top

openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
shijith.k
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New

We're in Beta

About us Mission Statement