⚡Heartbeat pattern
Предположим у нас есть простая очередь задач
Чтобы брать задачи из этой очереди и гарантировать эксклюзивность, можно использовать стандартный подход с
Окей, попробуем не брать блокировку, сделаем что-то типа такого
Казалось бы все ок, но что если воркер, взявший задачу, умрет перед execute task? Таска навечно повиснет в статусе running и никто не будет с ней ничего делать
—
Ровно эту проблему решают хартбиты:
1. Воркер раз в n секунд пингует базу, что позволяет нам убедиться что воркер жив и имеет конекшн до базы
2. Супервизор наблюдает за воркерами: если какой-то воркер не пинговал базу m секунд, то снимаем с него все задачи
В итоге задача снимется с умершего воркера, и ее возьмет любой другой свободный
Предположим у нас есть простая очередь задач
create table task
(
id bigint not null,
status varchar not null,
data jsonb not null
);
Чтобы брать задачи из этой очереди и гарантировать эксклюзивность, можно использовать стандартный подход с
select for update
. Однако, это вынуждает нас держать блокировку и транзакцию на все время исполнения задачи. В итоге получаем long-running transactions, которые негативно влияют на перфоманс базыОкей, попробуем не брать блокировку, сделаем что-то типа такого
val task = tx {
get scheduled task with lock;
set task status running;
}
execute task;
set task status finished;
Казалось бы все ок, но что если воркер, взявший задачу, умрет перед execute task? Таска навечно повиснет в статусе running и никто не будет с ней ничего делать
—
Ровно эту проблему решают хартбиты:
1. Воркер раз в n секунд пингует базу, что позволяет нам убедиться что воркер жив и имеет конекшн до базы
2. Супервизор наблюдает за воркерами: если какой-то воркер не пинговал базу m секунд, то снимаем с него все задачи
В итоге задача снимется с умершего воркера, и ее возьмет любой другой свободный