Elixir v1.0.4 ships with two new important options for new projects. If you generate a new application with mix new
, you will see in your mix.exs
:
[build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod]
Although those options were originally meant to be in Elixir v1.1, we have decided to bring them into v1.0.4 and do a new release. In this post, we will explain why.
Protocol consolidation
One of Elixir’s most important features are protocols. Protocols allow developers to write code that accept any data type, dispatching to the appropriate implementation of the protocol at runtime. For example:
defprotocol JSON do
def encode(data)
end
defimpl JSON, for: List do
def encode(list) do
# ...
end
end
We have written about protocols before and I recently explored on my Erlang Factory talk the foundation protocols have allowed us to build.
However, in order to play nicely with the dynamic nature of the Erlang VM where modules can be loaded at any time by the VM, as well as any protocol implementation, protocols need to check on every dispatch if a new implementation is available for any given data type.
While we would gladly pay this price in development as it gives developers flexibility, we would like to avoid such in production as deployments gives us a consolidated view of all modules in the system allowing us to skip those runtime checks. For this reason, Elixir provides a feature called protocol consolidation, that consolidates all protocols with their implementations, giving protocols a fast dispatch to use in production.
Prior to Elixir v1.0.4, protocol consolidation had to be manually invoked by calling mix compile.protocols
, which would consolidate protocols into a predefined directory, and this directory had to be explicitly added to your load path when starting your project. Due to the manual nature of such commands, a lot of developers ended-up not running them in production, or were often confused when doing so.
For this reason, Elixir v1.0.4 introduces a :consolidate_protocols
option to your projects which will take care of consolidating and loading all protocols before your application starts. This option is also set to true when :build_embedded
is true.
Build embedded
When compiling your projects, Elixir will place all compiled artifacts into the _build
directory:
_build/
dev/
lib/
your_app/
ebin/
priv/
dep1/
dep2/
test/
prod/
Many of those applications and dependencies have artifacts in their source that are required during runtime. Such artifacts are placed in the priv
directory in Elixir applications. By default, Elixir will symlink to their source directories during development.
In production, though, we could copy those contents to avoid symlink traversal, embedding all relevant files to run your application into the _build
directory, without a need for their sources.
That’s what the :build_embedded
option does and it defaults to true in production for new applications.
Start permanent
Elixir code is packaged into applications. For example, each entry we saw under _build/dev/lib
above is a different application. When an application is started, it can be started in one of the three following modes:
-
permanent
– if app terminates, all other applications and the entire node are also terminated -
transient
– if app terminates with :normal reason, it is reported but no other applications are terminated. If a transient application terminates abnormally, all other applications and the entire node are also terminated -
temporary
– if app terminates, it is reported but no other applications are terminated
The default mode is temporary, which again, makes sense for development. For example, our test library called ExUnit, is also an application. If the application being tested crashes, we still want the ExUnit application to continue running in order to finish all tests and generate the proper reports. In this case, you definitely do not want your application to run as permanent.
However, in production, once your application crashes permanently, beyond recovery, we want the whole node to terminate, otherwise whatever you have monitoring your application at the operating system level won’t notice any change.
The :start_permanent
option starts your application as :permanent
and it defaults to true in production for new applications.
Summing up
Those new options have been introduced into Elixir v1.0.4 because they are very important for running Elixir in production. They bring more performance and stability to your Elixir-based systems.
There are other smaller changes in this release, like support for Erlang 17.5 and 18.0-rc1, as well as bug fixes. Check the release notes for more information and enjoy!
Shouldn’t that be?:
defprotocol JSON do
def encode(data)
end
*defimpl* JSON, for: List do
def encode(list) do
# …
end
end
Great catch. It has been fixed, thank you!