rogach

rogach

How to: Tweak mix release to work with hot code reloading

I’ve spent some time understanding how to do hot code reloading with releases built using mix release, and here I’d like to detail the steps needed, in hopes that it will help someone.

So, in order to make hot-reloadable release you need:

  1. Write an appup file (the Erlang’s Appup cookbook is a great resource for that: Appup Cookbook — Erlang System Documentation v29.0.2).
  2. Tweak the release generated by mix release, by copying the .rel & .appup files into expected places and generating the relup file. This can be done by hand, but I’ve wrote a simple function that can do that right inside the mix release task:
def project do
  [
    releases: [
      app_name: [
        include_executables_for: [:unix],
        steps: [:assemble, &release_fixup_step/1, :tar],
      ],
    ],
    ...
  ]
end

defp release_fixup_step(release) do
  releases_dir = Path.join(release.path, "releases")
  rel_file = Path.join([releases_dir, release.version, "#{release.name}.rel"])
  :ok = :release_handler.create_RELEASES(releases_dir, rel_file, [])

  # Yes, we have three copies of a same file in the same place.
  # This is necessary to appease the release_handler.
  File.cp!(rel_file, Path.join([releases_dir, release.version, "#{release.name}-#{release.version}.rel"]))
  File.cp!(rel_file, Path.join([releases_dir, "#{release.name}-#{release.version}.rel"]))

  # Copy appup file into the correct location and generate the relup.
  appup_file = Path.join("appups", "#{release.version}.appup")
  if File.exists?(Path.join("appups", "#{release.version}.appup")) do
    File.cp!(
      appup_file,
      Path.join([release.path, "lib", "#{release.name}-#{release.version}", "ebin", "#{release.name}.appup"])
    )

    {:ok, appup} = :file.consult(appup_file)
    [{appup_release_version, [{version_up_from, _}], [{version_down_to, _}]}] = appup

    if appup_release_version != to_charlist(release.version) do
      raise "Unexpected version in appup: #{appup_release_version} (expected #{release.version})"
    end

    if version_up_from != version_down_to do
      raise "Unexpected versions in appup instructions: #{version_up_from} != #{version_down_to}"
    end

    :systools.make_relup(
      ~c"#{release.name}-#{release.version}",
      [~c"#{release.name}-#{version_up_from}"],
      [~c"#{release.name}-#{version_down_to}"],
      path: [to_charlist(Path.join(release.path, "releases/*")), to_charlist(Path.join(release.path, "lib/*/ebin"))],
      outdir: to_charlist(release.version_path)
    )
  end

  release
end

Now, after running mix release you will get a release archive in _build/<env>/app_name-<version>.tar.gz.

  1. Copy this archive into the releases/ directory of your running release.
  2. Connect to the Iex shell of the running release.
  3. Carefully run the commands:
:release_handler.unpack_release(~c"app_name-<version>")
:release_handler.install_release(~c"<version>")
:release_handler.make_permanent(~c"<version>")

Where Next?

Trending in Guides/Tuts Top

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
ausimian
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New
type1fool
I just stumbled on a newly redesigned elixir-lang.org. :tada: It looks like @Software_Mansion did the work, and I think it is generally a...
New
akoutmos
@hugobarauna and I (Alex Koutmos) have been hard at work on writing a book on Nerves that takes you from simply blinking LEDs to building...
New

We're in Beta

About us Mission Statement