Heartbeat pattern



Предположим у нас есть простая очередь задач



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 секунд, то снимаем с него все задачи



В итоге задача снимется с умершего воркера, и ее возьмет любой другой свободный