Internally: it means that Ecto will internally expect configurations to be set at runtime rather than compile time.
Externally: it means that Ecto will provide a proper API to do runtime configuration. The only way to do runtime configuration in Ecto 2.0 was by using url: {:system, "DATABASE_URL"} which has two big problems: it only works for the :url parameter and it only allows the configuration to be read from the system environment. If you need to read it from elsewhere, you are out of luck.
Ecto 2.1 now provides the init/2 callback inside your repo for runtime configuration:
def init(_, config) do
{:ok, Keyword.put(config, :url, System.get_env("DATABASE_URL"))}
end
Now you have the flexibility to do whatever you want. Also note Ecto allows the configuration to be given at the moment you call start_link on the Repo.
Hi, I have a few questions on the direction that’s been mentioned above.
The two problems I’ve found so far with {:system, "MY_VAR"} are that:
it’s not standard and requires custom code (that people are extracting in libraries)
Reading with System.get_env("MY_VAR") is slower than reading from the Application env (but benchmarks show that caching it in the wrapper speeds thing up).
Both problems could be solved by adding a layer between the Elixir API and the underlying Erlang calls.
So I guess that a different way to frame the problem is that we have a centralized configuration API that does not currently support runtime configuration, which is a common requirement, as opposed to saying that “runtime configuration should be moved to runtime”.
I can see how this would work when starting supervisors and processes. If a dependency requires configuration (e.g. API tokens, URLs, flags, etc), they can be read from the ENV and passed to the supervision tree, then something will either store the config in a GenServer’s state or a ETS table, depending on how frequently it needs to be accessed. (please correct me if I’m wrong)
I am not sure how it would work for libraries that don’t start processes though.
init runtime configuration only works for supervised dependencies, it will not work for things like:
plug Plug.Session,
signing_salt: "how to manage this setting"
Maybe applications could accept a module instead of values (or both) ?
plug Plug.Session,
handler: MyApp.MyConfigHandler
defmodule MyApp.MyConfigHandler do
# use MyApp.CompileTimeSettings
def setting({:plug, :session, :cookie, :signing_salt}) do
# - "very secret signing salt"
# - System.get_env("COOKIE_SIGNING_SALT")
# - MyExternalServiceAPI.get(:signing_salt)
# - or whatever
end
def setting(tuple) do
IO.inspect tuple, label: "Show me available settings"
nil
end
end
I would be very happy to be able to configure my dependencies this way.