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;
$$;