При обновлении платформы 1С:Предприятие 8.3 администраторы и разработчики иногда сталкиваются с непонятным поведением системы: в журнале регистрации появляется множество записей со статусом «Транзакция не завершена», а в консоли кластера или окне блокировок отображаются фантомные записи, у которых не указаны ни сеанс, ни имя компьютера, ни пользователь. Проанализируем подробно причины возникновения этого явления, выясним, насколько оно опасно для сохранности данных, и рассмотрим эффективные методы решения данной проблемы.
Разберем механизм работы журнала регистрации 1С. Когда в коде программы вызвана инструкция НачатьТранзакцию() или происходит неявный старт транзакции (например, перед записью объекта базы данных), платформа сразу создает в журнале регистрации запись с первоначальным статусом InProgress (в интерфейсе — «Транзакция не завершена»).
Дальнейшее развитие событий зависит от того, как завершился процесс:
ЗафиксироватьТранзакцию() статус меняется на «Транзакция завершена».ОтменитьТранзакцию() статус меняется на «Транзакция отменена».rphost, сеанс COM-соединения или фоновое задание завершились аварийно, либо были принудительно сняты до фиксации, итоговая запись в журнал просто не попадает. Первоначальная запись навсегда остается со статусом «Транзакция не завершена».Выясним главную причину беспокойства: могут ли «потеряться» или исказиться данные при наличии таких записей? Нет, целостность данных в информационной базе не нарушается. Все современные СУБД (MS SQL Server, PostgreSQL) работают по строгому стандарту ACID. Если транзакция не была явно зафиксирована командой commit, то при обрыве сеанса или завершении процесса СУБД выполняет автоматический откат (Rollback). Никакие частичные изменения в базы данных не попадают.
Однако эта ситуация создает существенные проблемы для производительности и стабильности работы:
Проанализируем специфику работы с внешними соединениями и фоновыми задачами. При использовании компоненты COMConnector созданные в оперативной памяти объекты удерживаются системным сборщиком мусора Windows (Garbage Collector). Если после выполнения обмена не очистить ссылки на полученные объекты, соединение с целевой базой останется активным в фоновом режиме, а не закрытые транзакции будут продолжать висеть на сервере.
Аналогично с регламентными заданиями: при возникновении зацикливания или неограниченного ожидания внешнего ресурса процесс зависает. Когда сервер приложений 1С завершает зависший сеанс по таймауту, записи отмены транзакции в журнал регистрации не попадают.
Рассмотрим, как правильно организовывать работу с внешними базами по COM. Перед завершением работы алгоритма необходимо явно обнулять все ссылки на объекты внешней базы и само внешнее соединение.
Посмотрим на пример правильного оформления кода:
COMСоединение = Новый COMОбъект("V83.COMConnector");
БазаПриемник = COMСоединение.Connect("Srvr=""Server1C"";Ref=""TradeBase"";");
// Выполнение операций с объектами внешней базы
ВыгруженныйОбъект = БазаПриемник.Справочники.Номенклатура.НайтиПоКоду("00001");
// Обязательная явная очистка ссылок перед закрытием
ВыгруженныйОбъект = Неопределено;
БазаПриемник = Неопределено;
COMСоединение = Неопределено;
Также рекомендуется избегать выполнения длительных транзакций записи с большим объемом данных через COM. Обработку данных лучше делить на небольшие порции (пакеты).
Рассмотрим классическую ошибку разработчиков, из-за которой транзакции остаются в подвешенном состоянии. Если внутри блока Попытка происходит ошибка СУБД, платформа помечает транзакцию как «только для отмены». Если в блоке Исключение отсутствует явная отмена, транзакция висит до окончания сеанса.
Приведем правильный шаблон обработки транзакций согласно стандартам разработки 1С:
НачатьТранзакцию();
Попытка
// Выполнение операций записи и обработки данных
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
// Логирование ошибки и проброс исключения выше
ЗаписьЖурналаРегистрации(" Ошибка обработки", УровеньЖурналаРегистрации.Ошибка,,, ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
ВызватьИсключение;
КонецПопытки;
Обратите внимание: вызов НачатьТранзакцию() должен располагаться строго перед блоком Попытка, а в блоке Исключение первым оператором всегда должен быть вызов ОтменитьТранзакцию().
Если проблема проявилась сразу после перехода на новый релиз платформы (например, при переходе с 8.3.9 на один из билдов 8.3.10), выполним следующие действия по диагностике и устранению:
TLOCK, TTIMEOUT, DEADLOCK и EXCP. Это позволит точно определить модуль, строчку кода и сеанс, где открывается и не завершается транзакция.Для избавлений от проблем с COM-соединениями проанализируем альтернативные варианты обмена данными, которые не удерживают сеансы и транзакции на сервере: