Собственно блокировка: pg_try_advisory_lock()
Теперь модифицируем наш запрос так, чтобы при его запуске на нескольких машинах результаты никогда не пересекались. Это делается так (псевдо-операция @IDS := ... означает, что данные нужно сохранить в некоторый массив вызывающего скрипта):/* Исходный запрос: получаем элементы-кандидаты на обработку. */ @IDS := ARRAY( SELECT id FROM subscription WHERE last_mail_at < MAILING_STARTED_AT AND subscription_type = <тип подписки> AND pg_try_advisory_lock(tableoid::INTEGER, id) /* вот она, блокировка! */ LIMIT 100 ); /* Пост-проверка: отсеиваем тех, кто успел пометиться обработанным за */ /* время работы предыдущего запроса (см. ниже подробности про это). */ @IDS := ARRAY( SELECT id FROM subscription WHERE last_mail_at < MAILING_STARTED_AT AND id IN (@IDS) );
Функция pg_try_advisory_lock() пытается "повесить" исключительную блокировку на некоторый
"виртуальный" идентификтор, который описывается парой ее параметров. Если этот идентификатор
уже заблокирован, она ничего не делает и возвращает false. Если же блокировку удалось
установить, то функция возвращает true. Блокировка снимается, когда вызывается
pg_advisory_unlock(), либо когда клиент отсоединяется от базы данных.