Глупый вопрос про обновление gen_server

512 views
Skip to first unread message

Alexandr Alexeev

unread,
Sep 7, 2012, 5:37:35 AM9/7/12
to erlang-...@googlegroups.com
Скажем, у меня есть gen_server и мне хочется очень простой вещи: иметь
возможность выкатить на сервер новый код (.erl/.beam - не суть),
послать серверу команду myserver:upgrade() и чтобы код обновился, а
состояние сервера не изменилось.

Написал следующее решение:

upgrade() -> gen_server:call({global, ?MODULE}, { upgrade }).

handle_call({ upgrade }, _From, State) ->
code:purge(?MODULE),
compile:file(?MODULE),
code:load_file(?MODULE),
{ reply, ok, State };

Похоже, что этот код делает то, что мне нужно, однако беспокоят
некоторые моменты:
* Не выявится ли каких косяков под настоящей нагрузкой?
* Почему не вызывается code_change?
* Из-за второго пункта меня терзают подозрения, что я что-то сделал не
так, изобрел велосипед к примеру.

Что скажите?

В процессе гугления мне попадались упоминания всяких Erlmakefile и
инструментов для работы с релизами, но для меня это пока сложно,
хотелось бы с мат частью разобраться для начала.

Max Lapshin

unread,
Sep 7, 2012, 5:51:44 AM9/7/12
to erlang-...@googlegroups.com
Если вы просто на ноде запустите этот код, или просто выкатите новый
код под сервер и сделаете make:all([load]) на этой ноде (для этого как
раз нужен Emakefile), то у вас обновится код во всех gen_server-ах.

Обновление кода осуществляется в тот момент, когда вызывается функция
с явным указанием модуля:

?MODULE:loop(...)

Поэтому самопальные процессы в которых написан просто
loop(...)

спонтанно дохнут, когда их модуль вытесняется.

Однако в таком простом виде у вас всё свалится, если стейт — рекорд и
он поменялся.
Для этого есть система релизов, которая останавливает все ваши
процессы, вызывает в них code_change и запускает дальше.

Лично я сходу не представляю, как можно взять и написать code_change
так, что бы перенести данные из старого стейта в новый.
Т.е. у меня есть идеи, но я даже не хочу их проверять. Впрочем, у
кого-то это работает, но они никогда не рассказывают, как именно.

Поэтому резюме такое:
1) самый простой способ — make:all([load]). на ноде и сразу получать новый код
2) если рекорд поменялся всё свалится — будьте к этому готовы

Anton Fedorov

unread,
Sep 7, 2012, 6:33:56 AM9/7/12
to erlang-...@googlegroups.com
я апгрейжу так:
%upgrade(Server, Ver) ->
ok = sys:suspend(Server),
make:load([all]),
ok = sys:change_code(Server, Server, Ver, []),
ok = sys:resume(Server),
ok.



В письме от Птн, 07 Сен 2012, 16:37 Alexandr Alexeev пишет:
> --
> --
> Страница рассылки: http://groups.google.com/group/erlang-russian
> Новости: http://erlanger.ru
> Чат: xmpp://erl...@conference.jabber.ru
> Чат для оффтопа: xmpp://erlang...@conference.jabber.ru
>
> Написать письмо: erlang-...@googlegroups.com
> Отписаться: erlang-russia...@googlegroups.com
>
>
>


--
Regards,
Anton Fedorov
Call2ru service
E-Mail: datac...@call2ru.com
Jabber: datac...@call2ru.com
Skype: datacompboy
ICQ: 272-35-262
Mobile: +7-913-925-7974 [SMS 24h, Call 05:00-19:00 MSKT (GMT+3)]

Max Lapshin

unread,
Sep 7, 2012, 6:37:27 AM9/7/12
to erlang-...@googlegroups.com
2012/9/7 Anton Fedorov <datac...@call2ru.com>:

> я апгрейжу так:
> %upgrade(Server, Ver) ->
> ok = sys:suspend(Server),
> make:load([all]),
> ok = sys:change_code(Server, Server, Ver, []),
> ok = sys:resume(Server),
> ok.
>

А что при этом происходит с другими процессами?

Anton Fedorov

unread,
Sep 7, 2012, 6:42:31 AM9/7/12
to erlang-...@googlegroups.com
Какие минусы я вижу в вашем решении:

> handle_call({ upgrade }, _From, State) ->
> code:purge(?MODULE),
в этот момент текущий код становится "фоновым", актуальный -- удаляется

> compile:file(?MODULE),
а если тут произошла ошибка?

> code:load_file(?MODULE),
получили пыщ-пыщ-ололо.

> { reply, ok, State };

> * Почему не вызывается code_change?
Если уж делать ЭТО таким образом, то правильнее так:

handle_call({ upgrade }, _From, State) ->
{ok, ?MODULE} = compile:file(?MODULE), % упадёт даже если будут варнинги!
{module, ?MODULE} = code:load_file(?MODULE),
% code_change вызывается уже у новой версии кода
{ok, NewState} = ?MODULE:code_change(?VSN, State, []),
{ reply, ok, NewState };

Только -define(VSN) нужно обновлять кроме -version(), предеф версии такой нет
доступной. а так было бы совсем удобно.

Anton Fedorov

unread,
Sep 7, 2012, 7:01:28 AM9/7/12
to erlang-...@googlegroups.com

В письме от Птн, 07 Сен 2012, 17:37 Max Lapshin пишет:
>> я апгрейжу так:
>> %upgrade(Server, Ver) ->
>> ok = sys:suspend(Server),
>> make:load([all]),
>> ok = sys:change_code(Server, Server, Ver, []),
>> ok = sys:resume(Server),
>> ok.
>>
>
> А что при этом происходит с другими процессами?

Собственно, ничего. В том и смысл suspend / resume

Max Lapshin

unread,
Sep 7, 2012, 7:01:49 AM9/7/12
to erlang-...@googlegroups.com
2012/9/7 Anton Fedorov <datac...@call2ru.com>:

>
> В письме от Птн, 07 Сен 2012, 17:37 Max Lapshin пишет:
>>> я апгрейжу так:
>>> %upgrade(Server, Ver) ->
>>> ok = sys:suspend(Server),
>>> make:load([all]),
>>> ok = sys:change_code(Server, Server, Ver, []),
>>> ok = sys:resume(Server),
>>> ok.
>>>
>>
>> А что при этом происходит с другими процессами?
>
> Собственно, ничего. В том и смысл suspend / resume
>

Но ты и под ними меняешь код.

Anton Fedorov

unread,
Sep 7, 2012, 7:04:55 AM9/7/12
to erlang-...@googlegroups.com
Если ты про load([all]) -- то я показал схему, а не точный список команд :)
сперва suspend всем нужным, потом load, потом code_change всем, потом resume всем.
всё это должно лечь в 5 сек (дефолтный таймаут gen_server:call).

В письме от Птн, 07 Сен 2012, 18:01 Max Lapshin пишет:
> --
> --
> Страница рассылки: http://groups.google.com/group/erlang-russian
> Новости: http://erlanger.ru
> Чат: xmpp://erl...@conference.jabber.ru
> Чат для оффтопа: xmpp://erlang...@conference.jabber.ru
>
> Написать письмо: erlang-...@googlegroups.com
> Отписаться: erlang-russia...@googlegroups.com
>
>
>


Max Lapshin

unread,
Sep 7, 2012, 7:05:39 AM9/7/12
to erlang-...@googlegroups.com
Т.е. ты делаешь восход солнца вручную и это прекрасно работает?

Anton Fedorov

unread,
Sep 7, 2012, 7:07:22 AM9/7/12
to erlang-...@googlegroups.com
Вручную -- это топикстартер. А я делаю закат солнца с помощью лебёдки.
Работает прекрасно. Да, релизы я не осиливал, а вот code_change пользую с
положительным успехом.

В письме от Птн, 07 Сен 2012, 18:05 Max Lapshin пишет:
> Т.е. ты делаешь восход солнца вручную и это прекрасно работает?
>

Alexandr Alexeev

unread,
Sep 7, 2012, 7:09:36 AM9/7/12
to erlang-...@googlegroups.com
Так, OK. Помогите пожалуйста разобраться, как обновить код по-человечески.

Я создал Emakefile:
{'src/test.erl', [{outdir, "ebin"}]}.

Собрал сервер:
erl -make

Запустил сервер:
erl -noshell -pa ebin -sname test -run test start

Поработал с сервером, теперь решил его обновить. Мне почему-то
казалось, что должно сработать простое erl -make, после чего
виртуальная машина должна увидеть новый beam, подгрузить его и
автоматически переключаться на новый код при вызове
?MODULE:any_function. Но видимо это не так, ибо не работает. Я даже
добавил вызов ?MODULE:dummy.

А как нужно?

7 сентября 2012 г., 14:42 пользователь Anton Fedorov
<datac...@call2ru.com> написал:

> --
> --
> Страница рассылки: http://groups.google.com/group/erlang-russian
> Новости: http://erlanger.ru
> Чат: xmpp://erl...@conference.jabber.ru
> Чат для оффтопа: xmpp://erlang...@conference.jabber.ru
>
> Написать письмо: erlang-...@googlegroups.com
> Отписаться: erlang-russia...@googlegroups.com
>
>

--
С уважением, Александр
Личный блог: http://eax.me/
Мой форум: http://it-talk.org/
Мой Twitter: http://twitter.com/afiskon

Max Lapshin

unread,
Sep 7, 2012, 7:11:07 AM9/7/12
to erlang-...@googlegroups.com
Так. А вот насчёт апгрейда структуры мне сейчас пришла в голову мысль.

Если code_change вызывается ещё в старом модуле, то можно добавить
функцию, которая превращает рекорд в проплист и обратно.

Тогда из старого рекорда мы извлечем данные старым модулем и переложим в новый:

code_change(OldState) ->
OldProplist = state_to_proplist(OldState),
NewState = ?MODULE:proplist_to_state(OldProplist)

Но это только если code_change в старом модуле.

Max Lapshin

unread,
Sep 7, 2012, 7:12:24 AM9/7/12
to erlang-...@googlegroups.com
> Поработал с сервером, теперь решил его обновить. Мне почему-то
> казалось, что должно сработать простое erl -make, после чего
> виртуальная машина должна увидеть новый beam, подгрузить его и

А как она должна увидеть? Дергая раз в секунду на диске все загруженные бимы?
А если они по сети были залиты?

Нет, это безусловно невозможно. Так работают штуки типа sync и
reloader, но лично я ими не пользуюсь.

>
> А как нужно?
>

Написали же: в консоли сделать make:all([load]).

Haspadar

unread,
Sep 7, 2012, 7:16:12 AM9/7/12
to erlang-...@googlegroups.com
Кстати вариант - подконнектиться через rsh, reloader:start(), обновить код, reloader:stop(). Но это не лучший совет, наверное. 

7 сентября 2012 г., 14:12 пользователь Max Lapshin <max.l...@gmail.com> написал:

Anton Fedorov

unread,
Sep 7, 2012, 7:18:20 AM9/7/12
to erlang-...@googlegroups.com
sys вызывает code_change уже в новом контексте.

Меняя туплы я делаю так:
code_change("1.0.0", {state, A, B, C}, _) ->
{ok, #state{a=A, b=B, c=C, d=D}}.

то есть для старой версии вручную выписываю старую структуру.
хоть и неудобно.

В письме от Птн, 07 Сен 2012, 18:11 Max Lapshin пишет:

Max Lapshin

unread,
Sep 7, 2012, 7:21:55 AM9/7/12
to erlang-...@googlegroups.com
> Меняя туплы я делаю так:
> code_change("1.0.0", {state, A, B, C}, _) ->
> {ok, #state{a=A, b=B, c=C, d=D}}.
>

Хоть индуса на такое нанимай.

Anton Fedorov

unread,
Sep 7, 2012, 7:24:37 AM9/7/12
to erlang-...@googlegroups.com
А так часто надо менять #state{} ?

В письме от Птн, 07 Сен 2012, 18:21 Max Lapshin пишет:

Max Lapshin

unread,
Sep 7, 2012, 7:29:21 AM9/7/12
to erlang-...@googlegroups.com
А если его не менять, то почему просто не сделать make:all([load]).
без этих code_change?

Anton Fedorov

unread,
Sep 7, 2012, 7:31:51 AM9/7/12
to erlang-...@googlegroups.com


В письме от Птн, 07 Сен 2012, 18:29 Max Lapshin пишет:
> А если его не менять, то почему просто не сделать make:all([load]).
> без этих code_change?

Еще иногда надо поменять структуру в mnesia, ets, запустить чего-нибудь в фоне и
еще всякие мелочи.

Но, естественно, code_change пишется только тогда когда оно надо. Всё остальное
-- через просто make:all([load]).

Max Lapshin

unread,
Sep 7, 2012, 7:35:45 AM9/7/12
to erlang-...@googlegroups.com
Ну вот: поменять структуру рекорда в ets.
Та же ведь фигня.

Alexandr Alexeev

unread,
Sep 7, 2012, 8:20:26 AM9/7/12
to erlang-...@googlegroups.com
>> Написали же: в консоли сделать make:all([load]).

Разобрался, спасибо! Вот только я надеялся, что code_change() будет
вызываться, но похоже, что он и не должен:

This function is called by a gen_server when it should update its
internal state during a *RELEASE* upgrade/downgrade. (
http://www.erlang.org/doc/man/gen_server.html )

А чтобы произошло обновление релиза, нужно воспользоваться -version
или VSN или может еще чем-то?

7 сентября 2012 г., 15:35 пользователь Max Lapshin
<max.l...@gmail.com> написал:


> Ну вот: поменять структуру рекорда в ets.
> Та же ведь фигня.
>

> --
> --
> Страница рассылки: http://groups.google.com/group/erlang-russian
> Новости: http://erlanger.ru
> Чат: xmpp://erl...@conference.jabber.ru
> Чат для оффтопа: xmpp://erlang...@conference.jabber.ru
>
> Написать письмо: erlang-...@googlegroups.com
> Отписаться: erlang-russia...@googlegroups.com
>
>

--

Reply all
Reply to author
Forward
0 new messages