При выполнении тяжелых регламентных операций в 1С, таких как Расчет себестоимости выпуска, программисты часто сталкиваются с ошибкой Конфликт блокировок при выполнении транзакции. Ситуация осложняется тем, что ошибка Lock request time out period exceeded (native=1222) может возникать даже в том случае, если в базе данных в этот момент работает только один пользователь. Рассмотрим подробно, почему это происходит и как найти решение задачи.
Для начала разберем природу ошибки. Сообщение Microsoft SQL Server Native Client 11.0: Lock request time out period exceeded означает, что одна транзакция пыталась получить доступ к данным, которые уже были заблокированы другой транзакцией, и время ожидания (по умолчанию в 1С это 20 секунд) истекло. В нашем случае конфликт происходит на уровне регистра УчетЗатрат при вызове метода Записать().
Важный нюанс: если ошибка вылетает при единственном работающем пользователе, значит, конфликт происходит между основным сеансом пользователя и фоновым заданием, которое этот сеанс инициировал. Проанализируем ключевой момент: автор темы заметил, что если в коде отключить форму прогресса выполнения (ожидание завершения), документ проводится без проблем. Это указывает на то, что родительский сеанс удерживает блокировку, мешающую фоновому заданию.
Когда мы используем метод ДлительныеОперации.ВыполнитьСКонтекстомКлиента, система передает в фоновое задание параметры сеанса и временные таблицы. Если перед запуском фонового задания в основном сеансе была открыта транзакция или объект (документ) был заблокирован для записи, фоновое задание может «споткнуться» об эту блокировку.
Разберем, как это выглядит в коде. Часто разработчики по ошибке пишут конструкцию вида:
НачатьТранзакцию();
// Какие-то действия с документом
Результат = ДлительныеОперации.ВыполнитьВФоне(...);
ЗафиксироватьТранзакцию();
В этой ситуации фоновое задание пытается записать данные в регистр, но основная транзакция еще не завершена и удерживает блокировку. Никогда не запускайте длительные фоновые операции внутри открытой транзакции.
Одной из скрытых причин является механизм эскалации блокировок (Lock Escalation). При записи огромного количества строк (например, при расчете себестоимости) SQL Server может решить, что блокировать каждую строку по отдельности слишком накладно, и заблокирует всю таблицу целиком.
Проанализируем ситуацию: если фоновое задание начало массовую запись в таблицу _AccumRegTotals... (итоги регистра накопления), а основной сеанс пытается хотя бы прочитать данные из этой же таблицы для обновления формы, возникает конфликт. Выясним, как с этим бороться:
ALLOW_ROW_LOCKS = ON) и страниц (ALLOW_PAGE_LOCKS = ON).Если визуальный анализ кода не помогает, необходимо настроить Технологический журнал (ТЖ). Это позволит точно увидеть, какой именно процесс установил блокировку и какой ее ожидает. Для этого создадим файл logcfg.xml со следующим содержанием:
<config xmlns="http://v8.1c.ru/7.4/config/admin/vrs/logcfg">
<log location="C:\LOGS\LOCKS" history="2">
<event>
<eq property="Name" value="TTIMEOUT"/>
</event>
<event>
<eq property="Name" value="TLOCK"/>
<p:conf property="WaitConnections"/>
</event>
<property name="all"/>
</log>
</config>
Рассмотрим, что мы получим в логах: событие TTIMEOUT покажет момент возникновения ошибки, а в описании события TLOCK будет указан номер сеанса (WaitConnections), который удерживал ресурс. Это позволит однозначно определить «виновника» — будь то другое фоновое задание, полнотекстовый поиск или зависший сеанс.
Если у вас есть доступ к серверу баз данных, воспользуемся SQL Profiler. Нам необходимо отследить событие Lock:Timeout. Разберем по шагам:
Errors and Warnings -> Exception и Locks -> Lock:Timeout.SPID (ID процесса в SQL). Вы сможете увидеть SQL-запрос, который не смог выполниться, и сопоставить его с таблицей 1С.Подведем итог и составим план действий для исправления ситуации:
ВыполнитьВФоне документ записан и транзакция закрыта.ДлительныеОперации.ВыполнитьСКонтекстомКлиента на стандартный метод ДлительныеОперации.ВыполнитьВФоне, передавая только необходимые параметры (ссылку на документ, период) через структуру.УчетЗатрат (в конфигурациях с РАУЗ), стандартных 20 секунд может быть мало. Попробуйте программно увеличить таймаут перед записью набора записей:
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.УчетЗатрат");
// Установка фильтров
Блокировка.Заблокировать();
Помните, что расчет себестоимости — это процедура, максимально нагружающая систему. Правильное разделение прав доступа и использование фоновых заданий без избыточного клиентского контекста — залог стабильной работы программы.