I am currently working on a live media server that will allow general consumers to send us live video. In our current environment, we saw programs sent to us with a duration of days, so the idea of ββfixing a mistake (or adding a function) without disconnecting users is extremely convincing.
However, when I wrote the code, I realized that exchanging hot code does not make any sense if I do not write every process, so that all state is always executed inside gen_server, and all external modules that call gen_server should be as simple as possible.
Take the following example:
-module(server_template). -behaviour(gen_server). -export([start/1, stop/0]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). start() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). init([]) -> {ok, {module1:new(), module2:new()}}. handle_call(Message, From, State) -> {reply, ok, State}. handle_cast(any_message, {state1, state2}) -> new_state1 = module1:do_something(state1), new_state2 = module2:do_something(state2), {noreply, {new_state1, new_state2}}. handle_info(_Message, _Server) -> {noreply, _Server}. terminate(_Reason, _Server) -> ok. code_change(_OldVersion, {state1, state2}, _Extra) -> new_state1 = module1:code_change(state1), new_state2 = module2:code_change(state2) {ok, {new_state1, new_state2}}
According to what I could find when a new version of the code is loaded into the current working environment without using an OTP system, you can update it to the current version of the code by calling your module as an external function call, therefore my_module:loop(state) .
What I also see is that when performing a hot swap, the code_change/3 function is called and updates the state, so I can use this to make sure that each of my dependent modules transfers the last state that they gave me to the state for current version of the code. He does this because the supervisor knows about the current process, which allows you to pause the process so that it can call the code change function. Things are good.
However, if calling an external module always calls the current version of this module, then it seems to break if a hot swap is performed in the middle of a function. For example, my same gen_server is currently in the process of processing any_message stock, say, between running module1:do_something() and module2:do_something() .
If I understand things correctly, module2:do_something() will now call the new current version of the do_something function, which could potentially mean that I am passing ungraded data to the new version of module2:do_something() . This can easily cause problems if it changes a record, an array with an unexpected number of elements, or even if there is no value on the map that the code expects.
Do I really not understand how this situation works? If this is correct, this seems to indicate that I should track some types of version information for any data structure that can cross module boundaries, and each public function should check this version number and, if necessary, perform migration on demand .
This seems to be a very high order, which seems insane, error prone, so I wonder if I am missing something.