Исключения в PostgreSQL (deadlock) в хранимой процедуре

«Пост-Грэс-Кью-Эл» свободная объектно-реляционная СУБД. Обмениваемся вопросами и ответами о её работе.
Ответить
ORA600
Сообщения: 3
Зарегистрирован: Ср июн 01, 2022 5:17 pm

Исключения в PostgreSQL (deadlock) в хранимой процедуре

Сообщение ORA600 »

Я хочу переписать процедуру на PostgreSQL с Oracle.

Код: Выделить всё


set define off;

CREATE TABLE hr.t1 (c1 NUMBER PRIMARY KEY, c2 VARCHAR2(50));
INSERT INTO hr.t1 (c1) VALUES(1);
INSERT INTO hr.t1 (c1) VALUES(2);


CREATE OR REPLACE EDITIONABLE PROCEDURE "HR"."DEADLOCK_EX1" IS 
 deadlock			EXCEPTION;
 PRAGMA EXCEPTION_INIT(deadlock,-60);
BEGIN
 UPDATE t1 SET c2 = 'str 1, session 1' where c1=1;
 dbms_output.put_line(sysdate); 
 dbms_lock.sleep(30); 
 insert into t1 values(3,'str 3, session 1'); --- test case
 dbms_lock.sleep(30); 
 UPDATE t1 SET c2 = 'str 2, session 1' where c1=2;
 COMMIT;
 EXCEPTION WHEN deadlock then commit;
END;

/

create or replace PROCEDURE    deadlock_ex2 IS 
 deadlock			EXCEPTION;
 PRAGMA EXCEPTION_INIT(deadlock,-60);
BEGIN

 UPDATE t1 SET c2 = 'str 2, session 2' where c1=2;
 insert into t1 values(4,'str 4, session 2'); --- test case
 UPDATE t1 SET c2 = 'str 1, session 2' where c1=1;

 COMMIT;
 EXCEPTION WHEN deadlock then commit; -- without exception - commit for all block 
END;

Тут ясно, что есть ловля deadlock,
и когда deadlock происходит, оказываются зафиксированными все записи в двух процедурах,
какие они сделали. То есть в одной сессии запускаем одну процедуру, в другой другую,
данные фиксируется для двух сессий после дедлока (на момент исключения).

Код: Выделить всё



CREATE OR REPLACE PROCEDURE    hr.DEADLOCK_EX3()
LANGUAGE plpgsql
   AS $$
BEGIN
        BEGIN
--- SET         TRANSACTION ISOLATION LEVEL REPEATABLE READ;
   UPDATE hr.T1 SET C2 = 'str 1, session 1' where C1 = 1;
   RAISE NOTICE '%',LOCALTIMESTAMP;
   PERFORM pg_sleep(30);
   insert into hr.T1  values(3,'str 3, session 1'); --- test case

   PERFORM pg_sleep(30);
   UPDATE hr.T1 SET C2 = 'str 2, session 1' where C1 = 2;
   -- COMMIT
--   RETURN;
-- COMMIT;
   EXCEPTION WHEN deadlock_detected then
      COMMIT; -- COMMIT
END;
 -- without exception - commit for all block
END; $$;


CREATE OR REPLACE PROCEDURE  hr.DEADLOCK_EX4()
LANGUAGE plpgsql
   AS $$
BEGIN
        -- SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
        BEGIN
   UPDATE hr.T1 SET C2 = 'str 2, session 2' where C1 = 2;
   insert into hr.T1  values(4,'str 4, session 2'); --- test case

   UPDATE hr.T1 SET C2 = 'str 1, session 2' where C1 = 1;

--  COMMIT;
--RETURN;
   EXCEPTION WHEN deadlock_detected then
      COMMIT; -- COMMIT
 -- without exception - commit for all block
 END;
END; $$;
Аналогичные попытки сделать то же самое на PostgreSQL к успеху не привели, как при

\set ON_ERROR_ROLLBACK off

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

Вопрос такой:

следует ли вообще пытаться управлять в PostgreSQL системными исключениями по типу deadlock через deadlock_detected и подобное? Или
следует искать обходное решение, то есть ловить системное исключение вручную, например, через системное ожидание?
Naeel Maqsudov
Сообщения: 20
Зарегистрирован: Пн июн 06, 2022 10:59 am
Откуда: Moscow
Контактная информация:

Re: Исключения в PostgreSQL (deadlock) в хранимой процедуре

Сообщение Naeel Maqsudov »

Тут ясно, что есть ловля deadlock,
и когда deadlock происходит, оказываются зафиксированными все записи в двух процедурах,
какие они сделали.
Простите, но это какая-то ерунда... Допустим, что deadlock возник на первом апдейте, этот оператор откатывается и Oracle переходит к обработке исключений, где фиксируется всё, что эта сессия делала до вызова процедуры. (Т.е. ровно 0 записей, обработанных этой конкретной процедурой). Я не знаю, о какой «другой» процедуре идёт речь, если об этой же самой вызванной из другой сессии, то даже если они вызваны одновременно, тут нет deadlock-ов. Мне кажется, Вы не совсем понимаете разницу между lock и deadlock.
ORA600
Сообщения: 3
Зарегистрирован: Ср июн 01, 2022 5:17 pm

Re: Исключения в PostgreSQL (deadlock) в хранимой процедуре

Сообщение ORA600 »

Там две процедуры, в исходном коде каждая из них вызывается из отдельной сессии.
Мы говорим про сохранение inserts после ловли deadlock на update.
Сделал так для простоты.

Про ноль записей тоже неверно предположение, так как там до этого как был insert.

Еще варианты? Размышления?
Naeel Maqsudov
Сообщения: 20
Зарегистрирован: Пн июн 06, 2022 10:59 am
Откуда: Moscow
Контактная информация:

Re: Исключения в PostgreSQL (deadlock) в хранимой процедуре

Сообщение Naeel Maqsudov »

Про ноль записей тоже неверно предположение
Это не предположение, а знание.

Код: Выделить всё

EXCEPTION WHEN deadlock then commit; -- without exception - commit for all block 
Вот тут ↑ ошибка. Это так не работает.

Процедура deadlock_ex2 успешно делает 3 DML-оператора в транзакции своей сессии и вслед за ними делает COMMIT.

На этом комите возникает исключение и управление передаётся в секцию EXCEPTION и там опять выполняется COMMIT, который приведёт к тому же самому исключению, потому что причина исключения для этой сессии и этой транзакции не устранена. Копит не выполнится только потому, что мы пытаемся выполнить его 2 раза.

В результате вся процедура завершится необработанным исключением, а транзакция останется не завершена, и зкомиченых изменений нет.
Naeel Maqsudov
Сообщения: 20
Зарегистрирован: Пн июн 06, 2022 10:59 am
Откуда: Moscow
Контактная информация:

Re: Исключения в PostgreSQL (deadlock) в хранимой процедуре

Сообщение Naeel Maqsudov »

Вообще, код, могущий приводить к deadlock-ам, это ошибка проектирования. Её надо исправлять, а не переносить из Ora в PG.
Между прочим, исключения -00060 (deadlock) при использовании конкретно этих процедур вообще не происходит. Либо Вы приводите нерелевантный для Вашей задачи код, либо путаете lock и deadlock.
AntonS
Сообщения: 52
Зарегистрирован: Пт июн 03, 2022 8:51 am

Re: Исключения в PostgreSQL (deadlock) в хранимой процедуре

Сообщение AntonS »

Очень верно замечено, ORA-00060: deadlock detected это программная ошибка, её необходимо исправлять. В поиске находятся известная рекомендации Тома Кайта на этот счет:

1. Если в нескольких сессиях транзакции одновременно модифицируют одни и те же строки, и при этом порядок блокирования строк пересекается, то только в одной из них выполнится коммит, остальные гарантированно получат ORA-00060.
2. Даже если Вы уверены что пакетная модификация строк реализована корректно и модификаций одних и тех же строк нет, а дедлоки продолжаются, то надо брать трейсы дедлоков Oracle и анализировать причины такого поведения.
3. Возможно, эти строки модифицирует кто-то еще кроме Вас, например какой-то неучтенный джоб.
Последний раз редактировалось AntonS Пт апр 28, 2023 9:37 pm, всего редактировалось 2 раза.
AntonS
Сообщения: 52
Зарегистрирован: Пт июн 03, 2022 8:51 am

Re: Исключения в PostgreSQL (deadlock) в хранимой процедуре

Сообщение AntonS »

Насколько известно, Oracle выдаёт сеансу-жертве deadlock сообщение об ошибке, но не откатывает всю транзакцию.
Интересно, что PostgreSQL ведет себя иначе. Как показывает следующий пример, после deadlock закоммитить транзакцию в PostgreSQL не получится:

Код: Выделить всё

CREATE TABLE t1(id int GENERATED ALWAYS AS IDENTITY, text_data TEXT);
INSERT INTO t1(text_data) VALUES ('arbitrary text');
INSERT INTO t1(text_data) VALUES ('arbitrary text');
В сессии #1

Код: Выделить всё

# BEGIN;
BEGIN
#  UPDATE t1 SET text_data='useful data' WHERE id=1;
UPDATE 1
В сессии 2

Код: Выделить всё

BEGIN;		
	INSERT INTO t1(text_data) VALUES ('arbitrary text');
	UPDATE t1 SET text_data='useful data' WHERE id=2;
UPDATE 1
Снова в сессии 1

Код: Выделить всё

*# UPDATE t1 SET text_data='useful data' WHERE id=2;
UPDATE 1
Сессия 1 повисла на ожидании блокировки строки.

Наконец, в сессии 2

Код: Выделить всё

*# UPDATE t1 SET text_data='useful data' WHERE id=1;
ERROR:  40P01: deadlock detected
DETAIL:  Process 2001171 waits for ShareLock on transaction 860357335; blocked by process 2003123.
Process 2003123 waits for ShareLock on transaction 860357336; blocked by process 2001171.
HINT:  See server log for query details.
CONTEXT:  while updating tuple (0,1) in relation "t1"
LOCATION:  DeadLockReport, deadlock.c:1174
!# COMMIT;
ROLLBACK
Как видим, на команду COMMIT база данных PostgreSQL отвечает ROLLBACK, транзакция не подтверждается и откатывается.
pluzanov
Сообщения: 27
Зарегистрирован: Пт июл 08, 2022 4:43 pm

Re: Исключения в PostgreSQL (deadlock) в хранимой процедуре

Сообщение pluzanov »

AntonS писал(а): Вт апр 25, 2023 8:34 am Как видим, на команду COMMIT база данных PostgreSQL отвечает ROLLBACK, транзакция не подтверждается и откатывается.
Всё верно. PostgreSQL не умеет откатывать результат работы одной команды, в которой произошла ошибка. Поэтому сбой в любой команде транзакции приведет к отмене всей транзакции.
Аватара пользователя
SQL*Plus
Сообщения: 173
Зарегистрирован: Ср апр 20, 2022 1:09 pm

Re: Исключения в PostgreSQL (deadlock) в хранимой процедуре

Сообщение SQL*Plus »

pluzanov писал(а): Пт апр 28, 2023 12:27 pm PostgreSQL не умеет откатывать результат работы одной команды, в которой произошла ошибка.
Поэтому сбой в любой команде транзакции приведет к отмене всей транзакции.
Очень ценное замечание!
Мы - оракулисты - сразу приземлились, поскольку даже не думали, что так может быть...

Спасибо!
Ответить