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

pg_dbms_lock. Пользовательские блокировки

Версия: 1.0.0

В исходном дистрибутиве установлено по умолчанию: нет.

Связанные компоненты: отсутствуют.

Схема размещения: dbms_lock.

Расширение pg_dbms_lock обеспечивает совместимость с пакетом Oracle DBMS_LOCK, за исключением функции DBMS_LOCK.CONVERT() и некоторых неподдерживаемых режимов блокировки. Реализованы следующие хранимые процедуры:

  • ALLOCATE_UNIQUE() – выделяет уникальный идентификатор блокировки для именованной блокировки;
  • REQUEST() – запрашивает блокировку определенного режима;
  • RELEASE() – освобождает блокировку;
  • SLEEP() – приостанавливает выполнение процедуры на указанное время.

Расширение применяется для следующих целей:

  • управление доступом для устройств;
  • координация параллельных сессий;
  • ограничение доступа к ресурсам по времени;
  • определение завершения работы сессии в результате COMMIT/ROLLBACK.

Доработка

Доработка не проводилась.

Ограничения

Ограничения отсутствуют.

Установка

Перед началом работы с расширением pg_dbms_lock, необходима установка расширения pg_background.

pg_dbms_lock имеет некоторую асинхронность в своих функциях и для реализации этого необходим фоновый процесс, который и реализует pg_background.

Для нормальной работы pg_dbms_lock, необходимо расширение pg_background в каждой базе данных, где планируется использование pg_dbms_lock.

Установка расширения pg_dbms_lock для операционных систем Альт 8 СП и Astra Linux:

sudo apt-get install /usr/pangolin-6.5/3rdparty/pg_dbms_lock/pangolin-pg-dbms-lock-1.0.0-{OS}.x86_64.rpm -y

Для других ОС:

sudo dnf install /usr/pangolin-6.5/3rdparty/pg_dbms_lock/pangolin-pg-dbms-lock-1.0.0-{OS}.x86_64.rpm -y

Далее вручную, при наличии прав администратора СУБД:

CREATE EXTENSION pg_dbms_lock SCHEMA dbms_lock;

SET search_path=public,ext,dbms_lock

Если не хватает прав или база данных используется как сервис, то можно просто импортировать расширение в рабочую БД, запустив скрипт из папки: sql/pg_dbms_lock--1.0.0.sql.

Важно при этом иметь установленное в этой БД расширение pg_background.

Настройка

Настройка не требуется.

Использование модуля

allocate_unique()

Функция создает идентификатор блокировки в диапазоне 1073741824 to 1999999999.

Идентификатор блокировки необходим для скоординированной работы приложений.

Для работы бизнес-логики удобнее использовать именованные блокировки в отличие от цифровых идентификаторов.

В этом случае необходимо использовать функцию allocate_unique().

Первая сессия, вызвавшая функцию allocate_unique() вызывает генерацию уникального lock ID, который сохраняется в таблице dbms_lock.dbms_lock_allocated.

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

Именованная блокировка остается ассоциированной с определенным цифровым идентификатором в течение по меньшей мере expiration_secs (по умолчанию 10 дней), прошедших с момента последнего вызова функции allocate_unique() с данным конкретным именем блокировки.

Записи из таблицы dbms_lock.dbms_lock_allocated могут удаляться для переиспользования места.

Синтаксис:

dbms_lock.allocate_unique (
lockname IN varchar,
lockhandle INOUT varchar,
expiration_secs IN integer DEFAULT 864000);
  • lockname – имя блокировки для которого выполняется создание unique ID.
  • lockhandle – возвращает handle для lock ID, сгенерированному функцией dbms_lock.allocate_unique. handle используется в последующих вызовах функций REQUEST() и RELEASE(). Все сеансы, использующие lockhandle, возвращаемый функцией ALLOCATE_UNIQUE() с одним и тем же именем блокировки, будут ссылаться на одну и ту же блокировку. Поэтому между сеансами необходимо передавать не дескриптор блокировки, а имя блокировки, чтобы получить из него дескриптор блокировки.
  • expiration_secs – количество секунд с момента вызова функции allocate_unique для этой блокировки, перед удалением блокировки из таблицы dbms_lock_allocated. Период по-умолчанию составляет 10 дней. Нет необходимости удалять записи вручную. Очистка записей имеющих признаки устаревших, будет выполнена последующими вызовами allocate_unique() в автоматическом режиме.

Пример использования:

DO $$
DECLARE
printer_lockhandle varchar;
BEGIN
CALL dbms_lock.allocate_unique (lockname => 'printer_lock', lockhandle => printer_lockhandle);
IF ( printer_lockhandle IS NULL ) THEN
RAISE EXCEPTION 'DBMS_LOCK.ALLOCATE_UNIQUE() FAIL';
END IF;
END;
$$;

DO $$
DECLARE
lock_res int;
printer_lockhandle varchar;
DBMS_LOCK_X_MODE int := 6;
rec record;
BEGIN
CALL dbms_lock.allocate_unique (lockname => 'printer_lock', lockhandle => printer_lockhandle);
IF ( printer_lockhandle IS NULL ) THEN
RAISE EXCEPTION 'DBMS_LOCK.ALLOCATE_UNIQUE() FAIL';
END IF;

RAISE NOTICE 'Found lockhandle => %', printer_lockhandle;

lock_res := dbms_lock.request( lockhandle => printer_lockhandle,
lockmode => DBMS_LOCK_X_MODE,
timeout => 5,
release_on_commit => false);

IF ( lock_res <> 0 ) THEN
RAISE EXCEPTION 'DBMS_LOCK.REQUEST() FAIL: %', lock_res;
END IF;

FOR rec IN SELECT objid, mode FROM pg_locks WHERE objid IS NOT NULL
LOOP
RAISE NOTICE 'objid => % | mode => %', rec.objid, rec.mode;
END LOOP;

lock_res := dbms_lock.release(lockhandle => printer_lockhandle);

IF ( lock_res <> 0 ) THEN
RAISE EXCEPTION 'DBMS_LOCK.RELEASE() FAIL: %', lock_res;
END IF;

END;
$$;

SELECT objid, mode FROM pg_locks WHERE objid IS NOT NULL AND locktype = 'advisory';

SELECT name, lockid, expiration FROM dbms_lock.dbms_lock_allocated;

request()

Функция запрашивает блокировку в определенном режиме.

request() является перегруженной функцией. Она может принимать в качестве аргумента как определенный пользователем идентификатор, так и возвращаемый функцией allocate_unique().

Синтаксис:

dbms_lock.request(
id IN integer
lockmode IN integer DEFAULT 6,
timeout IN integer DEFAULT 32767,
release_on_commit IN boolean DEFAULT false
)
RETURN integer;

dbms_lock.request(
lockhandle IN varchar,
lockmode IN integer DEFAULT 6,
timeout IN integer DEFAULT 32767,
release_on_commit IN boolean DEFAULT false
)
RETURN integer;
  • id/lockhandle – идентификатор блокировки от 0 до 1073741823 или lockhandle, возвращаемый функцией allocate_unique().
  • lockmode – запрашиваемый режим блокировки. PostgreSQL поддерживает только блокировки в режимах Exclusive mode (6) и Shared mode (4).
  • timeout – число секунд для продолжения попыток получения прав на блокировку. Если права на блокировку получить не удается, вызов возвращает 1. По умолчанию значение для таймаута 32767 секунд.
  • release_on_commit – установите этот параметр в значение true для удаления блокировки по событию COMMIT/ROLLBACK. В противном случае блокировка сохраняется до момента завершения сессии или явного ее удаления.

Возвращаемые функцией значения:

  • 0 – успех;
  • 1 – «тайм-аут»;
  • 3 – ошибка параметра;
  • 4 – уже имеется блокировка, указанная по id или lockhandle;
  • 5 – недопустимый дескриптор блокировки.

Пример использования:

DO $$
DECLARE
lock_res int;
DBMS_LOCK_X_MODE int := 6;
BEGIN
lock_res := DBMS_LOCK.REQUEST( 123, DBMS_LOCK_X_MODE, 300, FALSE );
IF ( lock_res <> 0 ) THEN
RAISE EXCEPTION 'DBMS_LOCK.REQUEST() FAIL: %', lock_res;
END IF;
END;
$$;

release()

Функция явно удаляет блокировку, ранее созданную в результате вызова функции. Блокировка автоматически уничтожается при завершении сессии.

Функция release() является перегруженной.

Может принимать в качестве параметра как пользовательское имя блокировки так и lockhandle, возвращаемый функцией allocate_unique().

Синтаксис:

dbms_lock.release (
id IN integer)
RETURN integer;
dbms_lock.release (
lockhandle IN varchar)
RETURN integer;

idlockhandle, определенный пользователем от 0 до 1073741823 или lockhandle, возвращаемый функцией allocate_unique().

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

  • 0 – успех;
  • 3 – ошибка параметра;
  • 4 – не владеет блокировкой, указанной по id или lockhandle;
  • 5 – недопустимый дескриптор блокировки.

Пример использования:

DO $$
DECLARE
lock_res int;
BEGIN
-- release lock
lock_res := DBMS_LOCK.RELEASE( 123 );
IF ( lock_res <> 0 ) THEN
RAISE EXCEPTION 'DBMS_LOCK.RELEASE() FAIL: %', lock_res;
END IF;
END;
$$;

sleep()

Функция вызывает приостановку сессии на определенное количество секунд.

Синтаксис:

DBMS_LOCK.SLEEP (
seconds IN double precision);

seconds – количество времени в секундах, на которое нужно приостановить сеанс. Наименьший шаг может быть указан в сотых долях секунды; например, 1.95 является допустимым значением времени.

Пример использования:

CALL DBMS_LOCK.SLEEP(0.70);

Ссылки на документацию разработчика

Дополнительно поставляемый модуль pg_dbms_lock: https://github.com/HexaCluster/pg_dbms_lock.