Разберем ситуацию, с которой разработчики часто сталкиваются при написании собственных механизмов обмена данными или отладке нетиповых конфигураций 1С. Допустим, мы выбираем изменения из плана обмена для отправки текущего сообщения, а затем пытаемся удалить регистрацию для предыдущего номера. Однако после этого вызова выясняется, что очистились вообще все изменения по узлу, включая те, которые предназначались для нового пакета. Посмотрим на этот механизм подробнее, выясним причину такого поведения платформы и проанализируем правильные способы работы с таблицами регистрации.
Зачастую в коде можно встретить подобную логику, когда разработчик пытается одновременно читать новые данные и очищать старые:
Выборка = ПланыОбмена.ВыбратьИзменения(Узел, НомерСообщения);
Сч = 0;
Пока Выборка.Следующий() Цикл
Сч = Сч + 1;
Сообщить(Выборка.Получить());
КонецЦикла;
Сообщить("Всего: "+Сч);
// Попытка удалить старые изменения:
ПланыОбмена.УдалитьРегистрациюИзменений(Узел, НомерСообщения - 1);
Если мы передадим в этот код НомерСообщения = 9, а затем вызовем удаление для номера 8, мы можем с удивлением обнаружить, что при следующей выборке для номера 9 изменений больше нет. Кажется, что метод удалил изменения, которые мы только что выбрали для нового сообщения. Но на самом деле причина кроется в скрытой логике присвоения номеров.
Рассмотрим по шагам, что происходит под капотом, когда мы вызываем метод ПланыОбмена.ВыбратьИзменения:
Именно поэтому, когда мы в коде вызываем ПланыОбмена.УдалитьРегистрациюИзменений(Узел, 8), система честно отрабатывает свое назначение: она удаляет из таблиц регистрации все записи, у которых номер сообщения меньше или равен переданному параметру (в нашем случае <= 8). Поскольку наши объекты так и остались с номером 8, они благополучно стираются, несмотря на то, что мы пытались прочитать их под 9-м номером.
Проанализируем ситуацию с архитектурной точки зрения. Пытаться очищать таблицы регистрации одновременно с генерацией нового пакета без подтверждения от узла-приемника — в корне неверный подход. Это нарушает принцип гарантированной доставки сообщений.
Если удалить изменения сразу при отправке, то потеря файла обмена (например, из-за сбоя сети) приведет к необратимому расхождению данных баз — отправитель будет считать, что изменения ушли, а приемник их так и не получит.
Стандартный и надежный сценарий (квитирование) выглядит так:
ЧтениеСообщения.НомерПринятого.ПланыОбмена.УдалитьРегистрациюИзменений(Узел, НомерПринятого).ПланыОбмена.ВыбратьИзменения. В этот пакет попадут объекты с невыгруженными изменениями (NULL), а также те старые изменения (если они были), которые не вошли в номер из квитанции (т.е. приемник их еще не подтвердил).В завершение разберем еще одну важную деталь, которую стоит учитывать программистам. Поскольку метод ВыбратьИзменения неявно модифицирует таблицы в базе данных (перезаписывает значения NULL на переданный номер сообщения), его вызов всегда устанавливает транзакционные блокировки на записи регистрации.
При больших объемах данных и интенсивном документообороте это может приводить к взаимным блокировкам и "подвисанию" системы. Поэтому в очень сложных интеграциях иногда целесообразно отказаться от стандартных методов платформы в пользу прямых запросов к таблицам (например, к Справочник.Номенклатура.Изменения) и порционного чтения с ручным снятием регистрации. Это дает больше контроля над производительностью, хотя и требует написания дополнительного кода для корректной маршрутизации.