Начнем с определения того, что такое автономные транзакции в принципе:
Автономные транзакции позволяют создавать новые подтранзакции (subtransaction), которые могут сохранять или отменять изменения вне зависимости от родительской транзакции. Подробнее тут.
Реализованы они в СУБД Oracle, и являются очень гибким и удобным средством. Самым популярным (но далеко не единственным) использованием автономных транзакций является логирование. Рассмотрим несложный пример. Представьте себе следующую ситуацию — в вашей БД реализована длинная и сложная хранимая процедура, например ежемесячный биллинг. Важные события вы наверняка хотели бы видеть в таблице логов. Вы, конечно, можете просто писать в нее безо всяких ухищрений. Но есть один серьезный недостаток — если в процедуре происходит необработанная ошибка — транзакция откатывается вместе со всеми записями в таблицу логов. Тут на помощь и приходят автономные транзакции. Они выполняются независимо от родительской транзакции, и вы можете увидеть записи в таблице логов еще до того, как закончилось выполнение логгируемой процедуры биллинга.
Перейдем же к теме топика. Несмотря на претенциозный заголовок, в PostgreSQL автономных транзакций не существует. Но есть способ, с помощью которого мы можем получить схожий результат.
Во-первых, нам понадобится contrib-модуль dblink. Этот модуль позволяет с помощью своих функций обращаться к другому экземпляру PostgreSQL. Подробное описание выходит за рамки топика. От себя скажу, что dblink — один из нескольких полезнейших contrib-модулей, которые я всегда устанавливаю независимо от задач, решаемых БД.
Покажу описываемый прием на примере из начала топика. Реализуем логирование на «автономных» транзакциях.
Создаем таблицу логов:
Создадим функцию для логгирования:
Теперь в любой момент мы можем вызвать функцию «log» и запись тут же появится в таблице логов независимо от статуса транзакции, в во время которой мы ее вызвали.
Как видите, весь прием состоит в том, что мы записываем в таблицу логов с помощью функции «dblink_exec». Это означает, что создается новые соединение, сессия и транзакция, в контексте которых и происходит запись.
Автономные транзакции позволяют создавать новые подтранзакции (subtransaction), которые могут сохранять или отменять изменения вне зависимости от родительской транзакции. Подробнее тут.
Реализованы они в СУБД Oracle, и являются очень гибким и удобным средством. Самым популярным (но далеко не единственным) использованием автономных транзакций является логирование. Рассмотрим несложный пример. Представьте себе следующую ситуацию — в вашей БД реализована длинная и сложная хранимая процедура, например ежемесячный биллинг. Важные события вы наверняка хотели бы видеть в таблице логов. Вы, конечно, можете просто писать в нее безо всяких ухищрений. Но есть один серьезный недостаток — если в процедуре происходит необработанная ошибка — транзакция откатывается вместе со всеми записями в таблицу логов. Тут на помощь и приходят автономные транзакции. Они выполняются независимо от родительской транзакции, и вы можете увидеть записи в таблице логов еще до того, как закончилось выполнение логгируемой процедуры биллинга.
Перейдем же к теме топика. Несмотря на претенциозный заголовок, в PostgreSQL автономных транзакций не существует. Но есть способ, с помощью которого мы можем получить схожий результат.
dblink
Во-первых, нам понадобится contrib-модуль dblink. Этот модуль позволяет с помощью своих функций обращаться к другому экземпляру PostgreSQL. Подробное описание выходит за рамки топика. От себя скажу, что dblink — один из нескольких полезнейших contrib-модулей, которые я всегда устанавливаю независимо от задач, решаемых БД.
Пример
Покажу описываемый прием на примере из начала топика. Реализуем логирование на «автономных» транзакциях.
Создаем таблицу логов:
CREATE TABLE "public"."logs" (
"log_id" BIGSERIAL,
"source" TEXT NOT NULL,
"level" TEXT NOT NULL,
"message" TEXT NOT NULL,
"time_added" TIMESTAMP WITHOUT TIME ZONE DEFAULT now() NOT NULL,
CONSTRAINT "logs_pkey" PRIMARY KEY("log_id")
) WITH OIDS;
* This source code was highlighted with Source Code Highlighter.
Создадим функцию для логгирования:
CREATE OR REPLACE FUNCTION "public"."log"(IN p_source text, IN p_level text, IN p_message text)
RETURNS void AS
$$
DECLARE
INSERT_SQL text := 'INSERT INTO public.logs (source, level, message) VALUES (''[SOURCE]'',''[LEVEL]'',''[MESSAGE]'')';
BEGIN
IF p_level != 'ERROR' AND p_level != 'WARNING'
AND p_level != 'INFO' AND p_level != 'DEBUG' THEN
RAISE EXCEPTION 'Log level should be one of the following types: ERROR, WARNING, INFO, DEBUG';
END IF;
INSERT_SQL := replace(INSERT_SQL, '[SOURCE]', p_source);
INSERT_SQL := replace(INSERT_SQL, '[LEVEL]', p_level);
INSERT_SQL := replace(INSERT_SQL, '[MESSAGE]', p_message);
PERFORM dblink_exec('dbname=' || current_database(), INSERT_SQL);
END;
$$ LANGUAGE 'plpgsql';
* This source code was highlighted with Source Code Highlighter.
Теперь в любой момент мы можем вызвать функцию «log» и запись тут же появится в таблице логов независимо от статуса транзакции, в во время которой мы ее вызвали.
PERFORM "public"."log"('monthly billing', 'INFO', 'Starting monthly billing');
* This source code was highlighted with Source Code Highlighter.
Как видите, весь прием состоит в том, что мы записываем в таблицу логов с помощью функции «dblink_exec». Это означает, что создается новые соединение, сессия и транзакция, в контексте которых и происходит запись.
Замечания
- К сожалению, этот финт не может полностью заменить автономных транзакций Oracle. Проблемой является производительность — я бы не советовал использовать этот прием налево и направо, подумайте — где это действительно нужно.
- Подобный метод применим не только к PostgreSQL. Каждая известная мне СУБД располагает функциями, подобными «dblink_exec»
- Как грамотно заметили в комментариях — использование dblink_connect для создания persistent соединения, и использование этого соединения в дальнейшем, ощутимо ускорит работу.