Блокировка «выбрать» в обновлении … из select — postgresql

#sql #postgresql #concurrency #locking

Вопрос:

У меня есть унаследованный код со следующим:

 const sql = `UPDATE fqdn SET lock = $1
                    FROM (SELECT id FROM fqdn WHERE region = $2 AND lock IS NULL AND expires < $3 OR lock = $1 LIMIT $4) AS expired
                    WHERE fqdn.id = expired.id
                    RETURNING fqdn.id, fqdn.config, fqdn.data`;
 

Существует несколько микросервисов, выполняющих этот запрос в одной и той же базе данных.
Идея создателя состоит в том, чтобы заблокировать пакет строк, с которыми будет работать каждая микросервисная служба, полем блокировки, которое является полным доменным именем ($1 => полное доменное имя) машины, на которой запущена микросервисная служба.

Похоже, однако, что иногда два микросервиса могут работать над одним и тем же пакетом. Есть ли способ сериализовать его так, чтобы параллельный второй поток выполнял выбор только после того, как первый завершил обновление блокировки?

Спасибо

Ответ №1:

В запросе действительно есть условие гонки. Два одновременных выполнения могут найти одну и ту же строку в подзапросе, и, хотя одно из обновлений будет заблокировано, в конечном итоге оно продолжит обработку, когда завершится другая транзакция.

Вы должны добавить следующее в подзапрос:

 FOR NO KEY UPDATE SKIP LOCKED
 

Это гарантировало бы, что никакие два запроса не смогут возвращать одни и те же строки.

Комментарии:

1. Спасибо, Лоренц, похоже, это хорошо работает