К статье
Практический материал

Разработка и Конфигурация · 11 мин

Транзакции в 1С: что это и существуют ли вложенные транзакции

Что такое транзакция в 1С, как работают НачатьТранзакцию и ЗафиксироватьТранзакцию, есть ли вложенные транзакции в 1С и как правильно писать код.

Транзакция в 1С — это механизм, который гарантирует, что группа операций с базой данных будет выполнена либо целиком, либо не будет выполнена вообще. Если при записи документа, проведении или пакетном изменении данных на каком-то шаге возникает ошибка, все уже сделанные в рамках транзакции изменения откатываются, и база остаётся в согласованном состоянии. Без транзакций частично записанные данные приводили бы к рассогласованию регистров, «битым» документам и ошибкам в отчётах.

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

Один из самых частых вопросов — поддерживает ли 1С вложенные транзакции. Короткий ответ: полноценных вложенных транзакций с независимой фиксацией и откатом в платформе нет, но вызовы НачатьТранзакцию() можно вкладывать друг в друга, и платформа корректно считает уровни вложенности. Как именно это работает и какие ошибки из-за этого возникают — разбираем ниже.

Что такое транзакция и зачем она нужна в 1С

Транзакция объединяет несколько операций чтения и записи в единое целое. Классический пример — проведение документа «Реализация товаров»: платформа должна записать сам документ, сформировать движения по регистру остатков, регистру продаж и, возможно, по бухгалтерским счетам. Если после записи части движений произойдёт сбой, без транзакции в базе останется документ с неполными движениями — остатки на складе и данные отчётов разойдутся с реальностью.

Транзакция обеспечивает четыре ключевых свойства, известных как ACID: атомарность (всё или ничего), согласованность (данные не нарушают правил), изолированность (параллельные пользователи не видят промежуточных состояний) и долговечность (зафиксированные изменения не пропадут). В клиент-серверном варианте 1С эти свойства реализует СУБД — Microsoft SQL Server, PostgreSQL, Oracle или IBM DB2, а платформа транслирует команды транзакций на уровень базы данных.

Как платформа 1С управляет транзакциями автоматически

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

Важно понимать, что автоматическая транзакция охватывает только те операции, которые платформа считает единым действием. Если вы в своём коде последовательно записываете пять разных объектов пятью отдельными вызовами Записать(), каждый из них зафиксируется отдельно, и сбой на третьем объекте оставит первые два записанными. Именно в таких сценариях нужна явная транзакция.

Явное управление: НачатьТранзакцию, ЗафиксироватьТранзакцию, ОтменитьТранзакцию

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

bsl
НачатьТранзакцию();
Попытка
    ДокументОбъект.Записать();
    РегистрыНакопления.Остатки.ЗаписатьДвижения(НаборЗаписей);
    ЗафиксироватьТранзакцию();
Исключение
    ОтменитьТранзакцию();
    ВызватьИсключение ОписаниеОшибки();
КонецПопытки;

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

Порядок безопасной работы с явной транзакцией
  1. 1
    Вызовите НачатьТранзакцию() непосредственно перед первой операцией записи
  2. 2
    Поместите все операции записи в блок Попытка
  3. 3
    При успехе вызовите ЗафиксироватьТранзакцию()
  4. 4
    В блоке Исключение вызовите ОтменитьТранзакцию() и обработайте ошибку
  5. 5
    Убедитесь, что нет путей выполнения кода, где транзакция остаётся незакрытой

Есть ли в 1С вложенные транзакции

Формально платформа позволяет вызывать НачатьТранзакцию() внутри уже открытой транзакции — ошибки не будет. Но это не создаёт независимую вложенную транзакцию в смысле SQL (savepoint). Платформа просто ведёт счётчик уровней вложенности: каждый НачатьТранзакцию() увеличивает счётчик на единицу, каждый ЗафиксироватьТранзакцию() уменьшает.

Реальная фиксация в базе данных происходит только тогда, когда счётчик достигает нуля — то есть при закрытии самой внешней транзакции. Все «внутренние» вызовы ЗафиксироватьТранзакцию() фактически ничего не фиксируют, они лишь уменьшают счётчик. Поэтому нельзя зафиксировать внутреннюю часть работы и откатить внешнюю: либо фиксируется всё, либо ничего.

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

Как писать процедуры, которые вызываются из разных контекстов

Типичная проблема: процедура записи данных должна работать и сама по себе (открывая свою транзакцию), и внутри чужой уже открытой транзакции. Правильное решение — проверять активность транзакции функцией ТранзакцияАктивна(). Если транзакция уже открыта, процедура не должна ни начинать новую, ни фиксировать, ни отменять — управление остаётся за вызывающим кодом.

bsl
ОткрытаСвояТранзакция = НЕ ТранзакцияАктивна();
Если ОткрытаСвояТранзакция Тогда
    НачатьТранзакцию();
КонецЕсли;
Попытка
    // операции записи
    Если ОткрытаСвояТранзакция Тогда
        ЗафиксироватьТранзакцию();
    КонецЕсли;
Исключение
    Если ОткрытаСвояТранзакция Тогда
        ОтменитьТранзакцию();
    КонецЕсли;
    ВызватьИсключение ОписаниеОшибки();
КонецПопытки;

Такой шаблон — фактический стандарт в типовых конфигурациях. Он гарантирует, что процедура не «сломает» внешнюю транзакцию вызывающего кода и при этом корректно работает при самостоятельном вызове. Обратите внимание: при ошибке внутри чужой транзакции процедура не отменяет её сама, а пробрасывает исключение — решение об откате принимает владелец транзакции.

Типичные ошибки
01
Вызов ОтменитьТранзакцию() во вложенной процедуре — откатывается вся внешняя транзакция, а последующая фиксация падает с ошибкой
02
Вызов ЗафиксироватьТранзакцию() без проверки ТранзакцияАктивна() — ошибка «Транзакция не активна»
03
Длительные операции внутри транзакции (запросы к внешним сервисам, диалоги с пользователем) — удержание блокировок и взаимоблокировки
04
Отсутствие блока Исключение — при ошибке транзакция остаётся открытой и держит блокировки до конца сеанса

Блокировки, длительность транзакций и производительность

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

Практические рекомендации: не вызывайте внутри транзакции методы, требующие интерактивного взаимодействия с пользователем; не выполняйте обращения к веб-сервисам и длительные фоновые операции; при пакетной обработке большого числа объектов разбивайте работу на порции с отдельной транзакцией на каждую порцию. Для управляемых блокировок платформа предоставляет объект БлокировкаДанных, который позволяет явно указать, какие данные блокируются, и снизить избыточность.

Как проверить, что транзакция работает правильно

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

Контрольные точки

Как проверить результат

Штатное выполнение
Норма

Все объекты записаны, данные согласованы

Тревожный признак

Часть объектов записана, часть нет

Искусственная ошибка в середине
Норма

В базе нет ни одного изменения из транзакции

Тревожный признак

Остались частично записанные данные

Вызов внутри чужой транзакции
Норма

Процедура не начинает и не фиксирует транзакцию сама

Тревожный признак

Ошибка «Транзакция не активна» или преждевременная фиксация

Длительность транзакции
Норма

Между началом и фиксацией только операции записи

Тревожный признак

Внутри транзакции диалоги, веб-запросы, долгие расчёты

Для диагностики проблем с блокировками используйте технологический журнал с событиями TLOCK, TDEADLOCK и TTIMEOUT, а в клиент-серверном варианте — консоль кластера и средства СУБД. Эти инструменты показывают, какие сеансы и какие данные конфликтуют, и помогают найти слишком длинные транзакции.

Часто задаваемые вопросы

Можно ли зафиксировать часть транзакции, а часть откатить?

Нет, в 1С нет механизма точек сохранения (savepoint). Транзакция атомарна целиком: фиксируется или откатывается всё. Если нужна частичная фиксация, разбейте работу на несколько последовательных транзакций.

Что произойдёт, если внутри моей транзакции вызвать проведение документа?

Проведение выполнится внутри вашей внешней транзакции: платформа увеличит счётчик вложенности, но отдельную транзакцию не откроет. Если потом вы отмените свою транзакцию, движения документа тоже откатятся.

Чем опасна незакрытая транзакция?

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

Как узнать в коде, открыта ли сейчас транзакция?

Используйте функцию встроенного языка ТранзакцияАктивна() — она возвращает Истина, если счётчик вложенности больше нуля. Это стандартный способ решить, нужно ли процедуре открывать собственную транзакцию.

Почему возникает ошибка «Транзакция не активна»?

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

Нужны ли явные транзакции при проведении документа?

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

Итог

Что важно запомнить

Транзакция в 1С гарантирует целостность данных по принципу «всё или ничего». Полноценных вложенных транзакций нет — есть счётчик уровней: реально фиксируется только внешняя транзакция, а откат на любом уровне отменяет всё. В переиспользуемых процедурах проверяйте ТранзакцияАктивна(), держите транзакции короткими и всегда закрывайте их в блоке Исключение.