Какие production-риски есть у Actix (Rust): blocking code, connection pooling, config, auth, observability, deploy или graceful shutdown?
Главные production-риски Actix: блокирующий код в async-хендлерах, отсутствие пула соединений к БД, неправильная конфигурация воркеров, отсутствие graceful shutdown и слабая observability.
Production-риски Actix Web
Actix Web работает на tokio runtime, и каждая ошибка в async-коде может стоить производительности всего сервиса. Рассмотрим ключевые зоны риска.
Blocking code в async-контексте
Любой синхронный вызов внутри async-хендлера блокирует поток tokio. Это убивает throughput. Тяжёлые операции нужно выносить в web::block() или tokio::task::spawn_blocking():
use actix_web::{web, HttpResponse};
async fn heavy_handler() -> HttpResponse {
let result = web::block(|| {
// синхронная операция: чтение файла, CPU-тяжёлые вычисления
std::fs::read_to_string("/etc/hosts").unwrap()
})
.await
.unwrap();
HttpResponse::Ok().body(result)
}
Connection pooling
Без пула соединений каждый запрос открывает новое TCP-соединение к PostgreSQL — latency растёт, БД падает под нагрузкой. Стандартный стек: sqlx + PgPool, передаётся через web::Data:
use sqlx::PgPool;
use actix_web::{web, App, HttpServer};
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let pool = PgPool::connect(&std::env::var("DATABASE_URL").unwrap())
.await
.unwrap();
HttpServer::new(move || {
App::new()
.app_data(web::Data::new(pool.clone()))
.service(web::resource("/items").route(web::get().to(list_items)))
})
.workers(4)
.bind("0.0.0.0:8080")?
.run()
.await
}
Конфигурация воркеров
По умолчанию .workers() равен числу логических CPU. На контейнере с 2 vCPU и большим числом I/O-bound запросов стоит увеличить до 4–8. При CPU-bound нагрузке — держать равным числу ядер.
Auth
Встроенной аутентификации нет. Типичный подход — middleware с JWT через actix-web-httpauth. Риск: проверка токена блокирует если реализована синхронно, а секреты JWT попадают в репозиторий через хардкод.
Observability
Без трейсинга невозможно дебажить production. Минимальный стек: tracing + tracing-actix-web для request-id и spans, prometheus через actix-web-prom для метрик:
use actix_web_prom::PrometheusMetricsBuilder;
let prometheus = PrometheusMetricsBuilder::new("api")
.endpoint("/metrics")
.build()
.unwrap();
App::new().wrap(prometheus)
Graceful shutdown
Actix поддерживает graceful shutdown через .shutdown_timeout(). Без него in-flight запросы обрываются при деплое:
HttpServer::new(|| App::new())
.shutdown_timeout(30) // секунды
.bind("0.0.0.0:8080")?
.run()
.await
В Kubernetes добавляйте preStop hook с sleep 5, чтобы kube успел убрать pod из endpoints до отправки SIGTERM.
Подводные камни
- Вызов
std::thread::sleep()или синхронного I/O прямо в async-хендлере — полностью блокирует tokio-поток. - Клонирование
PgPoolне создаёт новый пул — это cheap Arc-clone, всё корректно. Но создание несколькихPgPool::connect()— ошибка, будет несколько независимых пулов. - Паника в хендлере не крашит весь процесс (actix её перехватывает), но возвращает 500 без деталей — логируйте явно.
web::Data<T>требует, чтобы T былSend + Sync; при использованииRefCellилиRcкомпилятор откажет.- Отсутствие rate limiting на уровне middleware позволяет одному клиенту исчерпать пул соединений.
- TLS-терминация в самом Actix через rustls требует правильной настройки сертификатов; легче вынести в nginx/envoy.
- Неправильный порядок middleware (например, логирование после auth) скрывает неавторизованные запросы в логах.
- Игнорирование
SIGTERMв Docker — контейнер убивается через 10 секунд принудительно, без завершения in-flight.
What hurts your answer
- Говорить только о запуске Actix (Rust), но не об эксплуатации
- Не упоминать observability, обновления, безопасность и rollback
- Описывать риски абстрактно, без способов их снижать
What they're listening for
- Видит production-риски Actix (Rust)
- Говорит про monitoring, rollout, rollback и безопасность
- Умеет ранжировать риски по вероятности и влиянию