Pull to refresh

Comments 12

Спасибо за Ваши статьи. С нетерпением жду следующих
Благодарю за приятный комментарий. Следующая статья уже в работе
Скажите пожалуйста, зачем для поднятия ETS из файла и сохранения в файл Вы используете gen_server? Не проще ли это сделать в application в start и stop коллбэках?
Изначально код взят из metrix (доступно в релизе), там обновления идут через вызовы gen_server. Gen_server решил оставить для явного указания owner у ETS таблички. Плюс наглядно показана механика trap_exit.
Но реализовать на уровне application:start и application:stop тоже можно.

Ну проще оно может и проще, но правильнее — вряд-ли по множеству причин. Например если понадобится эту ets-ку привязать к супервизору второго уровня типа


top_level_sup
-- some_worker
-- sub_sup + ets
---- some_sub_workers

То сделав потом во время работы приложения supervisor:terminate_child(top_level_sup, sub_sup) мы грохнем ets-ку не сделав дамп.

Не понял к чему Вы. В примере gen_server не делает ничего, кроме как создает ets при старте и дампит ее в файл при завершении. Как по мне — ген_сервер для этого совсем не нужен, для примера достаточно использовать коллбэки application. Если и использовать gen_server — то работать с ets полностью через него, об этом как раз в коментариях ниже

Я почему-то подумал, что вы предлагали создавать ETS не в процессе application controller, а в процессе корневого супервизора приложения. Ну тогда этот аргумент, пожалуй, не сработает.
Но я бы в любом случае 10 раз подумал бы прежде чем пристёгивать ETS к "системному" процессу. Часто потом возникает необходимость вокруг этой ETS наворачивать дополнительную логику, периодические всякие таски…
Опять же, всегда имеет смысл заворачивать ETS в модуль с API функциями вместо прямого обращения через ets:*, а для этого имеет смысл создать отдельный модуль. А если уже модуль создал, то конвертнуть его в gen_server это 10 строк.

Нет, из супервизора создавать ets я не предлагал:) Я начал смотреть код, когда увидел gen_server — сразу подумал, что работа со счетчиком идет через него (gen_server:call), и удивился, что по факту сервер ничего не делает. Для примера я предложил поместить создание и удаление таблицы в application controller. В этом случае пришлось бы сделать таблицу named public. Как такового паттерна здесь скорее нет. Я на практике использовал оба варианта: публичная таблица, создаваемая при старте приложения — как правило это какой-то кэш. Либо второй вариант — private таблица, вся работа идет через api родительского процесса. В примере получился микс, который новичка может сбить с толку.
работать с ets полностью через него

Имеете в виду работать с приватной таблицей через gen_server:call или работать с публичной таблицей через ets:*, но завернуть их в API-функции?

Имел ввиду работу с приватной таблицей через gen_server:call. В этом случае, кстати, в зависимости от задачи, можно вобще не использовать ets, а хранить счетчики в state ген-сервера.

Спасибо за статью.
Небольшой код-ревью сделаю, если уместно:



Насчет того что вам советуют создавать ETS прямо из супервизора — плохой совет. Для быстрых хаков ещё куда ни шло, но для реальных проектов и обучающих статей — плохая практика. Во первых ваши dump/restore не получилось бы тогда реализовать корректно, во вторых если захочется добавить какие-то периодические операции типа "удалять устаревшие ключи раз в 10 минут", то всё равно аккуратнее всего это будет смотреться внутри одного модуля с gen_server.

Спасибо за конструктивный ревью!
1) Метрики в таком виде были написаны в далеком 2011 году. Сначала просто, как индикатор, т.е открываем tail -f metrics_file и смотрим счетчики, а потом в некоторых проектах эти файлы обрабатывались на узлах агентами и отрпавлялись в zabbix. В статью импортировал этот код для того, чтобы показать, как работает метапроект из нескольких приложений.
Если выбирать из существующих библиотек, то folsom или exometer вполне решают данную задачу.
Так же хочется отметить, что в примере приложения нет логгера. Про подходы к логгированию и анализу логов тоже можно написать статью. В качестве практики для интересующихся, можно попробовать прикрутить lager (c вынесением в заголовочный файл макросов с уровнями от DEBUG до FATAL).
2) Убрал повторяющийся паттерн. Только добавил Req и State к Status, Body. Так как в обработчике могут поставить например cookie. Ну и State необходим, например для долгоживущих обработчиков (websocket, http-streaming)
3) Согласен, как раз в статье про внешние хранилища данных этот код модернизируется, и детали реализации хранилища спрятаны в общем интерфейсе: сначала добавляется класс обслуживания основанный на tarantool, а затем класс обслуживания на riak. Поправил интерфейс.
4) Catch убрал из реализации. Действительно за ним не было видно проблемы. Catch в erlang не стоит использовать, только совсем уж в крайних случаях.
Исходную проблему можно было отловить с помощью дополнительного теста проверяющего, что данные восстановились.
Sign up to leave a comment.

Articles