Как войти в хранимую процедуру, которая инкапсулирована в транзакцию

#mysql #stored-procedures #transactions #mysql-5.5

#mysql #хранимые процедуры #транзакции #mysql-5.5

Вопрос:

Краткая версия: Если есть запущенная транзакция, которая, возможно, будет откатана, как можно что-то записать в таблицу журналов? (Откат транзакции также приведет к откату записей журнала, что является проблемой здесь.)

Длинная версия:

У меня есть хранимая процедура, давайте вызовем ее, sp_A которая вызывает другие хранимые процедуры, давайте вызовем их sp_B и sp_C . Для обеспечения согласованности внутри XA TRANSATCION запускается sp_A . Что я на самом деле хочу сделать, так это войти в систему, если выполнение sp_B или sp_C завершается неудачей, и если да, то какие аргументы были переданы сбойной хранимой процедуре. Но поскольку sp_B и sp_C выполняют некоторые вставки в некоторых таблицах, мне нужно выполнить, XA ROLLBACK если sp_B или sp_C произойдет сбой.

Итак, мой вопрос таков: как я могу войти в таблицу, находясь внутри транзакции (или как выполнить инструкцию insert вне области транзакции)?

Чтобы дать вам небольшой пример того, что я хочу сделать (я упростил код и переименовал большинство параметров / переменных, используемых для решения проблемы, я бы никогда не мечтал использовать такие имена параметров, как a , b или c ;-)):

Хранимая процедура sp_A выглядит следующим образом:

 CREATE PROCEDURE `sp_A`
(
  IN a INT,
  IN b VARCHAR(64),
  OUT c INT
)
sp_label:BEGIN
  DECLARE l_errorMessage TEXT;
  DECLARE l_spSuccess INT;
  DECLARE l_errorOccured INT DEFAULT 0;
  DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN
    SET l_errorOccured = 1;
  END;

  XA START 'sp_A';

  CALL sp_B(a, b, l_spSuccess);

  IF l_errorOccured OR (l_spSuccessResult < 0) THEN
    XA END 'sp_A';
    XA ROLLBACK 'sp_A';
    SET c = -1;
    SET l_errorMessage = CONCAT('CALL sp_B(', a, ', ', b, ', l_spSuccess);');
    CALL sp_Log(l_errorMessage);
    LEAVE sp_label;
  END IF;

  CALL sp_C(a, b, l_spSuccess);

  IF l_errorOccured OR (l_spSuccessResult < 0) THEN
    XA END 'sp_A';
    XA ROLLBACK 'sp_A';
    SET c = -2;
    SET l_errorMessage = CONCAT('CALL sp_C(', a, ', ', b, ', l_spSuccess);');
    CALL sp_Log(l_errorMessage);
    LEAVE sp_label;
  END IF;

  XA END 'sp_A';
  XA COMMIT 'sp_A';
  SET c = 1;
END;
  

Хранимая процедура sp_B выглядит следующим образом ( sp_C выглядит аналогично):

 CREATE PROCEDURE `sp_B`
(
  IN a INT,
  IN b VARCHAR(64),
  OUT c INT
)
sp_label:BEGIN
  DECLARE l_errorMessage TEXT;
  DECLARE l_spSuccess INT;
  DECLARE l_errorOccured INT DEFAULT 0;
  DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN
    SET l_errorOccured = 1;
  END;

  INSERT INTO `someTable` (`a`, `b`) VALUES (a, b);

  IF l_errorOccured THEN
    SET c = -1;
    SET l_errorMessage = CONCAT('INSERT INTO `someTable` (`a`, `b`) VALUES (', a, ', ', b, ');');
    CALL sp_Log(l_errorMessage);
    LEAVE sp_label;
  END IF;

  SET c = 1;
END;
  

sp_Log просто вставляет данные в таблицу, которую я хочу использовать как свою logging table .

Таким образом, на самом деле ведение журнала внутри sp_A работает просто отлично (потому что вызов sp_Log находится вне транзакции), но ведение журнала внутри sp_B или sp_C не будет работать, потому что при откате транзакции, запущенной в sp_A , вставки, выполненные вызовом sp_Log внутри sp_B или sp_C , конечно, отменяются.

(Я упростил пример в sp_B и sp_C . Происходит нечто большее, чем просто одна простая инструкция insert, и просто для большего удовольствия внутри sp_B and sp_C также запускаются и фиксируются / откатываются транзакции.)

Я был бы признателен за любые подсказки, которые вы могли бы мне дать. Спасибо!

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

1. Кто бы ни проголосовал против… Пожалуйста, дайте мне подробную информацию, почему вы это сделали, чтобы я мог улучшить вопрос…

2. Я попытался включить команды журнала в другую транзакцию (мой «main» эквивалентен вашему «sp_a»). Что-то вроде: ЕСЛИ _rollback , ТО XA ЗАВЕРШИТЕ «main»; XA ПОДГОТОВЬТЕ «main»; XA ВЫПОЛНИТЕ ОТКАТ «main»; XA ЗАПУСТИТЕ «log»; ВСТАВЬТЕ В logtable ( message ) ЗНАЧЕНИЯ (‘Rolled back’); XA ЗАВЕРШИТЕ «log»; XA ЗАФИКСИРУЙТЕ «log» ОДНУ ФАЗУ; ОСТАВЬТЕ this_procedure; ЗАВЕРШИТЕ IF; К сожалению, я сталкиваюсь с дополнительными, не связанными проблемами, поэтому я не могу объявить свое решение рабочим.

Ответ №1:

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