How to keep state of an updated-dependency in run time

Hi, imagine you want to add a new dep in your project without restart your app:

Code.prepend_path(path)
Application.load(timex)
Application.ensure_all_started(timex)

With these 3 functions and a new source which is compiled before, your project can use new deps. But I have a problem to update an installed-app like timex.

For example, I want to update timex to new version: if I call the Application.load(app) and Application.ensure_all_started(app) functions, I can see this error: this dep is already loaded.

So I found another function: Application.unload this can not make timex unload, so it forces me to use Application.stop; This is the place for example I lose the state of timex or the other apps I want to update.

How can keep the states and freeze them (for example a gen server with supervisor, agent), after update I can be able to resume them again?

I have read some of the Erlang documents, so every page call release mode, but I have no idea how it can help me?

Thank you in advance.

2 Likes

I believe that an already-installed dependency will be updated as part of a hot-code upgrade of your own main app.

This will trigger all of the loaded GenServer’s code_change/3 methods to fire.
There are three possibilities:

  1. The GenServer has no custom implementation of code_change/3 which means that the default one is used. This one keeps the state the same. This does mean that if the structure of the state that if the new version of the GenServer expects the state to have a different shape, that it will probably crash once the next event arrives. Depending on how it is supervised it will restart after, but it might result in a temporary crash and some lost state and/or a lost event.
  2. The GenServer has a custom implementation of code_change/3. Assuming it is implemented correctly (and does not raise), the GenServer will be upgraded perfectly. Note that not many libraries implement custom code_change/3s for their servers.
  3. The GenServer has a custom implementation of code_change/3 which raises an exception. This prevents the hot-code upgrade of this particular OTP application (and usually your hot-code upgrade as a whole) to finish; everything will stay at/revert to the previously-used version. This is for instance commonly done for modules containing NIFs (natively-implemented functions) because making native code backwards/forwards compatible is again much more difficult.
3 Likes

I think I missed in my questions!!
When I use Application.stop :mishka_developer_tools it shows me:

00:14:26.602 [notice] Application mishka_developer_tools exited: :stopped

I think, all the state of this app after I run this function is gone. Without stop, I can not update a full project, not a module!!

I think I need to solve this problem at first, how to update a dependency which is in Application.loaded_applications, actually now I first delete it and load again

Do you suggestion?

I am no families enough with Erlang, but I found this post:
https://www.erlang.org/doc/design_principles/release_handling.html#release-upgrade-file

So could not test it

1 Like

By the way, I found this article in the following comment: erlang - Elixir: how can I leverage release_handler? - Stack Overflow

And I have read the release section of “Designing for Scalability with Erlang/OTP: Implement Robust, Fault-Tolerant Systems” book.

I am not sure, but I think using release for installing multi version of dep is not going to work perfectly in elixir, or I could not use it!! :pensive:

Hot Code Upgrades mix release — Mix v1.13.4

Erlang and Elixir are sometimes known for the capability of upgrading a node that is running in production without shutting down that node. However, this feature is not supported out of the box by Elixir releases.

1 Like

You do not need to stop an application first before upgrading it.

Maybe I am misunderstanding what you’re trying to accomplish?

2 Likes

As I said before, if I don’t stop the dependency, I can not load a new version of it.
I have no problem when I want to add a new dependency which is not installed before, but after compiling a new version of a dependency which was installed, How can I load the new version, when I run Application.loaded_applications I can be able to see the new version.

If still you do not understand, please let me know, I can create a short video

1 Like

To do hotcode reloading, you actually need to have the dependencies written for that, and you need to provide release upgrade scripts, which describe which modules in which application needs to get replaced.

There are probably more low level tools that could do hot reloading without releases, though it still requires that the applications are built with hot code upgrades in mind and that their GenServers or whatever keeps the state is able to convert old state to new state.

2 Likes

Yes, I know, but do you have any sample code or tools/lib for this what you said?

No.

I never used hotcode reload facilities in any way, though phoenix has to do its module reload some how? Perhaps you can transfer that to a manual dependenvy reload?

Anyway, I’d suggest to not try, but instead just stop your application, start it with updated deps and then rehydrate the state from the database.

I think, I should create a hook to let user extension before dropping in application, if they have important stay, so backup and let my program know is done, after that I can drop them