![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
Уже в нескольких местах рассказывал одну и ту же байку. Надо бы оформить постом, и потом только ссылку давать.
Итак. Года два-три назад писал я на Эрланге. И вот возник один баг: некий процесс, скажем, Игрек, через некоторое время после запуска вдруг падает. Причём, скотина, падает даже без каких-либо сообщений в логах. А так как занят он довольно важным делом — теряется куча информации. Соответственно, релиз пришлось откатить (то ещё удовольствие) и начать разбираться.
Как оказалось, падает Игрек только под нагрузкой, так что скормить ему какие-то простенькие входные данные и аккуратно отследить, где именно его скрючит, не представляется возможным. Добавленные во все мыслимые места логи показывают, что падает процесс, в общем-то, где попало. Точнее не совсем где попало, в некоей небольшой области, но не в строго определённом месте. Я поначалу грешил на оптимизатор (случалось сталкиваться с тем, что лог выведен, а предыдущая строчка таки не выполнилась из-за того, что процесс упал), но потом понял, что наш случай таки выходит за пределы бардака, который может устроить оптимизатор. А самое смешное, что, судя по логам git, ни сам Игрек, ни вообще что-либо в непосредственной близости от него, никто не трогал уже несколько месяцев. С чего он вдруг падать начал?
Что оказалось в итоге. Был некий другой процесс, скажем, Ипсилон. И он-то как раз занимался совершенно простенькой деятельностью; он принимал сообщение, смотрел, куда его сунуть, и отсылал дальше. Причём он дожидался ответа от конечного получателя ("ага, получили, спасибо"), но сам никакого ответа отправителю не возвращал. Реализован он был через
Но какой-то гений, который его писал (я уже не помню, кто это был) слегка ошибся. Если кто не знает, то
И вот что изменилось: сам Ипсилон мы тоже не трогали — а вот в одном из конечных получателей кое-что таки подкрутили и пооптимизировали. В результате ответ Ипсилону стал приходить гораздо быстрее. И Ипсилон стал, в результате, работать быстрее. А так как его работа — это постоянные падения, то и падать он стал чаще. Если под нагрузкой, конечно; когда приходит одно сообщение в час, то он и падать будет раз в час.
Супервизор не железный, у него есть предел выносливости, заданный в настройках. Так что он увидел, что один из процессов, за которыми он следит, слишком часто падает, сказал "вы меня достали", убил всех своих подопечных, после чего убился сам. Ну и, как вы понимаете, одним из его подопечных оказался Игрек, который вообще ни в чём не виноват. И чем именно занимается Игрек в этот момент — предсказать невозможно, но, с хорошей вероятностью, тем, чем он занимается чаще всего — а это довольно небольшая область. И даже пискнуть Игрек не может, убиение его супервизором совершается мгновенно и безжалостно.
Итак. Года два-три назад писал я на Эрланге. И вот возник один баг: некий процесс, скажем, Игрек, через некоторое время после запуска вдруг падает. Причём, скотина, падает даже без каких-либо сообщений в логах. А так как занят он довольно важным делом — теряется куча информации. Соответственно, релиз пришлось откатить (то ещё удовольствие) и начать разбираться.
Как оказалось, падает Игрек только под нагрузкой, так что скормить ему какие-то простенькие входные данные и аккуратно отследить, где именно его скрючит, не представляется возможным. Добавленные во все мыслимые места логи показывают, что падает процесс, в общем-то, где попало. Точнее не совсем где попало, в некоей небольшой области, но не в строго определённом месте. Я поначалу грешил на оптимизатор (случалось сталкиваться с тем, что лог выведен, а предыдущая строчка таки не выполнилась из-за того, что процесс упал), но потом понял, что наш случай таки выходит за пределы бардака, который может устроить оптимизатор. А самое смешное, что, судя по логам git, ни сам Игрек, ни вообще что-либо в непосредственной близости от него, никто не трогал уже несколько месяцев. С чего он вдруг падать начал?
Что оказалось в итоге. Был некий другой процесс, скажем, Ипсилон. И он-то как раз занимался совершенно простенькой деятельностью; он принимал сообщение, смотрел, куда его сунуть, и отсылал дальше. Причём он дожидался ответа от конечного получателя ("ага, получили, спасибо"), но сам никакого ответа отправителю не возвращал. Реализован он был через
gen_server
, но реально никакого внутреннего состояния там не было; если точнее, внутреннее состояние всегда оставалось []
.Но какой-то гений, который его писал (я уже не помню, кто это был) слегка ошибся. Если кто не знает, то
gen_server
требует, чтобы в сервере была реализована функция handle_cast
, которая, собственно, и решает, как именно следует реагировать на поступившее сообщение. Бизнес-логику реализует, если угодно. Так вот, эта функция должна была бы возвращать {noreply, []}
(говоря тем самым "ответа не будет, новое состояние []
"), но вместо этого возвращала {ok, []}
. Нарушение типизации такое, ага.gen_server
такого ответа не понимает. Ну, тупой он, не знает, что ok
— значит, типа, всё здорово, давай дальше в том же духе. Поэтому, по завершении handle_cast
, он, в полном соответствии с идеологией Erlang "let it crash" (т.е., "гори оно огнём") падает. Но дальше, опять-таки в полном соответствии с упомянутой идеологией, в дело включается супервизор. Который видит, что Ипсилон упал — и перезапускает его. А так как состояния у Ипсилона нет — всё работает как и должно. Вместо того, чтобы сидеть и дожидаться нового сообщения, Ипсилон падает, стартует заново, и дальше сидит и ждёт нового сообщения. И так оно работало не то чтобы месяцами — годами.И вот что изменилось: сам Ипсилон мы тоже не трогали — а вот в одном из конечных получателей кое-что таки подкрутили и пооптимизировали. В результате ответ Ипсилону стал приходить гораздо быстрее. И Ипсилон стал, в результате, работать быстрее. А так как его работа — это постоянные падения, то и падать он стал чаще. Если под нагрузкой, конечно; когда приходит одно сообщение в час, то он и падать будет раз в час.
Супервизор не железный, у него есть предел выносливости, заданный в настройках. Так что он увидел, что один из процессов, за которыми он следит, слишком часто падает, сказал "вы меня достали", убил всех своих подопечных, после чего убился сам. Ну и, как вы понимаете, одним из его подопечных оказался Игрек, который вообще ни в чём не виноват. И чем именно занимается Игрек в этот момент — предсказать невозможно, но, с хорошей вероятностью, тем, чем он занимается чаще всего — а это довольно небольшая область. И даже пискнуть Игрек не может, убиение его супервизором совершается мгновенно и безжалостно.
no subject
Date: 2016-01-31 12:10 pm (UTC)no subject
Date: 2016-01-31 01:40 pm (UTC)no subject
Date: 2016-01-31 01:59 pm (UTC)no subject
Date: 2016-01-31 05:31 pm (UTC)no subject
Date: 2016-01-31 02:30 pm (UTC)no subject
Date: 2016-01-31 02:34 pm (UTC)no subject
Date: 2016-01-31 03:06 pm (UTC)no subject
Date: 2016-01-31 03:33 pm (UTC)А диалайзер — особая головная боль. Я как-то для студентов нарисовал одну программку, строк на десять, где КАЖДАЯ строка содержала хотя бы одну ошибку типизации. И натравил на это диалайзер. Получил 1 (один) ворнинг. Причём неправильный. То, что программа вообще никакого смысла не имеет и работать не может, он не заметил. Добавить сюда ещё случаи, когда диалайзер даёт false positives (логично даёт, но от этого не легче), и пожалуйста — пользоваться им невозможно.
no subject
Date: 2016-01-31 03:40 pm (UTC)no subject
Date: 2016-01-31 04:19 pm (UTC)no subject
Date: 2016-01-31 08:27 pm (UTC)no subject
Date: 2016-02-01 08:41 am (UTC)no subject
Date: 2016-02-01 07:59 pm (UTC)Улучшает собственную карму
no subject
Date: 2016-02-01 08:03 pm (UTC)no subject
Date: 2016-02-03 01:31 am (UTC)no subject
Date: 2016-02-03 11:18 am (UTC)no subject
Date: 2016-02-01 09:10 am (UTC)Но писать их, эти функции, надоедало конечно, тут бы нормальные макросы помогли, но в эрланге макросов нормальных нет. Так что не вижу при чем тут типизация :) Все от копипасты.
no subject
Date: 2016-02-01 09:22 am (UTC)no subject
Date: 2016-02-01 10:37 am (UTC)handle_cast
в документации к OTP.no subject
Date: 2016-02-01 11:22 am (UTC)no subject
Date: 2016-02-03 01:32 am (UTC)no subject
Date: 2016-02-01 11:25 am (UTC)no subject
Date: 2016-02-01 01:34 pm (UTC)Вообще, по-моему, в инфраструктуре Эрланга многое сделано плохо. То же let it crash, например — да, кривой код, написанный джуниором, вряд ли приведёт к проблемам... немедленным. Их придётся фиксить потом, через годы.
no subject
Date: 2016-02-01 12:53 pm (UTC)no subject
Date: 2016-02-01 01:32 pm (UTC)no subject
Date: 2016-02-01 01:40 pm (UTC)srv_cleanup.erl:35: The inferred return type of handle_cast/2 ({'ok',_}) has nothing in common with {'noreply',_} | {'noreply',_,'hibernate' | 'infinity' | non_neg_integer()} | {'stop',_,_}, which is the expected return type for the callback of gen_server behaviour
Хотя если на проекте нет практики применения dialyzer то какое-то время придется провести чтобы он начал показывать осмысленные результаты без лишнего шума.
no subject
Date: 2016-02-01 03:27 pm (UTC)no subject
Date: 2016-02-01 03:39 pm (UTC)no subject
Date: 2016-02-01 06:58 pm (UTC)dialyzer ругается , как и требовалось. А вот на такое не ругается.
Т.е. клозы с паттернматчингом почему-то успешно пропускаются.
Вопрос — кто здесь неправ?
no subject
Date: 2016-02-01 07:04 pm (UTC)no subject
Date: 2016-02-01 07:20 pm (UTC)no subject
Date: 2016-02-01 07:23 pm (UTC)api_handle_cast(Pid) -> gen_server:cast(Pid, {dafuq, wtf});
no subject
Date: 2016-02-01 07:32 pm (UTC)no subject
Date: 2016-02-01 07:34 pm (UTC)no subject
Date: 2016-02-01 07:44 pm (UTC)ну и комбинаторный взрыв бы все асимптотам сделал грустно, думаю...
тут всёж тип задаётся на уровне колбэка, а почему диалайзер сдюживает былоб интересно узнать, конечно.
no subject
Date: 2016-02-01 07:53 pm (UTC)Да, не срабатывает. Причем и даже если явно spec написать перед handle_cast.
no subject
Date: 2016-02-01 07:23 pm (UTC)no subject
Date: 2016-02-01 07:26 pm (UTC)no subject
Date: 2016-02-01 07:28 pm (UTC)no subject
Date: 2016-02-01 07:30 pm (UTC)no subject
Date: 2016-02-02 10:40 am (UTC)no subject
Date: 2016-02-02 10:52 am (UTC)Былоб неплохо если кто-нибудь не поленился и спросил в рассылке, может Костис чего-нибудь бы и ответил :)
no subject
Date: 2016-02-01 02:42 pm (UTC){ok, dafuq}
в спеке коллбэка. только что проверил.no subject
Date: 2016-02-01 02:43 pm (UTC)no subject
Date: 2016-02-01 02:44 pm (UTC)-> any().
) он ругается.no subject
Date: 2016-02-01 02:46 pm (UTC)-> gen_server:call_reply()
вместо копипастинга или таскания одного и того же хедера из проекта в проектno subject
Date: 2016-05-08 07:08 am (UTC)Мораль байки -- надо быть сказочным идиотом, чтобы в первую очередь не грепнуть логи на такие записи?
no subject
Date: 2016-05-08 07:22 am (UTC)