Откуда берется двойной слэш в JSON и как решить конфликт блокировок при загрузке номенклатуры в 1С

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет IT и автоматизация бизнеса
← На главную

В процессе интеграции систем и обмена данными разработчики 1С часто сталкиваются с неочевидным поведением платформы. В этой статье мы подробно разберем две серьезные проблемы, возникшие при выгрузке и последующей загрузке справочника номенклатуры через формат JSON. Сначала мы выясним причину появления загадочного двойного слэша в путях категорий, а затем детально проанализируем, почему при загрузке больших файлов в файловую базу данных возникает ошибка «Конфликт блокировок при выполнении транзакции» и как её эффективно устранить.

Часть 1. Загадка двойного слэша в JSON-файле

Рассмотрим ситуацию: при выгрузке иерархии номенклатуры в JSON-файл в именах групп, содержащих символ обратного слэша (например, Выключатели\розетки IP54), в итоговом файле внезапно появляется двойной слэш: Выключатели\\розетки IP54. При этом обычный прямой слэш (/) выгружается совершенно нормально.

Выясним причину этого явления. Символ обратного слэша (\) в спецификации формата JSON (а также во многих языках программирования) является служебным экранирующим символом. Он используется для обозначения спецсимволов внутри строк — например, перевода строки (\n), табуляции (\t) или кавычек (\").

Если внутри строкового значения встречается сам символ обратного слэша, стандарт JSON обязывает его экранировать. Чтобы сериализатор понял, что это просто текстовый символ, а не начало управляющей последовательности, он автоматически дублирует его. Таким образом, строка Выключатели\\розетки в файле JSON — это абсолютно корректное представление строки Выключатели\розетки. При чтении этого JSON-файла стандартными средствами 1С (через объект ЧтениеJSON) двойной слэш автоматически преобразуется обратно в одинарный. Никаких дополнительных обработок и замен делать не нужно.

Прямой слэш (/) по стандарту не требует обязательного экранирования, поэтому он записывается в файл как есть, без удвоения.

Часть 2. Почему возникает конфликт блокировок при загрузке

Перейдем ко второй, более сложной проблеме. При импорте большого JSON-файла с номенклатурой (объемом более 10 Мб, содержащего тысячи элементов) в файловую базу 1С процесс прерывается аварийно примерно на середине. Платформа выдает ошибку: «Конфликт блокировок при выполнении транзакции». Это происходит даже в тех случаях, когда загрузка выполняется монопольно и в базе больше нет активных пользователей.

Проанализируем архитектурные причины этого поведения и разберем по шагам, как исправить код загрузки.

Ошибка 1. Избыточное и опасное использование метода Заблокировать()

В коде загрузки перед вызовом метода Записать() программисты часто пытаются принудительно вызвать метод Заблокировать():


НоваяНоменклатура.Заблокировать();
НоваяНоменклатура.Записать();

В сценарии пакетной программной загрузки данных в один поток явная объектная блокировка приносит исключительно вред. Метод Заблокировать() предназначен для интерактивной работы пользователей, чтобы предотвратить одновременное редактирование одного объекта двумя людьми в интерфейсе. При программной записи метод Записать() сам по себе устанавливает необходимую транзакционную блокировку базы данных.

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

Ошибка 2. Игнорирование режима загрузки данных

Это ключевая причина возникновения конфликтов блокировок. В представленном коде создания номенклатуры объект записывается в режиме обычной работы пользователя. Это означает, что при записи каждого элемента система 1С выполняет огромный объем невидимой работы:

  1. Запускаются все подписки на события и обработчики ПередЗаписью и ПриЗаписи, встроенные в конфигурацию (проверки, заполнение связанных регистров, расчеты цен и т.д.).
  2. Выполняется автоматическая регистрация изменений на узлах планов обмена. Если в базе настроен хотя бы один обмен данными (даже если он временно не используется), система пытается записать записи в служебные таблицы регистрации изменений.

В файловой базе данных 1С блокировка таблиц происходит не на уровне конкретной записи (как в MS SQL или PostgreSQL), а на уровне всей таблицы целиком. Массовое создание элементов без отключения этой логики мгновенно и намертво блокирует таблицы регистрации изменений планов обмена, вызывая транзакционные тупики.

Решение этой проблемы заключается в использовании свойства ОбменДанными.Загрузка. Перед записью созданного элемента обязательно устанавливайте это свойство в значение Истина:


НоваяНоменклатура = Справочники.Номенклатура.CreateItem();
// ... Заполнение реквизитов ...
НоваяНоменклатура.ОбменДанными.Загрузка = Истина; 
НоваяНоменклатура.Записать();

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

Ошибка 3. Отсутствие явного управления транзакциями

Рассмотрим физику работы файловой СУБД 1С. Если вы не открываете транзакцию в коде вручную, платформа вынуждена оборачивать каждый одиночный вызов метода Записать() в отдельную неявную транзакцию. Запись 10 000 элементов превращается в 10 000 транзакций. Каждая транзакция требует физического обращения к жесткому диску для фиксации изменений, что приводит к колоссальным задержкам и удержанию блокировок.

С другой стороны, попытка загрузить все 10 000 элементов в рамках одной гигантской транзакции приведет к переполнению оперативной памяти компьютера (так как файловая СУБД накапливает изменения текущей транзакции в ОЗУ) и вызовет падение платформы.

Оптимальным решением является группировка записи элементов в транзакции порциями (пакетами) по 100–500 элементов. Разберем шаблон реализации такого алгоритма:


&НаСервере
Процедура ЗагрузитьДанныеПорциями(СписокЭлементов)
    
    КоличествоВТранзакции = 0;
    РазмерПорции = 200; // Оптимальный размер пачки для файловой базы данных
    
    НачатьТранзакцию();
    
    Для Каждого ТекЭлемент Из СписокЭлементов Цикл
        
        // Создаем и заполняем элемент номенклатуры
        НовыйЭлемент = Справочники.Номенклатура.СоздатьЭлемент();
        НовыйЭлемент.Наименование = СокрЛП(ТекЭлемент.Наименование);
        // ... заполнение остальных реквизитов ...
        
        // Отключаем лишние обработчики и регистрацию изменений
        НовыйЭлемент.ОбменДанными.Загрузка = Истина;
        
        Попытка
            НовыйЭлемент.Записать();
            КоличествоВТранзакции = КоличествоВТранзакции + 1;
        Исключение
            ОтменитьТранзакцию();
            ЗаписатьВЛогОшибок("Не удалось записать: " + ТекЭлемент.Наименование + ". Ошибка: " + ОписаниеОшибки());
            // Перезапускаем транзакцию для продолжения обработки остальных элементов
            НачатьТранзакцию();
            КоличествоВТранзакции = 0;
            Продолжить;
        КонецПопытки;
        
        // Фиксируем порцию данных на диске и открываем новую транзакцию
        Если КоличествоВТранзакции >= РазмерПорции Тогда
            ЗафиксироватьТранзакцию();
            НачатьТранзакцию();
            КоличествоВТранзакции = 0;
        КонецЕсли;
        
    КонецЦикла;
    
    // Завершаем последнюю открытую транзакцию, если в ней остались элементы
    Если ТранзакцияАктивна() Тогда
        ЗафиксироватьТранзакцию();
    КонецЕсли;
    
КонецПроцедуры

Стратегия загрузки иерархических справочников

При импорте структуры папок и элементов «на лету» часто возникает ситуация, когда при создании элемента система пытается параллельно создать его родительскую группу. Это нарушает последовательность блокировок таблиц и вызывает сбои.

Наиболее надежный подход — разделить процесс загрузки на два независимых прохода:

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

Посмотрим на пример функции, которая создает структуру папок по переданному пути:


&НаСервере
Функция ПолучитьСоздатьРодителя(Знач ПутьКГруппе)
    
    МассивПапок = СтрРазделить(ПутьКГруппе, "/");
    Родитель = Справочники.Номенклатура.ПустаяСсылка();
    
    Для Каждого ИмяПапки Из МассивПапок Цикл
        ТекущаяПапка = Справочники.Номенклатура.НайтиПоНаименованию(ИмяПапки, Истина, Родитель);
        
        Если ТекущаяПапка.Пустая() Тогда
            НоваяГруппа = Справочники.Номенклатура.СоздатьГруппу();
            НоваяГруппа.Наименование = ИмяПапки;
            НоваяГруппа.Родитель = Родитель;
            НоваяГруппа.ОбменДанными.Загрузка = Истина;
            
            Попытка
                НоваяГруппа.Записать();
                Родитель = НоваяГруппа.Ссылка;
            Исключение
                ЗаписатьВЛог("Ошибка создания папки: " + ИмяПапки + ". " + ОписаниеОшибки());
                Родитель = Справочники.Номенклатура.ПустаяСсылка();
                Прервать;
            КонецПопытки;
        Иначе
            Родитель = ТекущаяПапка;
        КонецЕсли;
    КонецЦикла;
    
    Возврат Родитель;
    
КонецФункции

Часть 3. Устранение критических ошибок СУБД

При частых аварийных остановках, вызванных конфликтами блокировок и переполнением транзакций, в файловой базе данных (файл 1Cv8.1CD) могут возникнуть повреждения логической структуры таблиц. Симптомом этого является внезапное появление ошибок вида «Ошибка СУБД: Невосстановимая ошибка при выполнении запроса POST к ресурсу /e1cib/logForm».

Если вы столкнулись с подобным поведением, выполните следующие шаги по реанимации базы данных:

  1. Создайте резервную копию! Скопируйте файл 1Cv8.1CD в безопасное место перед проведением любых манипуляций.
  2. Проверка утилитой chdbfl.exe: Закройте все сеансы работы с базой данных. Найдите в каталоге установленной платформы 1С (папка bin) утилиту chdbfl.exe. Запустите ее, выберите файл базы данных, установите флаг «Исправлять обнаруженные ошибки» и выполните проверку физической целостности файла.
  3. Тестирование и исправление: Откройте базу данных в режиме «Конфигуратор». Перейдите в меню «Администрирование» — «Тестирование и исправление». Обязательно выберите пункты «Реиндексация таблиц информационной базы» и «Проверка логической целостности». Запустите процесс исправления.
  4. Очистка кэша: Удалите базу данных из списка запуска 1С и добавьте её заново — это позволит полностью очистить локальный кэш метаданных на компьютере разработчика, который часто является причиной ошибок связи с сервером при записи дампов.
  5. Использование Толстого клиента: Выполняйте масштабный импорт данных строго через запуск платформы в режиме «Толстый клиент (обычное приложение)» при прямом подключении к файлу базы. Это исключит веб-серверы, тонкие клиенты и веб-интерфейсы из цепочки передачи данных, предотвращая обрывы соединений по таймауту.

Подводя итог, отметим, что корректная работа с файловыми базами 1С при импорте данных требует обязательного отключения тяжелой бизнес-логики через установку свойства ОбменДанными.Загрузка = Истина, управления транзакциями с помощью порционной записи объектов и деления иерархической структуры на отдельные этапы загрузки.

← На главную