Используете ли Hangfire для задач по расписанию
Что проверяют
Работали ли вы с Hangfire в продакшене, а не только по README: где хранятся задачи, что происходит при падении и при нескольких экземплярах приложения. Следом обычно спрашивают про повторы, идемпотентность и чем Hangfire отличается от Quartz.NET или своего BackgroundService.
Как отвечать
Если использовали, опишите задачу, а затем механику:
- хранилище: задачи и их состояние лежат в базе и переживают перезапуск. SQL Server поддерживается из коробки, для PostgreSQL и Redis есть отдельные пакеты;
- типы задач:
BackgroundJob.Enqueueдля разовой,BackgroundJob.Scheduleс задержкой,ContinueJobWithдля цепочки иRecurringJob.AddOrUpdateс явным идентификатором и cron-выражением для расписания. Через DI удобнееIBackgroundJobClientиIRecurringJobManager, чем статические классы; - настройка: в современном .NET это
builder.Services.AddHangfire(...)иAddHangfireServer()вProgram.cs; - повторы: упавшая задача по умолчанию перезапускается до 10 раз с растущей паузой, настраивается атрибутом
[AutomaticRetry]; - расписание: cron по умолчанию считается в UTC, часовой пояс задают через
RecurringJobOptions; - кластер: при нескольких серверах повторяющаяся задача запускается одним из них, координация идёт через хранилище. Запретить параллельное выполнение помогает
[DisableConcurrentExecution].
Если Hangfire не использовали, так и скажите и расскажите, чем решали задачу: BackgroundService с PeriodicTimer, Quartz.NET, CronJob в Kubernetes. Затем назовите, чем Hangfire удобнее: панель мониторинга, хранение состояния и повторы без своего кода.
Где спотыкаются
Передают в задачу большие объекты или DbContext. Аргументы сериализуются в JSON и лежат в базе, передавать нужно идентификаторы, а зависимости получать в самой задаче через DI.
Пишут неидемпотентные задачи. Hangfire гарантирует выполнение хотя бы один раз: после сбоя воркера задача повторится, и письмо уйдёт дважды.
Открывают дашборд наружу. По умолчанию UseHangfireDashboard пускает только локальные запросы, для прода нужен свой IDashboardAuthorizationFilter с проверкой роли, а не фильтр, который всегда возвращает true.