Как дождаться во время SELECT этого отложенного коммита INSERT?

Я использую PostgreSQL 9.2 в среде Windows. Я работаю в среде 2PC (двухэтапная фиксация) с использованием MSDTC.

У меня есть клиентское приложение, которое запускает транзакцию на уровне изоляции SERIALIZABLE, вставляет новую строку данных в таблицу для определенного значения внешнего ключа (в столбце есть индекс) и голосует за завершение транзакции (The сделка ПОДГОТОВЛЕНА). Транзакция будет СОВЕРШЕНА координатором транзакций.

Сразу после этого, вне транзакции, тот же клиент запрашивает все строки для этого же конкретного значения внешнего ключа.

Поскольку перед действительной фиксацией предыдущей транзакции может возникнуть задержка, предложение SELECT может вернуть предыдущий снимок данных. На самом деле это иногда случается, и это проблематично. Конечно, приложение может быть переработано, но до тех пор я ищу решение для блокировки. Консультативная блокировка?

Я уже решил проблему, выполняя UPDATE для определенных строк, а затем используя SELECT... FOR SHARE, и это работает хорошо. SELECT ждет, пока транзакция не зафиксируется и не вернет старые и новые строки.

Теперь я пытаюсь решить это для INSERT. SELECT...FOR SHARE не блокирует и не возвращает сразу.

Здесь нет проблемы параллелизма, поскольку только один клиент имеет дело с определенным набором строк. Я уже знаю о MVCC.

Любая помощь приветствуется.


person Olivier MATROT    schedule 08.07.2013    source источник
comment
Вы пробовали SELECT...FOR UPDATE?   -  person Denis de Bernardy    schedule 08.07.2013
comment
@ Денис Да, тот же результат.   -  person Olivier MATROT    schedule 08.07.2013


Ответы (1)


Чтобы дождаться еще не зафиксированного INSERT, вам потребуется предикатная блокировка. В PostgreSQL есть ограниченная блокировка предикатов для сериализуемой поддержки, но она не предоставляется непосредственно пользователю.

Простая SERIALIZABLE изоляция здесь не поможет, потому что SERIALIZABLE требует только наличия порядка, в котором транзакции могли выполняться для получения согласованного результата. В вашем случае этот порядок SELECT, за которым следует INSERT.

Единственный вариант, который я могу придумать, это взять ACCESS EXCLUSIVE блокировку таблицы перед INSERTing. Это будет освобождено только в COMMIT PREPARED или ROLLBACK PREPARED времени, а тем временем любые другие запросы будут ждать блокировки. Вы можете применить это с помощью триггера BEFORE, чтобы избежать необходимости менять приложение. Вы, вероятно, получите нечетную взаимоблокировку и откат, если сделаете это таким образом, поскольку INSERT возьмет более низкую блокировку, тогда вы попытаетесь повысить блокировку в триггере. Если возможно, лучше запустить команду LOCK TABLE ... IN ACCESS EXCLUSIVE MODE перед командой INSERT.

Как вы уже упоминали, это в основном проблема неправильного дизайна приложения. Ожидание увидеть еще не зафиксированные строки на самом деле не имеет никакого смысла.

person Craig Ringer    schedule 08.07.2013
comment
Если все другие запросы будут ждать и никогда не завершатся ошибкой сериализации (ошибка 40001), это может сработать. - person Olivier MATROT; 08.07.2013
comment
@omatrot Так и должно быть, но они вполне могут потерпеть неудачу с 40P01 Deadlock Detected, если вы попытаетесь сделать это с помощью триггеров, поскольку у вас будет два tx, каждый из которых имеет блокировку младшего разряда для INSERT, и каждый из которых требует блокировка ACCESS EXCLUSIVE, для которой другой блокирует блокировку. Однако это проблема только в том случае, если вы INSERT выполняете более одной транзакции одновременно. - person Craig Ringer; 08.07.2013
comment
Я возьму LOCK TABLE... перед маршрутом INSERT, у меня может быть несколько транзакций одновременно. Я буду держать вас в курсе о прогрессе. - person Olivier MATROT; 08.07.2013
comment
@omatrot Просто помните, что LOCK не выпускается до тех пор, пока транзакция не COMMITs или не сделает ROLLBACK. Нет руководства UNLOCK. Таким образом, вы захотите, чтобы ваши транзакции были как можно короче и делали в них как можно меньше, если вы собираетесь выполнять полную блокировку таблицы. Вы не получите никакой выгоды от нескольких рабочих потоков, поскольку вы заставляете все вставки выполняться последовательно. Если подумать, вы можете избежать явного LOCK TABLE ... IN ACCESS EXCLUSIVE MODE перед SELECT, который должен видеть согласованные данные и не беспокоиться о ручной блокировке перед INSERT. - person Craig Ringer; 09.07.2013
comment
Проблема в том, что 2-фазная фиксация не является 3-фазной фиксацией. После фиксации prepare+final менеджер ресурсов (удаленная база данных) отсоединяется от транзакции, так как его работа завершена. И может участвовать в новых (даже распределенных) транзакциях. Другие RM могут фиксировать (локально) после первого RM, а координатор (монитор транзакций) всегда завершает свою транзакцию после поступления последнего зафиксированного сообщения. избежать такого рода призраков было бы запретить доступ к RM без координатора посередине. (Источник: Gray&Reuter 10.4) - person joop; 12.07.2013
comment
@CraigRinger Я перестал использовать этот метод, так как обнаружил, что, поскольку pg_dump использует AccessSharedLock в базе данных, получение блокировки может занять некоторое время для завершения резервного копирования :-( - person Olivier MATROT; 07.01.2014
comment
@omatrot Да. На самом деле вы должны иметь возможность использовать блокировку EXCLUSIVE, а не ACCESS EXCLUSIVE. Это по-прежнему блокирует все записи, но не блокирует чтение или pg_dump. - person Craig Ringer; 08.01.2014