Я пишу веб-приложение, и я экспериментировал с переносом операторов SQL из каждого веб-запроса с транзакцией с ISOLATION LEVEL REPEATABLE READ
, чтобы определить, где мое веб-приложение может выполнять неповторяемые операции чтения. Мой план состоял не в том, чтобы повторить попытку в случае неповторяемого чтения, а просто сообщить пользователю об ошибке на стороне сервера (500) и записать информацию (так как я ожидаю, что это будет очень редко).
В то же время в моем коде есть места, где я использую явную блокировку (SELECT ... FOR UPDATE
), чтобы убедиться, что я правильно сериализовал доступ и не вызывал неповторимых чтений.
Однако объединение двух идей дает мне неожиданные результаты.
Ниже приведен минимальный пример :
+--------------------------------------------------+--------------------------------------------------+
| Session 1 | Session 2 |
+--------------------------------------------------+--------------------------------------------------+
| BEGIN; | |
| SET TRANSACTION ISOLATION LEVEL REPEATABLE READ; | |
| SELECT * FROM users WHERE id = 1 FOR UPDATE; | |
| (returns as expected) | |
+--------------------------------------------------+--------------------------------------------------+
| | BEGIN; |
| | SET TRANSACTION ISOLATION LEVEL REPEATABLE READ; |
| | SELECT * FROM users WHERE id = 1 FOR UPDATE; |
| | (blocks as expected) |
+--------------------------------------------------+--------------------------------------------------+
| UPDATE users SET name = 'foobar' WHERE id = 1; | |
| COMMIT; | |
| (works as expected) | |
+--------------------------------------------------+--------------------------------------------------+
| | ERROR: could not serialize access due |
| | to concurrent update |
+--------------------------------------------------+--------------------------------------------------+
Мое ожидание заключается в том, что, поскольку сеанс 2 не выполнял никаких операций чтения до этого оператора SELECT
, и поскольку этот оператор возвращается только после того, как сеанс 1 завершил свое обновление, тогда сеанс 2 должен увидеть обновленное версия таблицы, и это сделало бы ее повторяемой.
Я полагаю, что, скорее всего, Postgres принимает версию при запуске BEGIN
, а не при получении блокировки для первого SELECT
.
Мои вопросы :
- Правильно ли мое понимание?
- Есть ли способ заставить Postgres вести себя так, как я ожидаю?
- Это будет считаться ошибкой, или работает, как задумано ?