Ошибки платформы 1С

Конфликт блокировок при выполнении транзакции и Попытка Исключение в 1С 8.3

Коротко

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

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

Почему возникает «Конфликт блокировок при выполнении транзакции» в 1С 8.3

В управляемом режиме блокировок платформа 1С 8.3 и СУБД (PostgreSQL, MS SQL Server) защищают изменяемые данные от одновременной перезаписи двумя контурами: менеджер управляемых блокировок кластера 1С контролирует логические пространства записей (регистры, объекты), а СУБД удерживает физические блокировки строк и таблиц до фиксации или отката транзакции. Когда в 1С возникает конфликт блокировок при выполнении транзакции, в журнале регистрации или окне пользователя появляется одно из трёх состояний:

  • Превышено время ожидания (Timeout). Первая транзакция заблокировала измерения регистра или объект и долго не завершается. Вторая транзакция пытается захватить пересекающийся набор измерений, ждёт установленный в свойстве конфигурации «Время ожидания блокировки данных» интервал (по умолчанию 20 секунд) и падает по таймауту.
  • Взаимоблокировка (Deadlock) на управляемых блокировках или в СУБД. Транзакция А сначала блокирует номенклатуру №1, затем пытается заблокировать номенклатуру №2. Параллельная транзакция Б уже удерживает номенклатуру №2 и запрашивает номенклатуру №1. Замкнутый цикл ожидания разрывается мгновенно (без ожидания 20 секунд) выбором жертвы и выбросом исключения.
  • Эскалация и широкие блокировки из-за неполного отбора. Если при записи набора записей или в объекте БлокировкаДанных не заполнено хотя бы одно измерение, входящее в ключевой индекс, блокируется весь диапазон (или весь регистр целиком), останавливая работу соседних пользователей и фоновых заданий.

Три главные причины конфликтов блокировок в коде 1С

На практике 90% конфликтов при проведении документов и работе регламентных заданий порождаются тремя типовыми ошибками проектирования транзакций в модулях объектов и общих модулях:

  1. Чтение данных запросом внутри транзакции без предварительной управляемой блокировки. В режиме управляемых блокировок СУБД работает на уровне изоляции READ COMMITTED (или версионирования строк в PostgreSQL), поэтому обычный запрос ВЫБРАТЬ внутри транзакции не блокирует читаемые остатки от изменения другим сеансом. Если два документа одновременно прочитают свободный остаток без предварительного вызова БлокировкаДанных.Заблокировать(), оба посчитают, что товара хватает, и столкнутся уже на этапе физической записи таблиц итогов регистра либо спишут остаток в минус.
  2. Длительные вычисления, файлы и внешние HTTP-вызовы внутри транзакции. Как только вызван метод НачатьТранзакцию() или стартовала ОбработкаПроведения, все захваченные управляемые и транзакционные блокировки удерживаются до самой фиксации. Если внутри этого участка выполняется обмен по HTTP, чтение тяжёлого файла Excel, сложный аналитический запрос без индексов или отправка почты, транзакция растягивается с миллисекунд до десятков секунд — и все параллельные сеансы отваливаются по 20-секундному таймауту.
  3. Отсутствие сортировки строк табличной части документа перед формированием движений. Когда в документе «Реализация товаров и услуг» позиции расположены в порядке Товар Б, Товар А, а в параллельном документе «Перемещение товаров» — в порядке Товар А, Товар Б, запись таблиц итогов регистра накопления в СУБД блокирует строки в противоположном порядке. Это классическая причина взаимоблокировок (deadlock) на ровном месте даже при быстрых транзакциях.
Найти проблемное место в модуле проведения проще с ИИ Консолью кода 1С: поручите агенту проверить движения документа и подписки на события в вашей базе — он найдёт чтение остатков без предварительной блокировки, несортированные табличные части и долгие вызовы внутри транзакции.

Как правильно блокировать данные через БлокировкаДанных перед чтением остатков

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

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

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

Обратите внимание: управляемая блокировка действует до конца текущей транзакции (внутри ОбработкаПроведения транзакция уже открыта платформой автоматически). Чтобы не получить взаимоблокировку на уровне СУБД при последующей записи набора движений, запрос по табличной части документа обязательно снабжают предложением УПОРЯДОЧИТЬ ПО по измерениям регистра (например, УПОРЯДОЧИТЬ ПО Товары.Номенклатура) и группируют дубли строк.

Как сократить время транзакции и вынести тяжёлые операции наружу

Золотое правило работы с НачатьТранзакцию(): внутри транзакционного блока должны оставаться только блокировка, быстрый контрольный запрос и запись в базу. Любую подготовку данных — разбор JSON/XML, обращение к внешнему веб-сервису, расчёт сложных цен или чтение файлов с диска — выполняют до открытия транзакции:

// 1. ВНЕ транзакции: долгий HTTP-запрос и подготовка таблицы
ДанныеПоставщика = ПолучитьСтатусыЗаказовПоHTTP(МассивНомеров);
ДанныеПоставщика.Сортировать("ЗаказПокупателя Возр");

// 2. ВНУТРИ транзакции: только быстрая блокировка и запись
НачатьТранзакцию();
Попытка
    Блокировка = Новый БлокировкаДанных;
    Элемент = Блокировка.Добавить("РегистрСведений.СтатусыЗаказов");
    Элемент.Режим = РежимБлокировкиДанных.Исключительный;
    Элемент.ИсточникДанных = ДанныеПоставщика;
    Элемент.ИспользоватьИзИсточникаДанных("Заказ", "ЗаказПокупателя");
    Блокировка.Заблокировать();

    Для Каждого СтрокаДанных Из ДанныеПоставщика Цикл
        ЗаписатьСтатусВРегистр(СтрокаДанных.ЗаказПокупателя, СтрокаДанных.Статус);
    КонецЦикла;

    ЗафиксироватьТранзакцию();
Исключение
    Если ТранзакцияАктивна() Тогда
        ОтменитьТранзакцию();
    КонецЕсли;
    ВызватьИсключение;
КонецПопытки;
Контур транзакции
НачатьТранзакцию()открывает транзакцию (или увеличивает счётчик вложенности)
ЗафиксироватьТранзакцию()сохраняет изменения в БД и снимает блокировки верхнего уровня
ТранзакцияАктивна()возвращает Булево (Истина, если транзакция открыта и не отменена)
ОтменитьТранзакцию()откатывает изменения и переводит внешнюю транзакцию в режим отмены

Попытка Исключение в 1С 8.3: ИнформацияОбОшибке и функции представления

Конструкция Попытка ... Исключение ... КонецПопытки в 1С 8.3 перехватывает runtime-исключения (включая конфликт блокировок, ошибки записи и вызова методов контекста). Старая функция ОписаниеОшибки() возвращает одну плоскую строку со склеенным текстом и не позволяет отделить технический стек от понятного пользователю сообщения. В современном коде 1С 8.3 используют объект ИнформацияОбОшибке, получаемый функцией глобального контекста ИнформацияОбОшибке(), и три специализированные функции форматирования:

Попытка
    ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
Исключение
    Инфо = ИнформацияОбОшибке();

    // Короткий текст для пользователя без модулей и номеров строк
    ТекстПользователю = КраткоеПредставлениеОшибки(Инфо);

    // Полный стек со всеми вложенными «по причине:» для администратора
    ТекстВЖурнал = ПодробноеПредставлениеОшибки(Инфо);

    // Только первопричина (самая глубокая ошибка в цепочке Инфо.Причина)
    ТекстПервопричины = ПредставлениеПричиныОшибки(Инфо);

    ЗаписьЖурналаРегистрации(
        "ПроведениеДокументов.ОшибкаБлокировки",
        УровеньЖурналаРегистрации.Ошибка,
        ДокументОбъект.Метаданные(),
        ДокументОбъект.Ссылка,
        ТекстВЖурнал,
        РежимТранзакцииЗаписиЖурналаРегистрации.Независимая);

    Сообщить(ТекстПользователю);
КонецПопытки;
Обработка ошибки
ИнформацияОбОшибке()→ ИнформацияОбОшибке (свойства Описание, ИмяМодуля, НомерСтроки, ИсходнаяСтрока, Причина)
КраткоеПредставлениеОшибки(Инфо)→ Строка для показа пользователю без технического стека
ПодробноеПредставлениеОшибки(Инфо)→ Строка с модулем, строкой кода и всей цепочкой причин
ПредставлениеПричиныОшибки(Инфо)→ Строка описания корневой причины (без внешних обёрток)

Такое разделение решает сразу две задачи по запросу «1с попытка исключение сообщение об ошибке»: пользователь видит лаконичное сообщение без пугающих путей вида {Документ.Реализация.МодульОбъекта(415)}, а в журнал регистрации с режимом РежимТранзакцииЗаписиЖурналаРегистрации.Независимая гарантированно сохраняется полный стек вызова (даже если основная транзакция будет отменена).

Почему в блоке Исключение обязательно вызывать ОтменитьТранзакцию()

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

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

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

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

    ЗаписьЖурналаРегистрации(
        "РегламентныеОперации.ОшибкаТранзакции",
        УровеньЖурналаРегистрации.Ошибка,
        ,
        СсылкаНаДокумент,
        ПодробноеПредставлениеОшибки(Инфо),
        РежимТранзакцииЗаписиЖурналаРегистрации.Независимая);

    ВызватьИсключение;
КонецПопытки;

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

В ИИ Консоли кода 1С можно попросить агента обернуть пакетную обработку документов в безопасные транзакции с повторными попытками при конфликте блокировок и независимой записью ошибок в журнал регистрации.

Сводная таблица методов работы с транзакциями, блокировками и ошибками в 1С 8.3

Конструкция / метод 1С 8.3 Назначение Где и как применять
Новый БлокировкаДанных + Заблокировать() Явная управляемая блокировка измерений регистра или объектов перед чтением и изменением Внутри активной транзакции строго до выполнения запроса контроля остатков
НачатьТранзакцию() / ЗафиксироватьТранзакцию() Открытие и успешная фиксация транзакции изменений БД Оборачивать только быстрые операции чтения под блокировкой и записи без HTTP и файлов
ТранзакцияАктивна() + ОтменитьТранзакцию() Проверка открытой транзакции и полный откат изменений при сбое В начале блока Исключение сразу после сохранения ИнформацияОбОшибке()
ИнформацияОбОшибке() Получение структурированного объекта ошибки (модуль, строка, описание, вложенная Причина) В первой строке секции Исключение вместо устаревшего ОписаниеОшибки()
КраткоеПредставлениеОшибки(Инфо) Формирование понятного пользователю текста без технических имён модулей и номеров строк Для вывода в интерфейс (Сообщить, ПоказатьПредупреждение)
ПодробноеПредставлениеОшибки(Инфо) Полное описание ошибки со стеком модулей, номерами строк и цепочкой «по причине:» Для записи в ЗаписьЖурналаРегистрации и диагностики разработчиком
ПредставлениеПричиныОшибки(Инфо) Текст самого глубокого (первичного) исключения без внешних обёрток «Ошибка при вызове метода контекста» Для быстрой фильтрации и классификации первопричины сбоя в логах

Частые ошибки при работе с транзакциями и Попытка Исключение в 1С

  • Чтение остатков в ОбработкаПроведения без предварительного БлокировкаДанных.Заблокировать(). В управляемом режиме запрос сам по себе не блокирует регистр от параллельного чтения, что приводит либо к списанию в минус, либо к конфликту блокировок СУБД на записи движений.
  • Незаполненное измерение в ЭлементБлокировкиДанных. Если в регистре три измерения (Организация, Склад, Номенклатура), а в блокировке указан только Склад, платформа заблокирует все товары и организации по этому складу.
  • Запись движений по несортированной табличной части документа. Разный порядок номенклатуры или складов в параллельно проводимых документах вызывает встречный захват строк в СУБД (deadlock). Всегда сортируйте и сворачивайте данные по измерениям регистра.
  • HTTP-запросы, ЧтениеXML и работа с файлами внутри НачатьТранзакцию(). Сетевые задержки удерживают блокировки в БД десятки секунд, из-за чего соседние сеансы падают по 20-секундному таймауту ожидания.
  • Пропуск ОтменитьТранзакцию() в секции Исключение. Перехват ошибки в 1С не откатывает транзакцию автоматически: она остаётся открытой или аварийной, удерживая блокировки и ломая следующие запросы.
  • Использование РежимТранзакцииЗаписиЖурналаРегистрации.Транзакционная при логировании ошибки до отката. Если записать ошибку в транзакционном режиме, а затем вызвать ОтменитьТранзакцию(), запись в журнале регистрации может откатиться вместе с транзакцией. Для ошибок используйте Независимая.
  • Вызов ВызватьИсключение ОписаниеОшибки(); вместо пустого ВызватьИсключение;. Передача строки в ВызватьИсключение создаёт новое исключение на текущей строке и затирает исходную цепочку ИнформацияОбОшибке().Причина.

Частые вопросы о конфликтах блокировок и Попытка Исключение в 1С 8.3

Почему возникает ошибка «Конфликт блокировок при выполнении транзакции» в 1С 8.3?

Ошибка возникает, когда транзакция не смогла получить управляемую блокировку 1С или блокировку СУБД в течение установленного времени ожидания (по умолчанию 20 секунд), либо когда менеджер блокировок обнаружил взаимоблокировку (deadlock) двух сеансов, захватывающих одни и те же ресурсы в разном порядке.

Чем ИнформацияОбОшибке() лучше ОписаниеОшибки() в блоке Попытка Исключение?

Функция ОписаниеОшибки() возвращает плоскую строку, а ИнформацияОбОшибке() возвращает объект со свойствами Описание, ИмяМодуля, НомерСтроки и ссылкой на вложенную ошибку Причина. Из него через КраткоеПредставлениеОшибки() получают понятный текст для пользователя, а через ПодробноеПредставлениеОшибки() — полный стек для журнала регистрации.

Нужно ли вызывать ОтменитьТранзакцию() внутри секции Исключение?

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

Тарифы

ИИ Консоль кода 1С

Лайт

Бесплатно
≈ 0,1 млн токенов на 2 дн.

Для ознакомления с возможностями

  • Все функции бесплатно на 2 дня +2 дня за подписку на Telegram или Max
  • Полный набор инструментов без урезаний
  • Без ввода карты

Старт

1990 руб. в месяц
≈ 10 млн токенов

Для индивидуального использования

Продвинутый

Популярный выбор
3990 руб. в месяц
≈ 25 млн токенов

Для интенсивного использования или команд

  • До 15 конфигураций
  • Встроенный агент запросов 1С
  • Свои подписки Codex, Claude и любые OpenAI-совместимые
  • Внешние обработки без Конфигуратора
  • Приоритетная техническая поддержка