При работе в высоконагруженных системах 1С, таких как Управление торговлей 11, пользователи часто сталкиваются с ситуацией, когда проведение документов начинает конфликтовать с процессами фоновой синхронизации данных. Особенно остро эта проблема проявляется при использовании внешних транспортов, таких как Яндекс.Диск. В этой статье мы подробно разберем причины возникновения ошибки "Конфликт блокировок" при обращении к таблицам __NODE, проанализируем механизмы работы платформы и рассмотрим несколько эффективных способов решения данной проблемы.
Для начала проанализируем техническую сторону вопроса. Когда в конфигурации настроен план обмена, платформа 1С для каждого узла создает служебную таблицу в базе данных СУБД (например, MS SQL Server). Эти таблицы имеют префикс __NODE (например, __NODE27). Их основная задача — хранить регистрацию изменений объектов.
Разберем, что происходит в момент конфликта:
__NODE для всех узлов, в которые этот документ должен быть выгружен. Это происходит внутри транзакции проведения.__NODE и, после успешной отправки пакета, удаляет записи из неё.Конфликт возникает из-за того, что процесс обмена накладывает блокировку на всю таблицу регистрации для обеспечения целостности данных при чтении и удалении. Если транзакция обмена длится слишком долго, попытка провести документ завершается ошибкой по таймауту. Раньше система могла "подвисать" и ждать, так как время выполнения обмена укладывалось в стандартный таймаут установки блокировки (обычно 20 секунд). Как только объем данных или время доступа к диску (в случае с Яндекс.Диском) увеличились, ожидание стало превышать этот порог, что привело к выбросу ошибки.
Одним из самых эффективных системных способов решения проблемы блокировок "чтение против записи" является включение режима Read Committed Snapshot Isolation (RCSI) в MS SQL Server.
Рассмотрим, как это помогает: в обычном режиме читающая транзакция (синхронизация) блокирует пишущую транзакцию (проведение документа). При включенном RCSI читающая транзакция использует версионность строк. Это позволяет процессу обмена читать данные, не блокируя запись новых изменений в таблицу __NODE.
Для включения этого режима (требуется монопольный доступ к базе) используйте следующий скрипт:
ALTER DATABASE [Имя_Вашей_Базы] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
ALTER DATABASE [Имя_Вашей_Базы] SET READ_COMMITTED_SNAPSHOT ON;
ALTER DATABASE [Имя_Вашей_Базы] SET MULTI_USER;
Если клиент настаивает на том, чтобы документ "ждал" окончания обмена, мы можем реализовать программный механизм повторных попыток. Посмотрим на пример кода, который можно внедрить в процедуру перед записью или проведением документа:
ПопыткаПроведения = Ложь;
КоличествоПопыток = 0;
МаксПопыток = 5;
Пока НЕ ПопыткаПроведения И КоличествоПопыток < МаксПопыток Цикл
Попытка
КоличествоПопыток = КоличествоПопыток + 1;
ЭтотОбъект.Записать(РежимЗаписиДокумента.Проведение);
ПопыткаПроведения = Истина;
Исключение
ОписаниеОшибки = ИнформацияОбОшибке().Описание;
Если Найти(ОписаниеОшибки, "Конфликт блокировок") > 0 Тогда
// Если возникла блокировка, ждем 2 секунды перед следующей попыткой
// В управляемом приложении можно использовать фоновое задание или таймаут
// Для упрощения примера используем задержку через КомандаСистемы
КомандаСистемы("timeout /t 2 /nobreak > nul");
Иначе
ВызватьИсключение; // Если ошибка не связана с блокировкой, пробрасываем её дальше
КонецЕсли;
КонецПопытки;
КонецЦикла;
Проанализируем метод, предложенный в обсуждении: использование специального признака, который будет сигнализировать о процессе обмена. Мы можем создать Константу или запись в Регистре сведений, которая будет устанавливаться в истину в начале кода синхронизации и сбрасываться в ложь в конце (обязательно в блоке Попытка...КонечнаяПопытка).
При проведении документа мы будем проверять этот флаг. Если он установлен, мы можем либо выводить пользователю предупреждение с просьбой подождать, либо автоматически ставить документ в очередь отложенного проведения. Это позволит избежать жестких ошибок платформы и сделает работу пользователей более комфортной.
Выясним причину долгого удержания блокировки процессом обмена. Часто это связано с тем, что Библиотека стандартных подсистем (БСП) пытается выгрузить слишком большой объем данных в одной транзакции.
Мы можем уменьшить размер порции данных при выгрузке. Для этого в коде модуля обмена или в правилах обмена следует ограничить количество объектов, обрабатываемых за один проход. Например, вместо 10 000 объектов за раз, настроим выгрузку порциями по 500. Таким образом, между транзакциями обмена будут появляться "окна", в которые SQL-сервер сможет пропустить транзакции проведения документов от пользователей.
Также проверьте параметр ПараметрыОбменаДанными.ВыгружатьОбъектыПорциями. В современных версиях УТ 11 это позволяет значительно снизить нагрузку на таблицу __NODE.
Использование облачных сервисов как транспорта часто накладывает дополнительные задержки на файловые операции. Если 1С записывает файл выгрузки напрямую в папку, которая синхронизируется агентом Яндекс.Диска, возникает ситуация двойной блокировки:
Это затягивает время выполнения транзакции 1С. Рекомендуем сначала выполнять выгрузку в локальную временную папку на быстром диске (SSD), а после завершения выгрузки и закрытия транзакции в 1С, копировать готовый файл в папку синхронизации Яндекс.Диска средствами операционной системы.
Для исправления конфликтов блокировок при синхронизации наиболее надежным подходом является сочетание технических настроек СУБД (RCSI) и оптимизации программного кода (порционная выгрузка). Если же бизнес-логика требует гарантированного проведения документа "здесь и сейчас", реализация цикла повторных попыток с небольшим ожиданием поможет сгладить пиковые нагрузки без прерывания работы пользователей.