Разработка и отладка фоновых заданий в 1С часто преподносят сюрпризы. Одна из самых коварных проблем — ситуация, когда программный код успешно выполняется, отладчик показывает наличие записей в наборе, но после завершения работы задания данные в базе так и не появляются. При этом ручной запуск того же кода под тем же пользователем может работать абсолютно корректно. Рассмотрим подробно, почему так происходит, и разберем механизмы платформы 1С, которые приводят к «мистическому» исчезновению данных.
Проанализируем базовое правило платформы 1С: каждая открытая транзакция должна быть либо явно зафиксирована, либо явно отменена. Рассмотрим ситуацию, когда в коде используется метод НачатьТранзакцию(), но управление выходит из процедуры или функции до того, как будут вызваны ЗафиксироватьТранзакцию() или ОтменитьТранзакцию(). В обычном интерактивном сеансе система может выдать предупреждение, но фоновое задание работает иначе.
Выясним причину: когда поток фонового задания завершается, сеанс закрывается. Если в этот момент в стеке вызовов осталась незавершенная транзакция, платформа выполняет неявный откат. Все изменения, которые вы видели в отладчике, физически стираются из временных таблиц СУБД, так и не попав в основное хранилище. Именно поэтому в момент выполнения кода записи «видны» (они находятся в контексте текущей транзакции), а после — исчезают.
Посмотрим на пример, с которым столкнулся автор темы. Часто разработчики оборачивают вызов ЗафиксироватьТранзакцию() в блок Если...Тогда. Если условие в этом блоке перестает выполняться (даже если оно работало месяцами), транзакция остается «подвисшей».
Разберем типичный ошибочный код:
НачатьТранзакцию();
// ... блок записи в регистр ...
НаборЗаписей.Записать();
Если НужноеУсловиеВыполнено Тогда
ЗафиксироватьТранзакцию();
КонецЕсли;
// Если условие Ложь, транзакция не зафиксирована, данные пропадут!
Проблема усугубляется в фоновых заданиях, так как они часто обрабатывают массивы данных в цикле. Если ошибка фиксации происходит на одной из итераций, последствия могут затронуть весь пакет данных.
Важно понимать, что в 1С, сколько бы раз вы ни вызывали НачатьТранзакцию(), физическая транзакция на уровне СУБД всегда остается одной (самой внешней). Все последующие вызовы лишь увеличивают счетчик вложенности.
Рассмотрим ситуацию: если во «внутреннем» коде, который вызывается из вашего фонового задания, произошла ошибка или был вызван метод ОтменитьТранзакцию(), вся транзакция помечается как «битая». Даже если ваш основной код позже успешно вызовет ЗафиксироватьТранзакцию(), платформа проигнорирует этот вызов и все равно откатит изменения при завершении задания. При этом никаких явных исключений может не возникнуть, если код не проверяет статус транзакции методом ТранзакцияАктивна().
Если вы столкнулись с исчезновением записей, пройдем по шагам для выявления узкого места:
НачатьТранзакцию() должен иметь гарантированный путь к ЗафиксироватьТранзакцию(). Лучше всего использовать конструкцию Попытка...Исключение.ОтменитьТранзакцию() или где транзакция не закрывается.Пример надежной структуры кода для фонового задания:
НачатьТранзакцию();
Попытка
// Ваш код работы с регистрами
Набор = РегистрыСведений.ВашРегистр.СоздатьНаборЗаписей();
// ... заполнение набора ...
Набор.Записать();
ЗафиксироватьТранзакцию();
Исключение
ТекстОшибки = ОписаниеОшибки();
ОтменитьТранзакцию();
ЗаписьЖурналаРегистрации("ФоновоеЗадание.ОшибкаЗаписи",
УровеньЖурналаРегистрации.Ошибка,
,
,
"Ошибка при записи в регистр: " + ТекстОшибки);
КонецПопытки;
Проанализируем, почему поведение в фоне отличается от ручного запуска. Фоновое задание работает в неинтерактивном сеансе. Это означает:
Если...Тогда) завязана на роль пользователя или параметры экрана, в фоне это условие может выдать неожиданный результат Ложь.Рассмотрим дополнительные советы, которые помогут избежать подобных проблем в будущем:
1. Контроль вложенности. Старайтесь не открывать транзакции там, где это не требуется. Если ваш код может быть вызван из другой транзакции, проверяйте текущее состояние. Однако помните, что вызов ОтменитьТранзакцию() всегда отменяет все уровни вложенности.
2. Использование метода ТранзакцияАктивна(). Перед завершением сложной логики можно проверить состояние транзакции. Если транзакция уже была отменена во вложенном вызове, этот метод вернет Ложь.
3. Перенос кода в обработку. Для диагностики (как это сделал автор темы) полезно вынести код из фонового задания во внешнюю обработку и запустить его в обычном сеансе, но с имитацией тех же условий (те же параметры, тот же объем данных). Часто это позволяет увидеть ошибку «недостающей фиксации», которую скрывает фоновый режим.
4. Работа с периодическими регистрами. Если ваш регистр является периодическим, убедитесь, что вы правильно устанавливаете отборы по периоду. Иногда записи не исчезают, а просто перезаписывают друг друга из-за одинакового времени (периода) или записываются «в прошлое», из-за чего их не видно в актуальных срезах.
Подводя итог, можно сказать: исчезновение записей в 99% случаев связано с нарушением логики управления транзакциями. Тщательный аудит всех путей выполнения кода (особенно внутри условий Если) и грамотное использование блоков Попытка...Исключение с записью в журнал регистрации позволяют быстро найти и устранить причину проблемы.