Как работает горячая замена кода Erlang в середине активности?

В настоящее время я работаю над живым медиа-сервером, который позволит обычным пользователям отправлять нам живое видео. В нашей текущей среде мы видели широковещательные рассылки, которые отправлялись нам в течение нескольких дней, поэтому идея исправить ошибку (или добавить функцию) без отключения пользователей является чрезвычайно убедительной.

Однако, когда я писал код, я понял, что горячая замена кода не имеет никакого смысла, пока я не напишу каждый процесс, чтобы все состояния всегда выполнялись внутри gen_server, а все внешние модули, которые вызывает gen_server, должны быть максимально простыми.

Давайте возьмем следующий пример:

-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}}

Согласно тому, что я мог найти, когда новая версия кода загружается в текущую среду выполнения без использования системы OTP, вы можете перейти на текущую версию кода, вызвав свой модуль как вызов внешней функции, так чтоmy_module:loop(state).

Я также вижу, что при горячей заменеcode_change/3 Функция вызывается и обновляет состояние, поэтому я могу использовать это, чтобы убедиться, что каждый из моих зависимых модулей переносит последнее состояние, которое они дали мне в состояние для текущей версии кода. Это происходит потому, что супервизор знает о запущенном процессе, что позволяет приостановить процесс и вызвать функцию изменения кода. Все хорошо.

Тем не менее, если вызов внешнего модуля всегда вызывает текущую версию этого модуля, это может привести к поломке, если горячая замена выполняется в середине функции. Например, тот же мой gen_server в настоящее время находится в процессе обработкиany_message В ролях, скажем, между бегомmodule1:do_something() а такжеmodule2:do_something().

Если я правильно понимаю вещи,module2:do_something() теперь будет вызывать новую текущую версиюdo_something функция, которая потенциально может означать, что я передаю неперенесенные данные в новую версиюmodule2:do_something(), Это может легко вызвать проблемы, если это изменившаяся запись, массив с неожиданным количеством элементов или даже если на карте отсутствует значение, ожидаемое кодом.

Я неправильно понимаю, как эта ситуация работает? Если это правильно, это указывает на то, что я должен отслеживать какой-то тип информации о версии для любой структуры данных, которая может переходить границы модуля, и каждая открытая функция должна проверять этот номер версии и выполнять миграцию по требованию, если это необходимо.

Это, кажется, чрезвычайно высокий заказ, который кажется безумно подверженным ошибкам, поэтому мне интересно, что я что-то упустил.

Ответы на вопрос(2)

Ваш ответ на вопрос