ElixirSeniorExperience

Какие 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 и безопасность
  • Умеет ранжировать риски по вероятности и влиянию

Related topics