Почему при отмене транзакции очищаются наборы записей в коллекции «Движения» документа?

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

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

Анализ ситуации: эксперимент с кодом

Для начала проанализируем ситуацию на конкретном примере. Представим сценарий, в котором мы программно добавляем записи в движения документа внутри транзакции, а затем намеренно ее отменяем. Посмотрим, как меняется количество строк в наборе записей.

Рассмотрим следующий фрагмент кода:


НачатьТранзакцию();

// Получаем объект документа и ссылку на одну из коллекций движений
ДокОбъект = СсылкаНаДокумент.ПолучитьОбъект();
НаборДвижений = ДокОбъект.Движения.Затраты;

// Добавляем новую запись в коллекцию в памяти
НаборДвижений.Добавить();
Сообщить("Количество строк ДО отмены: " + НаборДвижений.Количество()); // Выведет, например, 1

ОтменитьТранзакцию();

// Проверяем состояние объекта после отката транзакции
Сообщить("Количество строк ПОСЛЕ отмены: " + НаборДвижений.Количество()); // Выведет 0

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

Механизм автоматического отката состояния (Object State Rollback)

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

Проанализируем основные правила этого механизма:

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

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

Влияние времени жизни объекта на очистку

Рассмотрим интересную особенность, подмеченную практикующими разработчиками. Поведение платформы зависит от того, существует ли объект документа в памяти на момент завершения транзакции.

Посмотрим на модифицированный пример:


НачатьТранзакцию();

ф = ДокументСсылка.ПолучитьОбъект();
НаборСтрок = ф.Движения.Затраты;
НаборСтрок.Добавить();

Сообщить("До отмены =" + НаборСтрок.Количество());

// ВАЖНО: Уничтожаем связь с объектом документа
ф = Неопределено; 

ОтменитьТранзакцию();

Сообщить("После отмены =" + НаборСтрок.Количество());

В этом случае мы увидим, что количество строк не изменилось (осталось равным 1). Почему так происходит? Когда мы присвоили переменной ф значение Неопределено, связь между активной транзакцией и конкретным экземпляром объекта ДокументОбъект в памяти была разорвана. Платформа при выполнении ОтменитьТранзакцию() больше не видит активного «владельца» коллекции НаборСтрок, и триггер автоматической очистки не срабатывает.

Разница между связанными и независимыми наборами записей

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

  1. Набор из коллекции Движения:
    • Прямо связан с объектом документа.
    • Автоматически очищается платформой при откате транзакции для предотвращения записи некорректных данных.
    • Считается частью «состояния документа», которое платформа обязана защищать.
  2. Независимый набор записей (СоздатьНаборЗаписей()):
    • Является самостоятельным объектом управления.
    • Сохраняет свое содержимое в оперативной памяти даже после ОтменитьТранзакцию().
    • Откат происходит только на уровне базы данных (если был вызван метод Записать()), но не на уровне коллекции строк в памяти.

Практические рекомендации для разработчика

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

  1. Не полагайтесь на состояние объектов после отмены: Любой объект (будь то документ, справочник или набор записей), который участвовал в транзакции, после ее отмены должен считаться «грязным» или «протухшим».
  2. Принудительное перечитывание: Если после отмены транзакции вам необходимо продолжить работу с тем же документом, обязательно вызовите метод Прочитать() или заново получите объект из ссылки через ПолучитьОбъект(). Это гарантирует, что данные в памяти будут синхронизированы с текущим состоянием базы данных.
  3. Учитывайте режим записи: Очистка коллекции Движения происходит наиболее агрессивно, если у набора было установлено свойство Записывать = Истина. Это сигнал платформе, что данные планировались к фиксации, и при ошибке их нужно гарантированно сбросить.
  4. Использование независимых наборов для логов: Если вам нужно сохранить какие-то данные о ходе транзакции даже в случае ее падения (например, лог ошибок в регистр сведений), помните, что обычная запись в транзакции будет откачена. Для таких целей используются механизмы вне рамок основной транзакции (например, фоновые задания или запись в текстовые файлы/внешние базы).

Таким образом, очистка движений — это не ошибка платформы, а защитный механизм. Он предотвращает ситуацию, когда разработчик в блоке Попытка...Исключение после отмены транзакции может случайно вызвать Записать() у того же объекта документа, тем самым отправив в базу данных промежуточные, недоформированные строки движений.

← На главную