Code_change/3 callback not working on hot upgrade

I am trying to do hot upgrade and As I posted in past the Porcelain app stopped working, It was the reason It loses its ENVs on hot upgrade which was in past set by Porcelain itself.

I have found a workaround to make GenServer for this purpose. I did as

defmodule EvercamMedia.PorcelainJanitor do
  use GenServer
  require Logger
  @vsn DateTime.to_unix(DateTime.utc_now())

  def start_link() do
    GenServer.start_link(__MODULE__, :ok, [])
  end

  def init(args) do
    {:ok, args}
  end

  def code_change(_, _, _) do
    Logger.info "I am here code change"
  end

  defp ensure_porcelain_is_init do
    Porcelain.Init.init()
  end
end

And I added this to my application’s supervisor tree as well

      supervisor(EvercamMedia.PorcelainJanitor, []),

First, As we need to change @vsn to let upgrade release realize that there was a change. So that’s I did it with Unix date (tested it with app version as well). On each upgrade this get changed, I am sure about it as It get changed and the appup file also have evidence of this each time

{"1.0.1536866932",
 [{"1.0.1536866331",
   [{load_module,'Elixir.CameraActivity',[]},
    {update,'Elixir.EvercamMedia.PorcelainJanitor',{advanced,[]},[]},
    {load_module,'Elixir.SnapshotExtractor',[]},
    {load_module,'Elixir.Timelapse',[]},
    {load_module,'Elixir.VendorModel',[]},
    {load_module,'Elixir.Camera',['Elixir.VendorModel']},

As you can see its there with update attribute.

Now here comes the main part, After all this effort

  def code_change(_, _, _) do
    Logger.info "I am here code change"
  end

This code_change call back should work. and print Logger.info, Which it dont, Not even a single time. Not when I added the module not when It got updated.

Now whenever I made a change. in any file. This got changed too and came up in appup file as well. but code_change call back not working. Any clues? where I am wrong? or whhat I am doing wrong?

The code will not be replaced until you actually use a function from the module, from a process that has been started after the app upgrade. GenServers are updated lazy.

At least this is how I understood it when I read about it in the OTP 16 days.

can you refer to any example?

I would suggest a simpler test, which does not rely on Logger:

defmodule EvercamMedia.PorcelainJanitor do
  use GenServer
  @vsn DateTime.to_unix(DateTime.utc_now())

  def vsn, do: GenServer.call(__MODULE__, :vsn)

  def start_link() do
    GenServer.start_link(__MODULE__, [], name: __MODULE__)
  end

  def init(args) do
    {:ok, 1}
  end

  def handle_call(:vsn, _from, state), do: {:reply, state, state}

  def code_change(_, state, _) do
    state + 1
  end
end

Run the upgrade, then from a remote shell, call EvercamMedia.PorcelainJanitor.vsn(), you should get back 2 after the first upgrade, 3 after the second, and so on.

2 Likes