PostgreSQL Error 55P03 lock_not_available: Анализ Блокировок и NOWAIT
- Ошибка выполнения:
ERROR: 55P03: could not obtain lock on row in relation "...". - В 1С падает проведение документов с сообщением «Не удалось заблокировать запись (конфликт блокировок)».
- Сбой выполнения запросов с модификатором
NOWAITили при установленномlock_timeout. - Высокое время ожидания блокировок на тяжелых транзакциях закрытия месяца или партионного учета.
1. Принцип возникновения ошибки 55P03
Ошибка генерируется, когда запрос пытается захватить блокировку строки или таблицы с опцией NOWAIT (или при истечении времени lock_timeout), но ресурс уже удерживается другой транзакцией.
2. Анализ графа блокировок в реальном времени
Определите, кто блокирует целевой ресурс и кто ожидает блокировки:
SELECT
blocked_locks.pid AS blocked_pid,
blocked_activity.usename AS blocked_user,
blocking_locks.pid AS blocking_pid,
blocking_activity.usename AS blocking_user,
blocked_activity.query AS blocked_statement,
blocking_activity.query AS current_statement_in_blocking_process
FROM pg_catalog.pg_locks blocked_locks
JOIN pg_catalog.pg_stat_activity blocked_activity ON blocked_activity.pid = blocked_locks.pid
JOIN pg_catalog.pg_locks blocking_locks
ON blocking_locks.locktype = blocked_locks.locktype
AND blocking_locks.database IS NOT DISTINCT FROM blocked_locks.database
AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation
AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page
AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple
AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid
AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid
AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid
AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid
AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid
AND blocking_locks.pid != blocked_locks.pid
JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid
WHERE NOT blocked_locks.granted;
3. Использование SKIP LOCKED вместо NOWAIT
Для организации очередей задач используйте SKIP LOCKED вместо падения по NOWAIT:
-- Вместо SELECT ... FOR UPDATE NOWAIT (вызывающего 55P03):
SELECT id, task_payload
FROM task_queue
WHERE status = 'pending'
ORDER BY priority DESC
LIMIT 1
FOR UPDATE SKIP LOCKED; -- Пропускает заблокированные строки без ошибки
4. Настройка управляемых таймаутов блокировок
Скорректируйте параметры ожидания блокировок в postgresql.conf, чтобы сессии не зависали бесконечно:
-- Задержка перед записью дедлока в лог
ALTER SYSTEM SET deadlock_timeout = '1s';
-- Максимальное время ожидания захвата любой блокировки (таблицы/строки)
ALTER SYSTEM SET lock_timeout = '30s';
SELECT pg_reload_conf();
Частые вопросы (FAQ)
Чем отличается NOWAIT от стандартного выполнения SELECT FOR UPDATE?
Стандартный SELECT FOR UPDATE блокирует вызывающий процесс и ждет завершения конкурирующей транзакции. Конструкция NOWAIT мгновенно возвращает ошибку 55P03, если строка уже кем-то заблокирована.
Почему 1С:Предприятие часто использует управляемые блокировки с NOWAIT?
В режиме управляемых блокировок 1С сама контролирует бизнес-логику очередей, быстро прерывая конфликтующие транзакции для предотвращения зависания рабочих процессов rphost.
Как принудительно разблокировать зависшую строку?
Найдите blocking_pid по приведенному выше SQL-запросу и завершите блокирующий процесс: SELECT pg_terminate_backend(blocking_pid);.
Помогает ли увеличение lock_timeout предотвратить ошибку 55P03?
Если запрос использует явный синтаксис NOWAIT, таймаут игнорируется (ошибка возвращается мгновенно). Если ошибка вызвана истечением lock_timeout, увеличение параметра поможет завершить длинные операции.