Какие production-риски чаще всего возникают в проектах на Elixir: производительность, зависимости, конкурентность, деплой или observability?
Главные production-риски: GenServer-бутылочные горлышки под нагрузкой, утечки памяти через накопление сообщений в mailbox, сложность hot-code upgrades, отсутствие структурированного логирования и трассировки, а также управление зависимостями с нативными NIF.
Production-риски в проектах на Elixir
Elixir и OTP дают высокую надёжность из коробки, но на production возникают специфические проблемы, которые не очевидны во время разработки.
1. Производительность: GenServer как узкое место
Однопоточный GenServer, принимающий тысячи вызовов в секунду, становится бутылочным горлышком. Диагностика:
# Проверить длину очереди сообщений
Process.info(pid, :message_queue_len)
# => {:message_queue_len, 15000} -- тревожный знак
# :observer в iex
:observer.start()
# Recon для production без остановки
:recon.proc_count(:message_queue_len, 5)
Решения: партиционирование через :poolboy или собственный пул, переход на ETS для read-heavy данных, использование handle_continue/2 для тяжёлой инициализации.
2. Утечки памяти и накопление атомов
Атомы не удаляются GC. Динамическое создание атомов из пользовательских данных (через String.to_atom/1) исчерпывает лимит ~1 048 576 и валит ноду. Используйте String.to_existing_atom/1 или оставайтесь на строках. Большие бинарные данные (>64 байт) хранятся в shared heap — они удаляются GC не сразу, что приводит к временным всплескам памяти.
3. Зависимости с NIF
Нативные расширения (Rustler, порты через C) падают и валят всю BEAM VM, не только один процесс. Необходимо изолировать NIF в отдельный OS-процесс через Port или использовать :erlang.spawn_opt([:monitor]).
4. Деплой: releases и hot upgrades
Mix releases не поддерживают горячее обновление кода из коробки. Appups/relups сложны в поддержке. Стандартный подход — rolling restart через оркестратор (Kubernetes, Fly.io). Главный риск: потеря состояния GenServer при рестарте. Нужно персистировать состояние в PostgreSQL/Redis или использовать :sys.replace_state/2 для миграции.
# Graceful drain перед остановкой
System.stop(0)
# или через SIGTERM handler
5. Observability: логирование и трассировка
Стандартный Logger выводит plain text. На production нужны структурированные логи:
# config/prod.exs
config :logger, :console,
format: {MyApp.LogFormatter, :format},
metadata: [:request_id, :user_id, :trace_id]
# Использование OpenTelemetry
# mix.exs
{:opentelemetry, "~> 1.3"},
{:opentelemetry_exporter, "~> 1.6"},
{:opentelemetry_phoenix, "~> 1.1"}
Без трассировки межузловые вызовы в кластере нельзя отследить. Пропуск :telemetry-событий Phoenix/Ecto оставляет систему без метрик.
6. Конкурентность: race conditions в ETS
ETS операции атомарны только для одной строки. Операции «прочитать-изменить-записать» требуют либо GenServer-сериализации, либо атомарных ETS-операций (:ets.update_counter/3).
Подводные камни
- Supervision tree перезапускает упавший процесс немедленно — если причина системная (нет соединения с БД), процесс войдёт в restart loop и сработает max_restarts, убив весь supervisor.
- Длинные синхронные вызовы GenServer.call/3 с таймаутом по умолчанию 5 секунд блокируют caller; под нагрузкой каскадно забивают пулы.
- Mix release не включает исходники — трейсбеки в production будут без имён модулей без флага :debug_info.
- Fly.io и Kubernetes не гарантируют stable hostname — нужен libcluster с DNS или k8s-стратегией, иначе кластер не соберётся.
- Broadway/GenStage pipeline при backpressure накапливает сообщения в mailbox продюсера — нужен явный :max_demand.
- Отсутствие health-check эндпоинта, учитывающего состояние воркеров (не только HTTP 200), приводит к маршрутизации трафика на деградировавший узел.
What hurts your answer
- Говорить только о запуске Elixir, но не об эксплуатации
- Не упоминать observability, обновления, безопасность и rollback
- Описывать риски абстрактно, без способов их снижать
What they're listening for
- Видит production-риски Elixir
- Говорит про monitoring, rollout, rollback и безопасность
- Умеет ранжировать риски по вероятности и влиянию