|
| 1 | +<!-- doc/src/xml/atx.xml --> |
| 2 | + |
| 3 | +<chapter id="atx"> |
| 4 | + <title>Автономные транзакции</title> |
| 5 | + |
| 6 | +<sect1 id="atx-overview"> |
| 7 | + <title>Обзор</title> |
| 8 | + |
| 9 | +<para>&productname; поддерживает вложенные транзакции: они редко явно используются программистами; чаще всего они применяются для обработки ошибок и хранимых процедур. Вложенную подтранзакцию можно откатить, никак не влияя на родительскую транзакцию, но её фиксирование откладывается до момента фиксирования родительской транзакции.</para> |
| 10 | +<para>Однако в некоторых случаях приложениям требуется выполнять в рамках одной транзакции несколько независимых транзакций — <quote>автономные транзакции</quote>. Автономные транзакции нужны в первую очередь для реализации аудита, когда факт попытки действия, подлежащего аудиту, должен быть зафиксирован независимо от того, будет ли это действие завершено успешно или нет.</para> |
| 11 | + |
| 12 | +</sect1> |
| 13 | + |
| 14 | +<sect1><title>Поведение</title> |
| 15 | +<para>Автономные транзакции могут выполняться только внутри другой транзакции. Если уже начата транзакция (назовём её T0), пользователь может решить начать подтранзакцию. При этом T0 приостанавливается, помещается в стек автономных транзакций, и начинается новая транзакция (назовём её T1).</para> |
| 16 | +<para>Затем в какой-то момент пользователь может зафиксировать автономную транзакцию; после того как T1 будет зафиксирована, T0 извлекается из стека автономных транзакций и продолжает выполняться. Пользователь может также выполнить <command>COMMIT</command> в родительской транзакции T0 — в этом случае сначала фиксируется T1, затем T0 извлекается из стека и фиксируется.</para> |
| 17 | +<para>Все эти транзакции происходят одновременно; в любой момент времени может быть активна только одна транзакция, тогда как в стеке находятся несколько (или ноль) приостановленных транзакций. При этом могут иметь место любые комбинации <command>COMMIT</command>/<command>ROLLBACK</command> с T0 и T1; например, можно выполнить COMMIT T1 и ROLLBACK T0. Одни автономные транзакции могут вкладываться в другие, насколько это позволяют глобальные ограничения ресурсов (например, размер стека автономных транзакций), которые могут быть установлены на сервере.</para> |
| 18 | + |
| 19 | +</sect1> |
| 20 | + |
| 21 | +<sect1><title>Примеры</title> |
| 22 | + |
| 23 | +<para>Этот пример показывает, как выполняется автономная транзакция. Непрерывная линия обозначает активную транзакцию, а пунктирная — транзакцию, которая была приостановлена и помещена в стек автономных транзакций. Течение времени направлено вниз.</para> |
| 24 | + |
| 25 | +<programlisting>BEGIN; -- начинается обычная транзакция T0 |
| 26 | +| |
| 27 | +INSERT INTO t VALUES (1); |
| 28 | +:\ |
| 29 | +: BEGIN AUTONOMOUS TRANSACTION; -- начинается автономная транзакция |
| 30 | +: | -- T1, транзакция T0 помещается в стек |
| 31 | +: | |
| 32 | +: INSERT INTO t VALUES (2); |
| 33 | +: | |
| 34 | +: COMMIT AUTONOMOUS TRANSACTION / ROLLBACK AUTONOMOUS TRANSACTION; |
| 35 | +: | -- завершается автономная транзакция |
| 36 | +: | -- T1, транзакция T0 извлекается из стека |
| 37 | +:/ |
| 38 | +COMMIT / ROLLBACK; -- завершается транзакция T0</programlisting> |
| 39 | + |
| 40 | +<para>В зависимости от выбора из <command>COMMIT</command> и <command>ROLLBACK</command> в двух местах, мы можем получить четыре разных результата из: <programlisting>SELECT sum(x) from t;</programlisting></para> |
| 41 | + |
| 42 | +<para>В одной родительской транзакции может быть несколько автономных транзакций, если приложение будет просто повторять цикл помещения/извлечения их из стека.</para> |
| 43 | + |
| 44 | +<programlisting>BEGIN; -- начинается обычная транзакция T0 |
| 45 | +| |
| 46 | +INSERT INTO t VALUES (1); |
| 47 | +:\ |
| 48 | +: BEGIN AUTONOMOUS TRANSACTION; -- начинается автономная транзакция |
| 49 | +: | -- T1, транзакция T0 помещается в стек |
| 50 | +: | |
| 51 | +: INSERT INTO t VALUES (2); |
| 52 | +: | |
| 53 | +: COMMIT AUTONOMOUS TRANSACTION / ROLLBACK AUTONOMOUS TRANSACTION; |
| 54 | +: | -- завершается автономная транзакция |
| 55 | +: | -- T1, транзакция T0 извлекается из стека |
| 56 | +:/ |
| 57 | +| |
| 58 | +:\ |
| 59 | +: BEGIN AUTONOMOUS TRANSACTION; -- начинается автономная транзакция |
| 60 | +: | -- T2, транзакция T0 помещается в стек |
| 61 | +: | |
| 62 | +: INSERT INTO t VALUES (4); |
| 63 | +: | |
| 64 | +: COMMIT AUTONOMOUS TRANSACTION / ROLLBACK AUTONOMOUS TRANSACTION; |
| 65 | +: | -- завершается автономная транзакция |
| 66 | +: | -- T2, транзакция T0 извлекается из стека |
| 67 | +:/ |
| 68 | +COMMIT / ROLLBACK; -- завершается транзакция T0</programlisting> |
| 69 | + |
| 70 | +</sect1> |
| 71 | + |
| 72 | +<sect1><title>Видимость</title> |
| 73 | + |
| 74 | +<para>Правила видимости действуют так же, как и с независимыми транзакциями, выполняемыми через <literal>dblink</literal> T1 не видит эффекта действия T0, как ещё не зафиксированной транзакции. T0 может видеть эффект действия T1, в зависимости своего собственного режима изоляции. Если выбран режим изоляции Read Committed, родительская транзакция будет видеть изменения, производимые автономными транзакциями, но в режиме Repeatable Read они не будут видны.</para> |
| 75 | + |
| 76 | +<para>При этом становятся возможны взаимоблокировки в одном сеансе, так как автономная транзакция может оказаться в спутанном состоянии с одной из приостановленных транзакций в сеансе. Автономная транзакций T1 считается зависимой от родительской транзакции T0, и если она пытается получить какой-либо ресурс, удерживаемый T0, происходит взаимоблокировка.</para> |
| 77 | + |
| 78 | +</sect1> |
| 79 | + |
| 80 | +<sect1><title>Расширение языка SQL для автономных транзакций</title> |
| 81 | + |
| 82 | +<para>Транзакционные операторы <literal>BEGIN</literal>/<literal>END</literal> в &productname; расширены необязательным ключевым словом <literal>AUTONOMOUS</literal>:</para> |
| 83 | + |
| 84 | +<programlisting> BEGIN [AUTONOMOUS] [TRANSACTION] [<replaceable>уровень-изоляции</replaceable>] |
| 85 | + END [AUTONOMOUS] [TRANSACTION]</programlisting> |
| 86 | + |
| 87 | +<para>Указывать ключевое слово <literal>AUTONOMOUS</literal> в предложении <literal>END TRANSACTION</literal> не обязательно. С автономными транзакциями возможно организовать несколько уровней вложенности, но транзакция верхнего уровня автономной быть не может.</para> |
| 88 | + |
| 89 | +</sect1> |
| 90 | + |
| 91 | +<sect1><title>Расширение языка PL/pgSQL для автономных транзакций</title> |
| 92 | + |
| 93 | +<para>Конструкция блока в PL/pgSQL расширена необязательным ключевым словом <literal>autonomous</literal>. Это позволяет выполнять всё тело функции как автономную транзакцию:</para> |
| 94 | + |
| 95 | +<programlisting>create function foo(x integer) returns integer as $$ |
| 96 | +begin autonomous |
| 97 | + return x; |
| 98 | +end; |
| 99 | +$$ language plpgsql;</programlisting> |
| 100 | + |
| 101 | +<para>или создать отдельный блок begin-end:</para> |
| 102 | + |
| 103 | +<programlisting>create or replace function myaudit() returns boolean as $$ |
| 104 | +begin autonomous |
| 105 | + begin autonomous |
| 106 | + insert into audit_schedule values ('new audit',now()); |
| 107 | + end; |
| 108 | + ... -- собственно произвести аудит |
| 109 | + return true; |
| 110 | +end; |
| 111 | +$$ language plpgsql;</programlisting> |
| 112 | + |
| 113 | +<para>Когда в блоке <command>BEGIN AUTONOMOUS</command> возникает исключение, автономная транзакция прерывается и начинается обычная процедура обработки исключения — стек поднимается до тех пор, пока это исключение не будет перехвачено каким-либо обработчиком. То есть исключения обрабатываются точно так же, как и с обычными подтранзакциями &productname;.</para> |
| 114 | +<para>Когда ошибка перехватывается конструкцией <literal>EXCEPTION</literal>, локальные переменные функции PL/pgSQL остаются в том же состоянии, в каком они были в момент ошибки, но все изменения в состоянии базы данных, произведённые данным блоком кода, откатываются назад.</para> |
| 115 | + |
| 116 | +</sect1> |
| 117 | + |
| 118 | +<sect1><title>Расширения языка PL/Python для автономных транзакций</title> |
| 119 | + |
| 120 | +<para>В дополнение к методу <varname>subtransaction</varname>, модуль PL/Python предоставляет новый метод <varname>autonomous</varname>, который можно использовать с предложением <varname>WITH</varname> для того, чтобы начать автономную транзакцию:</para> |
| 121 | + |
| 122 | +<programlisting>create or replace function pythonomous() returns void as $$ |
| 123 | + plpy.execute("insert into atx_test values ('asd', 123)") |
| 124 | + |
| 125 | + try: |
| 126 | + with plpy.autonomous(): |
| 127 | + plpy.execute("insert into atx_test values ('bsd', 456)") |
| 128 | + except plpy.SPIError, e: |
| 129 | + print("error: %s" % e.args) |
| 130 | + |
| 131 | + plpy.execute("insert into atx_test values ('csd', 'csd')") |
| 132 | +$$ language plpythonu;</programlisting> |
| 133 | + |
| 134 | +<para>Обработка исключений в автономных транзакциях на языке PL/Python осуществляется так же, как и в подтранзакциях.</para> |
| 135 | + |
| 136 | +</sect1> |
| 137 | +</chapter> |
0 commit comments