Ответ 1
Да, это абсолютно возможно, что это может привести к более чем двум активным требованиям, поскольку параллельные транзакции не могут видеть изменения друг друга, поэтому два или более одновременных исполнения будут отображаться как две заявки, и оба приступают к обновлению своих целевых требований чтобы сделать их активными.
См. связанные: Операции с базами данных препятствуют условиям гонки.
Блокировка таблиц
Самый простой вариант - просто:
BEGIN;
LOCK TABLE claim IN EXCLUSIVE MODE;
UPDATE ...
COMMIT;
... но это довольно тяжелое решение.
Блокировка уровня строки на объекте пользователя
Предполагая, что у вас есть таблица user
для владельца претензии, вы должны:
SELECT 1 FROM user WHERE user_id = whatever FOR UPDATE
в той же транзакции, перед запуском UPDATE
. Таким образом, у вас будет эксклюзивная блокировка строки для пользователя, а другие операторы SELECT ... FOR UPDATE
блокируют вашу блокировку. Эта блокировка также блокирует UPDATE
и удаляет user
; он не будет блокировать обычный SELECT
пользователя без предложения FOR UPDATE
или FOR SHARE
.
См. явная блокировка в руководстве PostgreSQL.
SERIALIZABLE
Изоляция
Альтернативой является использование изоляции SERIALIZABLE
; PostgreSQL 9.2 и новее имеют обнаружение зависимостей транзакций, из-за которых все, кроме одной из конфликтующих транзакций, прерываются с ошибкой сериализации в приведенном выше примере. Поэтому ваше приложение должно помнить, что он пытался сделать, когда он запускает транзакцию, и может ловить ошибки, обнаруживать, что они являются сбоями в сериализации, и повторить попытку после сбоя в сериализации.
См. изоляция транзакций в руководстве PostgreSQL.
Контрольные блокировки
Иногда нет хорошего объекта-кандидата для блокировки строки, и по какой-либо причине или другой сериализуемой изоляции эта проблема не будет решена или не будет использоваться по другим причинам. Это не относится к вам, это просто общая информация.
В таких случаях вы можете использовать консультативные блокировки PostgreSQL для блокировки произвольных числовых значений; в этом случае вы бы, например, pg_advisory_xact_lock(active_claim.user_id)
. Явная глава блокировки содержит больше информации.