Перейти к основному содержимому

Механизм переключения режимов транзакций Pangolin

В СУБД Pangolin реализован механизм, обеспечивающий установку режима транзакций для всего Pangolin при помощи функции с ролевым ограничением.

Ранее управление режимом «только чтение» транзакций по умолчанию осуществлялось при помощи GUC-параметра default_transaction_read_only. Данный параметр относится к параметрам клиентского сеанса (соединения). Когда параметр активен, все новые транзакции сеанса по умолчанию получают характеристику при помощи SET TRANSACTION READ ONLY. При этом каждый пользователь мог менять режим транзакций в своем сеансе, меняя значение параметра или настраивая текущую транзакцию при помощи SET TRANSACTION READ WRITE.

Эта возможность не позволяла гарантировать одинаковый режим транзакций для множества сессий Pangolin, не разграничивала права на его переключение и не позволяла выдать части пользователей право на запись при действующем для остального экземпляра режиме «только чтение», в отличие от текущего реализованного решения.

Сведения

Функциональность доступна только для редакций Enterprise и Enterprise для ERP-систем.

Настройка

Реализованный механизм представляет из себя выполнение следующих пунктов:

  1. Добавление функции psql_set_transaction_mode для переключения RO в RW и обратно (RO – только чтение, RW – чтение и запись). Режим транзакционности, возникший в результате вызова функции, распространяется на все базы данных кластера.
  2. Добавление списка пользователей, которые имеют права на вызов psql_set_transaction_mode. Список реализован в виде GUC-параметра set_xact_mode_users.
  3. Добавление списка пользователей, которые имеют права на выполнение пишущих транзакций. Список реализован в виде GUC-параметра set_rw_users.
  4. Добавление представления psql_transaction_mode_history, в котором хранится история вызовов функции psql_set_transaction_mode.
  5. Добавление параметра transaction_mode_max_records, который регулирует максимальное количество записей в представлении psql_transaction_mode_history.

Описание данных объектов и примеры управления механизмом при помощи них описаны в подразделах далее.

Конфигурационные параметры

Параметр

Тип данных

Значение по умолчанию

Применение

Описание

set_xact_mode_users

string

Пусто

Изменение значения данного параметра доступно только суперпользователям

Имена пользователей разделяются символом , (запятая)

Список пользователей, которые будут иметь права на вызов psql_set_transaction_mode

set_rw_users

string

Пусто

Изменение значения данного параметра доступно только суперпользователям

Список пользователей, которые будут иметь права на выполнение пишущих транзакций

transaction_mode_max_records

integer

100000 (минимальное 0, максимальное 100000)

Изменение значения данного параметра доступно только суперпользователям и с выполнением перезапуска сервера

Максимальное количество записей в представлении psql_transaction_mode_history

Объекты БД

Функция

Название функции

Описание

Входные параметры

Возвращаемое значение

Примечание

psql_set_transaction_mode

Переключение режимов RO в RW и обратно.

Режим транзакционности, возникший в результате вызова функции, распространяется на все базы данных кластера

mode (обязательный параметр) – определяет режим, в который нужно переключить базу, и может принимать значения RO или RW (не зависит от регистра);

timeout (обязательный параметр) – задает в миллисекундах время ожидания завершения всех пишущих транзакций. Параметр может принимать значение -1, что означает бесконечное ожидание завершения всех пишущих транзакций, и 0 – немедленное завершение всех пишущих транзакций, или любое целое число (максимальное значение не ограничено)

text

При переключении от режима только чтение в режим чтение-запись, переход мгновенный. Параметр timeout необходимо передавать всегда, даже если происходит переключение в режим RW, иначе вызов функции закончится ошибкой

Представление

Представление psql_transaction_mode_history возвращает следующие поля информации:

Название поляВозвращаемое значениеОписание
modetextТекущее состояние БД (выбранный режим)
startedtimestamp with time zoneДата и время начала транзакции
endedtimestamp with time zoneДата и время начала завершения транзакции
sourcetextПричина наступления режима (старт СУБД или вызов функции)
pendingbooleanСледующее состояние
function_calledtimestamp with time zoneДата и время вызова функции для переключения в следующее состояние
timeoutintegerЗапрошенный тайм-аут завершения текущих RW транзакций

Управление

Управление механизмом переключения режимов

Управление осуществляется с помощью функции psql_set_transaction_mode, которая переключает режимы RO, RW. При переключении из режима RW в RO происходит прерывание пишущих транзакций, при этом сессии не останавливаются. Вызывающая функцию сессия блокируется до завершения всех пишущих транзакции. Если происходит вызов функции в одной сессии, то в других сессиях вызов функции блокируется (mutual exclusive).

примечание

Если пишущая транзакция не может быть остановлена штатными средствами, то будет остановлена вся сессия. Соответствующая информация будет записана в системный лог.

Если режим успешно переключен после вызова функции, то выводится сообщение об успешном переключении режима уровня WARNING и функция возвращает текущий режим (RO либо RW). Если пользователю не доступна эта функция, то попытка вызова завершается ошибкой, функция ничего не возвращает.

Пример вызова функции psql_set_transaction_mode:

SELECT psql_set_transaction_mode('RO', 100);
WARNING:  transaction mode successfully set to RO with 0 of aborted transactions

psql_set_transaction_mode
-----------------------
RO
(1 row)

При вызове функции psql_set_transaction_mode с параметром mode='RW' всегда необходимо указывать параметр timeout (иначе вызов функции закончится ошибкой), хотя переключение в режим RW происходит немедленно, без ожидания завершения текущих транзакций.

Пример вызова функции psql_set_transaction_mode с параметрами mode=RW, timeout=100:

SELECT psql_set_transaction_mode('RW', 100);
WARNING:  transaction mode successfully set to RW

psql_set_transaction_mode
-----------------------
RW
(1 row)

Вызов функции psql_set_transaction_mode выводит предупреждение, если переключение режима не требуется, функция возвращает текущий режим (RW/RO):

SELECT psql_set_transaction_mode('RO', 200);
WARNING:  current mode is "RO" (read only), nothing to do

psql_set_transaction_mode
----------------------
RO
(1 row)

Просмотреть текущий режим можно вспомогательной функцией psql_get_transaction_mode, которая возвращает RW (текущий режим чтение-запись) либо RO (текущий режим только чтение). Функция доступна любому пользователю:

SELECT psql_get_transaction_mode();
 psql_get_transaction_mode
---------------------------
RO
(1 row)

Вызывать функцию psql_set_transaction_mode могут пользователи, которые перечислены в специальном списке (подробнее смотрите раздел «Управление списком пользователей, имеющих права на переключение режимов»).

При вызове функции, в лог базы данных пишется сообщение с уровнем WARNING:

  • в какой режим выполнено переключение;
  • результат операции;
  • количество прерванных транзакций.

Информация о текущем режиме и история переключений содержится в представлении psql_transaction_mode_history.

Просмотреть представление psql_transaction_mode_history можно запросом SELECT:

SELECT * FROM psql_transaction_mode_history;
     mode     |          started           |    ended                   |  source  | pending |   function_called          | timeout
--------------+----------------------------+----------------------------+----------+---------+----------------------------+---------
RW | 2025-01-21 10:44:04.582146 | 2025-01-21 11:56:16.790523| START | False | <NULL> | <NULL>
RO | 2025-01-21 11:56:16.090523 | 2025-01-21 14:42:14.230106| FUNCTION | False | 2025-01-21 11:56:15.991376 | 200
RW | 2025-01-21 14:42:14.230106 | 2025-01-21 14:49:54.067529| FUNCTION | False | 2025-01-21 14:42:14.230106 | 0
RO | 2025-01-21 14:49:54.067529 | 2025-01-21 16:15:28.292891| FUNCTION | False | 2025-01-21 14:49:53.967529 | 250
RW | 2025-01-21 16:15:28.292891 | <NULL> | FUNCTION | False | 2025-01-21 16:15:28.292887 | 0
RO | <NULL> | <NULL> | FUNCTION | True | 2025-01-21 18:53:34.891701 | -1
(6 rows)

Максимальное количество записей в представлении psql_transaction_mode_history определяется параметром transaction_mode_max_records (по умолчанию 10000 записей).

Просмотреть параметр запросом SHOW может любой пользователь:

SHOW transaction_mode_records;
 transaction_mode_records
---------------------------
10000
(1 rows)

Изменять параметр может только суперпользователь, изменение параметра требует рестарта сервера:

ALTER SYSTEM SET transaction_mode_max_records = 100;
\q
sudo systemctl restart postgresql

Минимальное значение параметра transaction_mode_max_records - 0 (представление не хранит информацию об истории переключений режимов), максимальное - 100000 записей.

При перезапуске режим RO не сохраняется. После запуска база данных переходит в режим чтения и записи (RW). Пример:

psql -U user2
SELECT psql_set_transaction_mode('RO', 110);
WARNING:  transaction mode successfully set to RO with 0 of aborted transactions

psql_set_transaction_mode
-----------------------
RO
(1 row)
INSERT INTO test_table(id, data) VALUES(100, 'very important data');
ERROR:  cannot execute INSERT in a read-only transaction
\q
sudo systemctl restart postgresql

psql -U user2
INSERT INTO test_table(id, data) VALUES(100, 'very important data');
INSERT 0 1

Управление списком пользователей, имеющих права на переключение режимов

Имена пользователей, имеющих права на вызов функции psql_set_transaction_mode, перечисляются в настроечном GUC-параметре set_xact_mode_users. Имена пользователей разделяются символом , (запятая).

По умолчанию параметр пуст. Просмотреть список пользователей, имеющих права на переключение режимов, можно запросом SHOW:

SHOW set_xact_mode_users;
  set_xact_mode_users
-----------------------

(1 row)

Изменять параметр set_xact_mode_users может только суперпользователь. Изменение параметра не требует перезагрузки сервера:

ALTER SYSTEM SET set_xact_mode_users = 'user1, user2';

SELECT pg_reload_conf();

Просматривать параметр set_xact_mode_users может любой пользователь:

SHOW set_xact_mode_users;
  set_xact_mode_users
-----------------------
user1, user2
(1 row)

Очистить параметр set_xact_mode_users можно либо запросом RESET:

ALTER SYSTEM RESET set_xact_mode_users;

SELECT pg_reload_conf();

SHOW set_xact_mode_users;
  set_xact_mode_users
------------------------

(1 row)

Либо передав в параметр пустую строку:

ALTER SYSTEM SET set_xact_mode_users='';

SELECT pg_reload_conf();

SHOW set_xact_mode_users;
    set_xact_mode_users
------------------------

(1 row)

Управление списком пользователей, имеющих права на выполнение пишущих транзакций в режиме RO

Имена пользователей, имеющих права на выполнение пишущих транзакций в режиме RO, перечисляются в настроечном GUC-параметре set_rw_users.

По умолчанию параметр пуст. Просмотреть список пользователей, имеющих права на выполнение пишущих транзакций в режиме RO, можно запросом SHOW:

SHOW set_rw_users;
  set_rw_users
----------------

(1 row)

Изменять параметр set_rw_users может только суперпользователь. Изменение параметра не требует перезагрузки сервера:

ALTER SYSTEM SET set_rw_users = 'user1, user2';

SELECT pg_reload_conf();

Просматривать параметр может любой пользователь:

SHOW set_rw_users;
   set_rw_users
---------------
user1, user2
(1 row)

Очистить параметр можно либо запросом RESET:

ALTER SYSTEM RESET set_rw_users;

SELECT pg_reload_conf();

SHOW set_rw_users;
    set_rw_users
----------------

(1 row)

Либо передав в параметр пустую строку:

ALTER SYSTEM SET set_rw_users='';

SELECT pg_reload_conf();

SHOW set_rw_users;
    set_rw_users
----------------

(1 row)

Пользователь, указанный в параметре set_rw_users, может выполнять пишущие транзакции после переключения в режим RO. Сессия данного пользователя открывается сразу в режиме RW, никаких дополнительных действий не требуется:

ALTER SYSTEM SET set_rw_users = 'user1';
ALTER SYSTEM SET set_xact_mode_users='user1, user2';
SELECT pg_reload_conf();
\q
psql -U user1
SELECT psql_set_transaction_mode('RO', 10);
WARNING:  transaction mode successfully set to RO with 0 of aborted transactions

psql_set_transaction_mode
-----------------------
RO
(1 row)
INSERT INTO test_table(id, data) VALUES(100, 'very important data');
INSERT 0 1

Диагностика

Попытка вызова функции psql_set_transaction_mode пользователем, не добавленным в список set_xact_mode_users, заканчивается ошибкой:

SELECT psql_set_transaction_mode('RO', 100);
ERROR:  permission denied for function psql_set_transaction_mode

Вызов функции psql_set_transaction_mode с неверными параметрами mode или timeout пользователем, добавленным в параметр set_xact_mode_users, заканчивается ошибкой.

Вызов функции psql_set_transaction_mode с неверным параметром mode заканчивается ошибкой:

SELECT psql_set_transaction_mode('READ', 100);
ERROR:  mode "READ" does not exist, choose a correct mode: "RO" or "RW"

Вызов функции psql_set_transaction_mode с неверными параметром timeout заканчивается ошибкой:

SELECT psql_set_transaction_mode('RO', -120);
ERROR:  timeout must be a positive integer or "0" to interrupt all RW transactions immediately or "-1" for unlimited timeout

Вызов функции psql_set_transaction_mode с недопустимым значением параметров mode или timeout пользователем, добавленным в параметр set_xact_mode_users, заканчивается системной ошибкой.

Вызов функции psql_set_transaction_mode с попыткой передать строку в параметр timeout заканчивается ошибкой:

SELECT psql_set_transaction_mode('RO', 'l2o');
ERROR:  invalid input syntax for type integer: "l2o"

Вызов функции psql_set_transaction_mode с попыткой передать дробное число в параметр timeout заканчивается ошибкой:

SELECT psql_set_transaction_mode('RO', 120.7);
ERROR:  function psql_set_transaction_mode(unknown, numeric) does not exist

Вызов функции psql_set_transaction_mode в параллельной сессии блокируется и заканчивается ошибкой.

Пример вызова функции psql_set_transaction_mode в параллельной сессии:

SELECT psql_set_transaction_mode('RO', 200);
ERROR: function "psql_set_transaction_mode" was blocked due to concurrent call

Попытка изменить параметр transaction_mode_max_records не суперпользователем заканчивается ошибкой:

ALTER SYSTEM SET transaction_mode_max_records=2000;
ERROR:  permission denied to set parameter "transaction_mode_max_records"

Попытка присвоить параметру transaction_mode_max_records значение, превышающее максимальное, заканчивается ошибкой:

ALTER SYSTEM SET transaction_mode_max_records=7007007;
ERROR:  7000007 is outside the valid range for parameter "transaction_mode_max_records" (0 .. 100000)

Попытка присвоить параметру transaction_mode_max_records невалидное значение заканчивается ошибкой:

ALTER SYSTEM SET transaction_mode_max_records='thousand';
ERROR:  invalid value for parameter "transaction_mode_max_records": "thousand"

Попытка изменения параметра set_xact_mode_users не суперпользователем заканчивается ошибкой:

ALTER SYSTEM SET set_xact_mode_users = 'user1, user2';
ERROR:  permission denied to set parameter "set_xact_mode_users"

Попытка вызова функции psql_set_transaction_mode пользователем, не добавленным в список set_xact_mode_users, заканчивается ошибкой (даже если он добавлен в параметр set_rw_users или является суперпользователем):

SELECT psql_set_transaction_mode('RO', 100);
ERROR: permission denied for function psql_set_transaction_mode

Попытка изменения параметра set_rw_users не суперпользователем заканчивается ошибкой:

ALTER SYSTEM SET set_rw_users = 'user1, user2';
ERROR:  permission denied to set parameter "set_rw_users"

После вызова функции psql_set_transaction_mode, с переключением всех транзакций в режим RO, попытки выполнить RW-транзакции в тестовой базе данных по истечении периода, заданного в параметре timeout, пользователями, не добавленными в параметр set_rw_users, заканчиваются ошибкой (даже если они добавлены в параметр set_xact_mode_users или являются суперпользователями):

psql -U user2
SELECT psql_set_transaction_mode('RO', 10);
WARNING:  transaction mode successfully set to RO with 0 of aborted transactions

psql_set_transaction_mode
-----------------------
RO
(1 row)
INSERT INTO test_table(id, data) VALUES(100, 'very important data');
ERROR:  cannot execute INSERT in a read-only transaction