В разработке на платформе 1С часто встречается ситуация, когда два документа, работающие с данными за разные, непересекающиеся периоды, внезапно начинают конфликтовать и один из них не проводится с ошибкой блокировки. Например, один документ устанавливает блокировку на данные за прошлый месяц, а второй, оперативный, пытается провестись текущей датой и падает с ошибкой «Превышено максимальное время ожидания предоставления блокировки».
На первый взгляд, это кажется нелогичным, ведь данные разные. Давайте разберемся, почему так происходит, в чем разница между таймаутом и дедлоком, и как диагностировать и решить эту проблему.
Основной источник недопонимания — это поведение системы при записи данных без явной установки блокировок. Многие разработчики полагают, что если в коде проведения документа не написан блок БлокировкаДанных = Новый БлокировкаДанных, то никаких блокировок не будет. Это не так.
В режиме управляемых блокировок любая операция, изменяющая данные в базе (например, вызов метода НаборЗаписей.Записать()), автоматически устанавливает неявную управляемую исключительную блокировку. Платформа сама решает, по каким полям заблокировать данные, и делает это на основе ключевых полей изменяемой таблицы. Для регистра накопления это будут все его измерения.
Рассмотрим ситуацию из вопроса:
Записать().Записать() платформа пытается установить неявную блокировку на записываемые данные по всем измерениям регистра.Если значения каких-либо измерений у "Документа А" и "Документа Б" совпадают (например, 'Организация', 'Склад', 'Номенклатура'), возникает конфликт. Транзакция "Документа Б" будет ждать освобождения ресурса. Если "Документ А" не снимет свою блокировку в течение таймаута (по умолчанию 20 секунд), то транзакция "Документа Б" аварийно завершится с ошибкой.
Важный момент: управлять полями этой неявной блокировки напрямую нельзя. Единственный правильный подход — всегда явно устанавливать управляемую блокировку в любой транзакции, которая изменяет данные. Это делает код более предсказуемым и управляемым.
Даже если измерения документов не пересекаются напрямую, есть еще один неочевидный источник конфликтов — таблицы итогов регистра накопления.
Если для регистра включен расчет итогов (свойство Итоги в конфигураторе), то при записи данных платформа блокирует не только основные таблицы данных, но и таблицы итогов. Блокировка накладывается на итоги по тем измерениям, для которых они рассчитываются.
Посмотрим на пример:
Пусть у нас есть регистр накопления "Продажи" с измерениями "Организация", "Контрагент", "Номенклатура" и ресурсом "Сумма". Итоги рассчитываются по всем измерениям.
Несмотря на то, что периоды документов (январь и февраль) не пересекаются, они оба пытаются получить доступ к одной и той же строке в таблице итогов, что и приводит к конфликту блокировок. Это одна из самых частых причин "необъяснимых" блокировок при параллельном проведении.
В обсуждении возник спор о терминах. Важно четко их различать для правильной диагностики.
Правильное определение типа проблемы — первый шаг к ее решению. В нашем случае речь идет о таймауте.
Чтобы точно определить, по каким данным происходит конфликт, самым эффективным инструментом является Технологический журнал (ТЖ). Он позволяет увидеть "под капотом", какие именно ресурсы и по каким полям блокируются.
Разберем по шагам, как его использовать для нашей задачи:
logcfg.xml в папке conf сервера 1С и включить в нем сбор событий, связанных с блокировками. Минимально необходимая настройка:
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://v8.1c.ru/v8/tech-log">
<log location="C:\1C\Logs" history="4">
<event>
<eq property="name" value="TLOCK"/>
</event>
<event>
<eq property="name" value="TDEADLOCK"/>
</event>
<property name="all"/>
</log>
</config>
Не забудьте перезапустить службу агента сервера 1С, чтобы настройки применились.
C:\1C\Logs. Вам нужно найти событие TLOCK, которое длилось около 20 секунд. В логе оно будет выглядеть примерно так:
25:35.312015-20000024,TLOCK,1,process=rphost,p:processName=... t:connectID=123, ... WaitConnections=124, ...
Ключевой параметр здесь — duration, который для таймаута будет равен 20000000 микросекунд (20 секунд). t:connectID=123 — это номер соединения, которое ждало, а WaitConnections=124 — номер соединения, которое удерживало блокировку.
TLOCK будет указана область блокировки (Regions) и значения полей (Descr), по которым произошло пересечение. Вы увидите, что, например, оба сеанса пытались заблокировать одну и ту же запись с одинаковым значением поля "Организация", что и стало причиной конфликта. Это даст вам точный и неоспоримый ответ на вопрос, где именно пересеклись ваши транзакции.Таким образом, для решения исходной проблемы необходимо проанализировать логи ТЖ, определить, вызвана ли блокировка пересечением по основным измерениям или по таблицам итогов, и соответствующим образом скорректировать логику проведения документов или настройки итогов регистра.