Почему XML-файл выгрузки получается битым и как правильно использовать УдалитьРегистрациюИзменений

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

В практике разработки обменов данными в среде 1С:Предприятие часто встречается коварная проблема: процесс выгрузки завершается, файл создается, но при попытке его прочитать возникает фатальная ошибка "Extra content at the end of the document". При детальном рассмотрении оказывается, что в конце файла просто отсутствует закрывающий тег корневого элемента (например, </Выгрузка>). В этой статье мы разберем, почему метод УдалитьРегистрациюИзменений может быть косвенно связан с этой проблемой, и как правильно построить алгоритм выгрузки, чтобы избежать потери данных.

Анализируем причину: почему файл не закрывается?

Рассмотрим ситуацию, когда код выглядит логично: мы выбираем данные, записываем их в XML и тут же удаляем регистрацию изменений для этого объекта в плане обмена. Однако, если файл получается «обрубленным», это сигнализирует о том, что выполнение кода прервалось до того, как сработали методы ЗаписатьКонецЭлемента() и Закрыть(). Проанализируем основные причины такого поведения:

  1. Необработанное исключение: Если метод УдалитьРегистрациюИзменений вызывает ошибку (например, конфликт блокировок в СУБД), и этот код не обернут в блок Попытка...Исключение, выполнение всей процедуры немедленно прекращается. Платформа переходит к обработке ошибки, и строки кода, отвечающие за финализацию XML, игнорируются.
  2. Аварийное завершение процесса (Crash): Если при удалении регистрации изменений происходит критический сбой рабочего процесса (rphost), например, из-за нехватки оперативной памяти или внутренних ошибок платформы, буфер записи XML не сбрасывается на диск. В итоге мы получаем файл, записанный ровно до того момента, когда процесс «умер».
  3. Таймауты фоновых заданий: Если выгрузка выполняется в фоновом задании, оно может быть принудительно завершено кластером серверов по истечении лимита времени. Если это происходит в момент удаления регистрации большого объема данных, файл останется незавершенным.

Разбираем работу метода УдалитьРегистрациюИзменений

Метод ПланыОбмена.УдалитьРегистрациюИзменений() — это не просто «галочка» в памяти. Это полноценная операция записи в таблицу регистрации изменений (таблицу с префиксом _Chng). Рассмотрим, какие опасности она таит:

Блокировки данных: Когда мы удаляем регистрацию, СУБД накладывает блокировки на соответствующие строки. Если в этот же момент другой процесс (например, фоновое перепроведение документов) пытается зарегистрировать изменения для тех же объектов, возникает конфликт. В лучшем случае 1С подождет, в худшем — выбросит ошибку «Конфликт блокировок».

Транзакционная нагрузка: Вызов этого метода внутри цикла для каждого объекта создает множество мелких транзакций. Это значительно замедляет выгрузку. Проанализируем пример «опасного» кода:


Выборка = ПланыОбмена.ВыбратьИзменения(Узел, НомерСообщения);
Пока Выборка.Следующий() Цикл
    // Запись данных в XML
    ЗаписатьДанныеВXML(Выборка.Объект);
    
    // Удаление регистрации - ТУТ МОЖЕТ БЫТЬ ОШИБКА БЛОКИРОВКИ
    ПланыОбмена.УдалитьРегистрациюИзменений(Узел, Выборка.Объект); 
КонецЦикла;
ЗаписьXML.ЗаписатьКонецЭлемента();
ЗаписьXML.Закрыть();

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

Решение №1: Использование блока Попытка...Исключение

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


Попытка
    ПланыОбмена.УдалитьРегистрациюИзменений(Узел, СсылкаНаОбъект);
Исключение
    ЗаписьЖурналаРегистрации("ОбменДанными", УровеньЖурналаРегистрации.Ошибка, , , 
        "Не удалось снять с регистрации: " + ОписаниеОшибки());
    // Здесь мы решаем: продолжать ли запись файла или прервать процесс корректно
КонецПопытки;

Использование ОписаниеОшибки() в блоке исключения позволит вам увидеть реальную причину в Журнале регистрации, даже если визуально всё кажется исправным. Если там фигурирует «Конфликт блокировок», значит, проблема во взаимодействии с другими процессами.

Решение №2: Изменение архитектуры выгрузки (Рекомендуемое)

Рассмотрим более надежный подход. Чтобы гарантировать целостность XML-файла, операции с регистрацией изменений следует вынести за пределы процесса записи файла. Разберем по шагам наиболее стабильный алгоритм:

  1. Формирование файла: Выполняем полную выгрузку всех данных в XML.
  2. Закрытие файла: Вызываем ЗаписьXML.Закрыть(). Теперь файл гарантированно цел и готов к отправке.
  3. Массовое удаление регистрации: Только после успешного закрытия файла удаляем регистрацию изменений сразу для всей выборки или по номеру сообщения.

Проанализируем пример реализации такого подхода:


// 1. Подготовка
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл(ПутьКФайлу);
ЗаписьXML.ЗаписатьОбъявлениеXML();
ЗаписьXML.ЗаписатьНачалоЭлемента("Выгрузка");

ВыборкаИзменений = ПланыОбмена.ВыбратьИзменения(Узел, НомерСообщения);

// 2. Цикл записи данных (без удаления регистрации!)
Пока ВыборкаИзменений.Следующий() Цикл
    ДанныеОбъекта = ВыборкаИзменений.Объект;
    // ... логика записи ...
КонецЦикла;

// 3. Завершение работы с файлом
ЗаписьXML.ЗаписатьКонецЭлемента();
ЗаписьXML.Закрыть(); 

// 4. Безопасное удаление регистрации изменений
Попытка
    // Удаляем регистрацию сразу для всего сообщения
    ПланыОбмена.УдалитьРегистрациюИзменений(Узел, НомерСообщения);
Исключение
    Сообщить("Файл сформирован, но регистрация изменений не удалена: " + ОписаниеОшибки());
КонецПопытки;

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

Дополнительные рекомендации

Проанализируем ситуацию, когда даже перенос кода не помогает. В этом случае стоит обратить внимание на системные ресурсы:

Таким образом, мы выяснили, что ошибка в методе УдалитьРегистрациюИзменений действительно может привести к «битому» XML-файлу из-за нештатного прерывания выполнения кода. Соблюдение правильной последовательности действий (сначала закрыть файл, потом удалить регистрацию) и использование блоков обработки исключений сделает ваш обмен данными гораздо более отказоустойчивым.

← На главную