Как устранить конфликт блокировок при проведении документа «Этап производства 2.2» в 1С:ERP?

Программист 1С v8.3 (Управляемые формы) 1С:ERP Управление предприятием Управленческий учет Промышленность, строительство и АПК
← На главную

При работе в высоконагруженных базах 1С:ERP 2.4 / 2.5 разработчики часто сталкиваются с ошибкой: «Конфликт блокировок при выполнении транзакции. Неустранимый конфликт блокировок» (TDEADLOCK). Особенно остро эта проблема проявляется при автоматической обработке документов внешними обработками, обменамии или фоновыми заданиями. Рассмотрим природу этого конфликта на примере документа ЭтапПроизводства2_2 и разберем по шагам, как локализовать и устранить эту ошибку.

Выясняем причину: механизм возникновения взаимоблокировки (дедлока)

Для начала проанализируем, почему возникает дедлок, даже если пользователи работают с разными документами и физически не могут редактировать один и тот же объект одновременно. Ключ к разгадке кроется в механизме проведения документов цепочки производства в 1С:ERP.

При проведении конкретного этапа производства системе необходимо гарантировать согласованность данных по всему заказу-распоряжению (для расчета графиков, обеспечения, проверки статусов и т.д.). Для этого типовой код ERP накладывает управляемую исключительную блокировку на все этапы производства, связанные с тем же заказом, что и проводимый документ.

Рассмотрим классический сценарий взаимной блокировки двух параллельных транзакций:

  1. Транзакция А (Внешняя обработка): Начинает изменять и записывать Этап 1. Платформа 1С автоматически вешает неявную исключительную блокировку на этот документ. Далее обработка вызывает проведение Этапа 1, в процессе которого типовые алгоритмы ERP пытаются заблокировать все этапы по связанному заказу (включая Этап 2).
  2. Транзакция Б (Интерактивный пользователь или фоновое задание): В этот же самый момент записывает или изменяет Этап 2 из того же заказа-распоряжения. Платформа накладывает неявную исключительную блокировку на Этап 2. При проведении система пытается заблокировать все этапы этого заказа (включая Этап 1).

В итоге получаем замкнутый круг: Транзакция А держит Этап 1 и ожидает освобождения Этапа 2. Транзакция Б удерживает Этап 2 и ожидает освобождения Этапа 1. Менеджер блокировок СУБД или платформы 1С фиксирует неразрешимый тупик (TDEADLOCK), принудительно прерывает одну из транзакций (делает её «жертвой») и возвращает пользователю ошибку блокировки.

---

Решение А. Упреждающая блокировка по родителю (Заказу)

Самый надежный способ победить дедлок в данном случае — обеспечить строгую последовательность захвата ресурсов. Если система в любом случае будет блокировать все подчиненные этапы по заказу, мы должны сделать это первыми, в самом начале транзакции нашей внешней обработки, еще до внесения изменений в конкретные объекты.

Для этого сразу после открытия транзакции программно установим управляемую блокировку на все этапы по нашему заказу-распоряжению. Рассмотрим пример кода:


Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("Документ.ЭтапПроизводства2_2");

// Устанавливаем блокировку по реквизиту связи с заказом (Распоряжение)
ЭлементБлокировки.УстановитьЗначение("Распоряжение", СсылкаНаЗаказ);
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;

// Накладываем блокировку
Блокировка.Заблокировать();

Как это работает: Наша транзакция мгновенно монополизирует весь пул документов по конкретному заказу. Если в этот момент другой пользователь или фоновое задание попытается провести соседний этап по этому же заказу, их транзакция не упадет по дедлоку, а цивилизованно встанет в очередь ожидания (таймаут) на входе. Как только наша обработка завершит транзакцию, вторая транзакция успешно продолжит выполнение.

---

Решение Б. Минимизация времени жизни транзакции

Часто разработчики допускают критическую ошибку, помещая внутрь одной транзакции длительные операции: запросы к БД, сложные расчеты спецификаций, заполнение табличных частей и интерактивные действия. Чем дольше открыта транзакция, тем выше вероятность натолкнуться на конфликт блокировок.

Проанализируем структуру кода нашей внешней обработки и перенесем все подготовительные действия за рамки транзакции:

  1. Выполним все расчеты, тяжелые запросы и выборки данных ДО открытия транзакции.
  2. Заполним свойства и реквизиты объекта ДокументОбъект в памяти.
  3. Откроем транзакцию исключительно на время записи и проведения:

// Все сложные расчеты сделали заранее вне транзакции

НачатьТранзакцию();
Попытка
    // Упреждающая блокировка по заказу
    Блокировка = Новый БлокировкаДанных;
    ЭлементБлокировки = Блокировка.Добавить("Документ.ЭтапПроизводства2_2");
    ЭлементБлокировки.УстановитьЗначение("Распоряжение", ДокументОбъект.Распоряжение);
    Блокировка.Заблокировать();
    
    // Записываем и проводим объект
    ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
    
    ЗафиксироватьТранзакцию();
Исключение
    ОтменитьТранзакцию();
    ВызватьИсключение;
КонецПопытки;

---

Решение В. Диагностика конфликтов с помощью Технологического Журнала (ТЖ)

Если простые методы не помогли и ошибки продолжают возникать, необходимо точно определить «второго участника» конфликта блокировок. Для этого настроим сбор Технологического Журнала на сервере 1С.

Создадим или отредактируем файл конфигурации logcfg.xml на сервере приложений 1С, добавив в него отслеживание событий TDEADLOCK и TLOCK:


<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://v8.1c.ru/v8/tech-log">
    <log location="C:\v8_logs\deadlocks" history="24">
        <event>
            <eq property="Name" value="TDEADLOCK"/>
        </event>
        <event>
            <eq property="Name" value="TLOCK"/>
            <eq property="p:processName" value="workprocess"/>
        </event>
        <property name="all"/>
    </log>
</config>

При анализе полученных логов ТЖ обратим особое внимание на следующие параметры:

---

Решение Г. Проверка разделения итогов регистров накопления

В некоторых случаях конфликт блокировок происходит не на уровне самих документов, а на уровне таблиц итогов регистров накопления, по которым документ делает движения (например, РегистрНакопления.ОбеспечениеЗаказов или РегистрНакопления.ВыпускПродукции). Если несколько пользователей одновременно проводят этапы, система производит запись в одни и те же физические таблицы СУБД.

Выясним, включен ли режим разделения итогов для этих регистров в конфигурации:

  1. Откроем конфигуратор 1С.
  2. Найдем основные регистры накопления, по которым делает движения документ ЭтапПроизводства2_2.
  3. Перейдем в свойства регистра и проверим свойство «Разделение итогов» (оно должно быть установлено в значение Истина).

Использование механизма разделения итогов позволяет платформе записывать движения параллельно без взаимного блокирования актуальных остатков разными транзакциями.

← На главную