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 для операционных систем Альт и 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;
id – lockhandle, определенный пользователем от 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.