Problem with deploying release with edeliver and exrm

Hey, I have really weird situation.
Dependency named exredis is not added to release build lib directory.
I can see there a list of dependencies that targets to production environment, so I don’t see for example credo - it’s ok, but I also don’t see exredis.
Here is my mix.exs:

defmodule MyApp.Mixfile do
  use Mix.Project

  def application do
    [
      applications: [
        :cowboy, :edeliver, :exrm, :gettext, :logger, :phoenix_ecto,
        :phoenix_html, :phoenix_pubsub, :phoenix, :postgrex, :stripity_stripe,
      ],
      mod: {MyApp, []},
    ]
  end

  def project do
    [
      app: :my_app,
      aliases: aliases(),
      build_embedded: Mix.env == :prod,
      compilers: [:phoenix, :gettext] ++ Mix.compilers,
      deps: deps(),
      elixir: "~> 1.2",
      elixirc_paths: elixirc_paths(Mix.env),
      start_permanent: Mix.env == :prod,
      version: "0.0.4",
   ]
  end

  defp aliases do
    [
      "app.reset": ["deps.clean --all", "deps.get", "deps.compile", "ecto.reset"],
      "app.setup": ["deps.get", "deps.compile", "ecto.setup"],
      "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
      "ecto.reset": ["ecto.drop", "ecto.setup"],
      "test": ["ecto.create --quiet", "ecto.migrate", "test"]
    ]
  end

  defp deps do
    [
      {:cowboy, "~> 1.0"},
      {:credo, "~> 0.6", only: [:dev, :test]},
      {:edeliver, "~> 1.4.0"},
      {:exredis, ">= 0.2.4"},
      {:exrm, "~> 1.0.3"},
      {:gettext, "~> 0.11"},
      {:phoenix_ecto, "~> 3.0"},
      {:phoenix_html, "~> 2.6"},
      {:phoenix_live_reload, "~> 1.0", only: :dev},
      {:phoenix_pubsub, "~> 1.0"},
      {:phoenix, "~> 1.2.1"},
      {:postgrex, ">= 0.0.0"},
      {:stripity_stripe, "~> 1.4.0"},
    ]
  end

  defp elixirc_paths(:test), do: ["lib", "web", "test/support"]
  defp elixirc_paths(_), do: ["lib", "web"]
end

Here is my .deliver/config:

# 1. Give a name to your app

APP="my_app"

# 2. Declare the names of your servers and assign the public DNS

INSTANCE_MAIN="ec2-*-*-*-*.*****.compute.amazonaws.com"
INSTANCE_FIRST="ec2-*-*-*-*.*****.compute.amazonaws.com"
INSTANCE_SECOND="ec2-*-*-*-*.*****.compute.amazonaws.com"

# 3. Specify a user

USER="ec2-user"

# 4. Which host do you want to build the release on?

BUILD_HOST=$INSTANCE_MAIN
BUILD_USER=$USER
BUILD_AT="/tmp/edeliver/$APP/builds"

# 5. Optionally specify the staging host

# STAGING_HOSTS=$SG
# STAGING_USER=$USER
# DELIVER_TO="/home/ubuntu"

#6. Specify which host(s) the app is going to be deployed to

PRODUCTION_HOSTS="$INSTANCE_MAIN $INSTANCE_FIRST $INSTANCE_SECOND"
PRODUCTION_USER=$USER
DELIVER_TO="/home/ec2-user"

#7. Point to the vm.args file

LINK_VM_ARGS="/home/$USER/vm.args"

#8. This is for Phoenix projects

# For *Phoenix* projects, symlink prod.secret.exs to our tmp source
pre_erlang_get_and_update_deps() {
  local _prod_secret_path="/home/$USER/prod.secret.exs"
  if [ "$TARGET_MIX_ENV" = "prod" ]; then
    __sync_remote "
      ln -sfn '$_prod_secret_path' '$BUILD_AT/config/prod.secret.exs'
      cd '$BUILD_AT'
      APP='$APP' MIX_ENV='$TARGET_MIX_ENV' $MIX_CMD deps.clean --all
      APP='$APP' MIX_ENV='$TARGET_MIX_ENV' $MIX_CMD deps.get
      APP='$APP' MIX_ENV='$TARGET_MIX_ENV' $MIX_CMD compile
      APP='$APP' MIX_ENV='$TARGET_MIX_ENV' $MIX_CMD ecto.migrate
    "
  fi
}

Previous builds works fine. Just added one dependency, used it in code and pushed to git repository. All others dependencies for prod environment are already included in lib directory in release archive.

On last step of deploy I got error that node is not responding for pings. It took me some time to search for solution and I read about run app manually with console argument and here is output of it:

Using /home/ec2-user/my_app/releases/0.0.4/my_app.sh
Exec: /home/ec2-user/my_app/erts-8.2.2/bin/erlexec -boot /home/ec2-user/my_app/releases/0.0.4/my_app -boot_var ERTS_LIB_DIR /home/ec2-user/my_app/erts-8.2.2/../lib -env ERL_LIBS /home/ec2-user/my_app/lib -config /home/ec2-user/my_app/running-config/sys.config -pa /home/ec2-user/my_app/lib/consolidated -args_file /home/ec2-user/my_app/running-config/vm.args -user Elixir.IEx.CLI -extra --no-halt +iex -- console
Root: /home/ec2-user/my_app
/home/ec2-user/my_app
Erlang/OTP 19 [erts-8.2.2] [source] [64-bit] [async-threads:10] [hipe] [kernel-poll:false]

10:28:00.261 [info] Running MyApp.Endpoint with Cowboy using http://localhost:8080
10:28:00.345 [info] Application my_app exited: MyApp.start(:normal, []) returned an error: shutdown: failed to start child: MyApp.Redis
    ** (EXIT) an exception was raised:
        ** (UndefinedFunctionError) function Exredis.start_link/0 is undefined (module Exredis is not available)
            Exredis.start_link()
            (my_app) lib/my_app/redis.ex:15: MyApp.Redis.start_link/0
            (stdlib) supervisor.erl:365: :supervisor.do_start_child/2
            (stdlib) supervisor.erl:348: :supervisor.start_children/3
            (stdlib) supervisor.erl:314: :supervisor.init_children/2
            (stdlib) gen_server.erl:328: :gen_server.init_it/6
            (stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3
{"Kernel pid terminated",application_controller,"{application_start_failure,my_app,{{shutdown,{failed_to_start_child,'Elixir.MyApp.Redis',{'EXIT',{undef,[{'Elixir.Exredis',start_link,[],[]},{'Elixir.MyApp.Redis',start_link,0,[{file,\"lib/my_app/redis.ex\"},{line,15}]},{supervisor,do_start_child,2,[{file,\"supervisor.erl\"},{line,365}]},{supervisor,start_children,3,[{file,\"supervisor.erl\"},{line,348}]},{supervisor,init_children,2,[{file,\"supervisor.erl\"},{line,314}]},{gen_server,init_it,6,[{file,\"gen_server.erl\"},{line,328}]},{proc_lib,init_p_do_apply,3,[{file,\"proc_lib.erl\"},{line,247}]}]}}}},{'Elixir.MyApp',start,[normal,[]]}}}"}
Kernel pid terminated (application_controller) ({application_start_failure,my_app,{{shutdown,{failed_to_start_child,'Elixir.MyApp.Redis',{'EXIT',{undef,[{'Elixir.Exredis',start_link,[],[]},{'Elix

Crash dump is being written to: erl_crash.dump...done

Here is what I’m adding to children of my supervisor:

worker(MyApp.Redis, []),

and finally here is my simple module:

defmodule MyApp.Redis do
  @process_name :redis

  def get(key) do
    pid = Process.whereis(@process_name)
    Exredis.query(pid, ["get", key])
  end

  def set(key, value) do
    pid = Process.whereis(@process_name)
    Exredis.query(pid, ["set", key, value])
  end

  def start_link do
    {:ok, pid} = Exredis.start_link
    true = Process.register(pid, @process_name)
    {:ok, pid}
  end
end

Code builds at main node without problem, but just one dependency is not added to release and after deploy it crashes.
What am I doing worng?

1 Like

It’s missing in the list of applications…

2 Likes

You don’t have :exredis in your application list, only things in that list are included.

For note, removing the applications list will then infer it from the deps in the latest Elixir. :slight_smile:

EDIT; Ninja’d by a second by NobbZ! ^.^

2 Likes

But then it won’t work with the min version specified in the mix file. I’d not use dependency inference unless I use other features that enforce elixir “~> 1.4”

3 Likes

@NobbZ and @OvermindDL1: Thanks for fast responses.
I was think that only some libraries needs adding to applications list.
There is no info at ExRedis README.md, so I thought that I don’t need it there.
I will try it.

1 Like

You need to include every application into the list that needs to get started before yours in case of using solely mix for lifetime management, but every single one that needs to be available in prod when dealing with releases. You can read this as “every dependency that is used at runtime needs to be in the list”.

Exactly the confusion around this has led to 1.4s dependency inference.

3 Likes

@NobbZ and @OvermindDL1: thanks, it works!

1 Like