Skip to content

Heartbeat

Heartbeat contract

Heartbeat - это механизм, с помощью которого ChokaQ отличает долго выполняющееся здоровое задание от задания, которым владеет мертвый воркер.

Есть два понятия heartbeat:

HeartbeatStored wherePurpose
Worker heartbeatProcess memory через IWorkerManager.LastHeartbeatUtcДоказывает для health checks, что локальный worker loop жив.
Job heartbeatJobsHot.HeartbeatUtcДоказывает, что конкретное processing job все еще принадлежит живому execution.

Job Heartbeat Contract

Когда задание переходит в Processing, ChokaQ устанавливает StartedAtUtc и HeartbeatUtc. Пока handler выполняется, heartbeat task периодически обновляет HeartbeatUtc.

Позже zombie rescue проверяет processing rows, чей heartbeat старше настроенного timeout. Такие строки перемещаются в DLQ как Zombie, чтобы оператор мог оценить риск side effects перед resurrection.

Почему heartbeat, а не start time?

Один только start time не отличает здоровый долгий report от мертвого воркера. Heartbeat дает воркеру способ сказать: "Это задание все еще выполняется."

Configuration

Важные настройки:

SettingMeaning
Execution.HeartbeatIntervalMin / HeartbeatIntervalMaxJittered interval для per-job heartbeat writes.
Execution.HeartbeatFailureThresholdКоличество failed writes перед reporting degraded heartbeat state.
Execution.CancelOnHeartbeatFailureНужно ли отменять execution при heartbeat write failure.
Recovery.ProcessingZombieTimeoutВозраст, после которого missing heartbeat превращает processing work в zombie DLQ.
Queues.*.ZombieTimeoutSecondsOptional per-queue override.

Zombie timeout должен быть заметно больше heartbeat interval.

Failure path

Если heartbeat writes fail, ChokaQ записывает chokaq.jobs.heartbeat_failures. В зависимости от policy задание может продолжить работу или быть отменено. Если процесс умирает, heartbeat останавливается, и zombie rescue в итоге перемещает задание в DLQ.

Архитектурное решение

Почему этот pattern?

Heartbeat - это lease freshness signal. Он дешевле и точнее, чем считать любое долгое задание мертвым после фиксированного timeout от start time.

Trade-offs

Heartbeat writes добавляют database traffic. Интервал должен быть достаточно длинным, чтобы не создавать write noise, и достаточно коротким, чтобы обнаруживать dead workers в рамках operational budget.

Рассмотренные альтернативы

AlternativeBenefitCost
Timeout только от StartedAtUtcПросто.Убивает здоровые долгие задания.
Только external worker registryХорошая видимость процесса.Не доказывает progress конкретного задания.
Без heartbeatМеньше database traffic.Dead workers оставляют processing rows неоднозначными.

Дополнительные вопросы

Почему heartbeat-expired jobs перемещаются в DLQ?
Потому что user code мог уже произвести side effects. Оператор должен решить, безопасен ли retry.

Какие timeout values опасны?
Значения, близкие к heartbeat interval. Обычный jitter или transient SQL latency могут сделать здоровые задания похожими на dead.

Какая метрика здесь важна?
chokaq.jobs.heartbeat_failures, плюс строки DLQ с FailureReason = Zombie.

Лицензия Apache 2.0