Конфликт блокировок при выполнении транзакции и Попытка Исключение в 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% конфликтов при проведении документов и работе регламентных заданий порождаются тремя типовыми ошибками проектирования транзакций в модулях объектов и общих модулях:
-
Чтение данных запросом внутри транзакции без предварительной управляемой блокировки.
В режиме управляемых блокировок СУБД работает на уровне изоляции
READ COMMITTED(или версионирования строк в PostgreSQL), поэтому обычный запросВЫБРАТЬвнутри транзакции не блокирует читаемые остатки от изменения другим сеансом. Если два документа одновременно прочитают свободный остаток без предварительного вызоваБлокировкаДанных.Заблокировать(), оба посчитают, что товара хватает, и столкнутся уже на этапе физической записи таблиц итогов регистра либо спишут остаток в минус. -
Длительные вычисления, файлы и внешние HTTP-вызовы внутри транзакции.
Как только вызван метод
НачатьТранзакцию()или стартовалаОбработкаПроведения, все захваченные управляемые и транзакционные блокировки удерживаются до самой фиксации. Если внутри этого участка выполняется обмен по HTTP, чтение тяжёлого файла Excel, сложный аналитический запрос без индексов или отправка почты, транзакция растягивается с миллисекунд до десятков секунд — и все параллельные сеансы отваливаются по 20-секундному таймауту. -
Отсутствие сортировки строк табличной части документа перед формированием движений.
Когда в документе «Реализация товаров и услуг» позиции расположены в порядке
Товар Б, Товар А, а в параллельном документе «Перемещение товаров» — в порядкеТовар А, Товар Б, запись таблиц итогов регистра накопления в СУБД блокирует строки в противоположном порядке. Это классическая причина взаимоблокировок (deadlock) на ровном месте даже при быстрых транзакциях.
Как правильно блокировать данные через БлокировкаДанных перед чтением остатков
Стандарт разработки 1С требует в управляемом режиме сначала явно заблокировать нужные комбинации измерений
объектом БлокировкаДанных (в режиме РежимБлокировкиДанных.Исключительный), и только
после успешного вызова Заблокировать() читать остатки запросом и формировать движения.
Если блокируемые значения лежат в табличной части документа или результате запроса, их передают целиком через
ИсточникДанных и метод ИспользоватьИзИсточникаДанных(). Платформа сама отсортирует
и дедуплицирует ключи блокировки, исключая deadlock при захвате:
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.ТоварыНаСкладах");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.УстановитьЗначение("Склад", ЭтотОбъект.Склад);
ЭлементБлокировки.ИсточникДанных = ЭтотОбъект.Товары;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Номенклатура", "Номенклатура");
Блокировка.Заблокировать();
Обратите внимание: управляемая блокировка действует до конца текущей транзакции (внутри
ОбработкаПроведения транзакция уже открыта платформой автоматически). Чтобы не получить
взаимоблокировку на уровне СУБД при последующей записи набора движений, запрос по табличной части документа
обязательно снабжают предложением УПОРЯДОЧИТЬ ПО по измерениям регистра (например,
УПОРЯДОЧИТЬ ПО Товары.Номенклатура) и группируют дубли строк.
Как сократить время транзакции и вынести тяжёлые операции наружу
Золотое правило работы с НачатьТранзакцию(): внутри транзакционного блока должны оставаться только
блокировка, быстрый контрольный запрос и запись в базу. Любую подготовку данных — разбор JSON/XML, обращение
к внешнему веб-сервису, расчёт сложных цен или чтение файлов с диска — выполняют до открытия транзакции:
// 1. ВНЕ транзакции: долгий HTTP-запрос и подготовка таблицы
ДанныеПоставщика = ПолучитьСтатусыЗаказовПоHTTP(МассивНомеров);
ДанныеПоставщика.Сортировать("ЗаказПокупателя Возр");
// 2. ВНУТРИ транзакции: только быстрая блокировка и запись
НачатьТранзакцию();
Попытка
Блокировка = Новый БлокировкаДанных;
Элемент = Блокировка.Добавить("РегистрСведений.СтатусыЗаказов");
Элемент.Режим = РежимБлокировкиДанных.Исключительный;
Элемент.ИсточникДанных = ДанныеПоставщика;
Элемент.ИспользоватьИзИсточникаДанных("Заказ", "ЗаказПокупателя");
Блокировка.Заблокировать();
Для Каждого СтрокаДанных Из ДанныеПоставщика Цикл
ЗаписатьСтатусВРегистр(СтрокаДанных.ЗаказПокупателя, СтрокаДанных.Статус);
КонецЦикла;
ЗафиксироватьТранзакцию();
Исключение
Если ТранзакцияАктивна() Тогда
ОтменитьТранзакцию();
КонецЕсли;
ВызватьИсключение;
КонецПопытки;
Попытка Исключение в 1С 8.3: ИнформацияОбОшибке и функции представления
Конструкция Попытка ... Исключение ... КонецПопытки в 1С 8.3 перехватывает runtime-исключения
(включая конфликт блокировок, ошибки записи и вызова методов контекста). Старая функция
ОписаниеОшибки() возвращает одну плоскую строку со склеенным текстом и не позволяет отделить
технический стек от понятного пользователю сообщения. В современном коде 1С 8.3 используют объект
ИнформацияОбОшибке, получаемый функцией глобального контекста ИнформацияОбОшибке(),
и три специализированные функции форматирования:
Попытка
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
Исключение
Инфо = ИнформацияОбОшибке();
// Короткий текст для пользователя без модулей и номеров строк
ТекстПользователю = КраткоеПредставлениеОшибки(Инфо);
// Полный стек со всеми вложенными «по причине:» для администратора
ТекстВЖурнал = ПодробноеПредставлениеОшибки(Инфо);
// Только первопричина (самая глубокая ошибка в цепочке Инфо.Причина)
ТекстПервопричины = ПредставлениеПричиныОшибки(Инфо);
ЗаписьЖурналаРегистрации(
"ПроведениеДокументов.ОшибкаБлокировки",
УровеньЖурналаРегистрации.Ошибка,
ДокументОбъект.Метаданные(),
ДокументОбъект.Ссылка,
ТекстВЖурнал,
РежимТранзакцииЗаписиЖурналаРегистрации.Независимая);
Сообщить(ТекстПользователю);
КонецПопытки;
Такое разделение решает сразу две задачи по запросу «1с попытка исключение сообщение об ошибке»: пользователь
видит лаконичное сообщение без пугающих путей вида {Документ.Реализация.МодульОбъекта(415)},
а в журнал регистрации с режимом РежимТранзакцииЗаписиЖурналаРегистрации.Независимая гарантированно
сохраняется полный стек вызова (даже если основная транзакция будет отменена).
Почему в блоке Исключение обязательно вызывать ОтменитьТранзакцию()
Одно из самых опасных заблуждений в 1С — считать, что вход в секцию Исключение автоматически
закрывает транзакцию, открытую вызовом НачатьТранзакцию(). На самом деле транзакция остаётся
активной (или переходит в состояние «только для отката» после ошибки СУБД). Если забыть вызвать
ОтменитьТранзакцию():
- все захваченные транзакционные и управляемые блокировки продолжат висеть в сеансе или до завершения серверного вызова, блокируя остальных пользователей;
- следующий же запрос к базе данных в этом сеансе упадёт со вторичной ошибкой «В данной транзакции уже происходили ошибки»;
- при выходе из процедуры с незакрытой транзакцией платформа сама выбросит исключение о незавершённой транзакции, замаскировав исходную ошибку.
Почему по стандарту ИТС (#std783) сам вызов НачатьТранзакцию() ставят перед
блоком Попытка, а в секции Исключение проверяют Если ТранзакцияАктивна() Тогда?
Если сбой произошёл на этапе самой фиксации в ЗафиксироватьТранзакцию() и платформа уже сбросила
активность транзакции (либо код находится в общем обработчике), безусловный вызов ОтменитьТранзакцию()
выбросит новое исключение и затрёт первичную ИнформацияОбОшибке(). Поэтому канонический шаблон выглядит так:
НачатьТранзакцию();
Попытка
// Изменение данных в транзакции
ВыполнитьДвиженияДокумента(СсылкаНаДокумент);
ЗафиксироватьТранзакцию();
Исключение
Инфо = ИнформацияОбОшибке();
Если ТранзакцияАктивна() Тогда
ОтменитьТранзакцию();
КонецЕсли;
ЗаписьЖурналаРегистрации(
"РегламентныеОперации.ОшибкаТранзакции",
УровеньЖурналаРегистрации.Ошибка,
,
СсылкаНаДокумент,
ПодробноеПредставлениеОшибки(Инфо),
РежимТранзакцииЗаписиЖурналаРегистрации.Независимая);
ВызватьИсключение;
КонецПопытки;
Обратите внимание на порядок строк в секции Исключение: сначала сохраняем
Инфо = ИнформацияОбОшибке() в переменную, затем откатываем транзакцию через
ОтменитьТранзакцию(), после этого пишем независимое событие в журнал регистрации и пробрасываем
ошибку наверх оператором ВызватьИсключение; без параметров (чтобы сохранить исходный стек).
Сводная таблица методов работы с транзакциями, блокировками и ошибками в 1С 8.3
| Конструкция / метод 1С 8.3 | Назначение | Где и как применять |
|---|---|---|
Новый БлокировкаДанных + Заблокировать() |
Явная управляемая блокировка измерений регистра или объектов перед чтением и изменением | Внутри активной транзакции строго до выполнения запроса контроля остатков |
НачатьТранзакцию() / ЗафиксироватьТранзакцию() |
Открытие и успешная фиксация транзакции изменений БД | Оборачивать только быстрые операции чтения под блокировкой и записи без HTTP и файлов |
ТранзакцияАктивна() + ОтменитьТранзакцию() |
Проверка открытой транзакции и полный откат изменений при сбое | В начале блока Исключение сразу после сохранения ИнформацияОбОшибке() |
ИнформацияОбОшибке() |
Получение структурированного объекта ошибки (модуль, строка, описание, вложенная Причина) |
В первой строке секции Исключение вместо устаревшего ОписаниеОшибки() |
КраткоеПредставлениеОшибки(Инфо) |
Формирование понятного пользователю текста без технических имён модулей и номеров строк | Для вывода в интерфейс (Сообщить, ПоказатьПредупреждение) |
ПодробноеПредставлениеОшибки(Инфо) |
Полное описание ошибки со стеком модулей, номерами строк и цепочкой «по причине:» | Для записи в ЗаписьЖурналаРегистрации и диагностики разработчиком |
ПредставлениеПричиныОшибки(Инфо) |
Текст самого глубокого (первичного) исключения без внешних обёрток «Ошибка при вызове метода контекста» | Для быстрой фильтрации и классификации первопричины сбоя в логах |
Частые ошибки при работе с транзакциями и Попытка Исключение в 1С
-
Чтение остатков в
ОбработкаПроведениябез предварительногоБлокировкаДанных.Заблокировать(). В управляемом режиме запрос сам по себе не блокирует регистр от параллельного чтения, что приводит либо к списанию в минус, либо к конфликту блокировок СУБД на записи движений. -
Незаполненное измерение в
ЭлементБлокировкиДанных. Если в регистре три измерения (Организация,Склад,Номенклатура), а в блокировке указан толькоСклад, платформа заблокирует все товары и организации по этому складу. - Запись движений по несортированной табличной части документа. Разный порядок номенклатуры или складов в параллельно проводимых документах вызывает встречный захват строк в СУБД (deadlock). Всегда сортируйте и сворачивайте данные по измерениям регистра.
-
HTTP-запросы,
ЧтениеXMLи работа с файлами внутриНачатьТранзакцию(). Сетевые задержки удерживают блокировки в БД десятки секунд, из-за чего соседние сеансы падают по 20-секундному таймауту ожидания. -
Пропуск
ОтменитьТранзакцию()в секцииИсключение. Перехват ошибки в 1С не откатывает транзакцию автоматически: она остаётся открытой или аварийной, удерживая блокировки и ломая следующие запросы. -
Использование
РежимТранзакцииЗаписиЖурналаРегистрации.Транзакционнаяпри логировании ошибки до отката. Если записать ошибку в транзакционном режиме, а затем вызватьОтменитьТранзакцию(), запись в журнале регистрации может откатиться вместе с транзакцией. Для ошибок используйтеНезависимая. -
Вызов
ВызватьИсключение ОписаниеОшибки();вместо пустогоВызватьИсключение;. Передача строки вВызватьИсключениесоздаёт новое исключение на текущей строке и затирает исходную цепочкуИнформацияОбОшибке().Причина.
Частые вопросы о конфликтах блокировок и Попытка Исключение в 1С 8.3
Почему возникает ошибка «Конфликт блокировок при выполнении транзакции» в 1С 8.3?
Ошибка возникает, когда транзакция не смогла получить управляемую блокировку 1С или блокировку СУБД в течение установленного времени ожидания (по умолчанию 20 секунд), либо когда менеджер блокировок обнаружил взаимоблокировку (deadlock) двух сеансов, захватывающих одни и те же ресурсы в разном порядке.
Чем ИнформацияОбОшибке() лучше ОписаниеОшибки() в блоке Попытка Исключение?
Функция ОписаниеОшибки() возвращает плоскую строку, а ИнформацияОбОшибке() возвращает объект со свойствами Описание, ИмяМодуля, НомерСтроки и ссылкой на вложенную ошибку Причина. Из него через КраткоеПредставлениеОшибки() получают понятный текст для пользователя, а через ПодробноеПредставлениеОшибки() — полный стек для журнала регистрации.
Нужно ли вызывать ОтменитьТранзакцию() внутри секции Исключение?
Да, обязательно. Сам по себе переход в секцию Исключение не отменяет транзакцию, открытую методом НачатьТранзакцию(). Проверьте активность через Если ТранзакцияАктивна() Тогда и вызовите ОтменитьТранзакцию(), чтобы снять блокировки и вернуть базу в согласованное состояние.
ИИ Консоль кода 1С
Лайт
Для ознакомления с возможностями
- Полный набор инструментов без урезаний
- Без ввода карты
Старт
Для индивидуального использования
- До 5 конфигураций
- Встроенный агент запросов 1С
-
≈ 100 задач Подробнее про лимиты
- Техническая поддержка
Продвинутый
Популярный выборДля интенсивного использования или команд
- До 15 конфигураций
- Встроенный агент запросов 1С
-
≈ 300 задач Подробнее про лимиты
-
Свои подписки Codex, Claude и любые OpenAI-совместимые
-
Внешние обработки без Конфигуратора
- Приоритетная техническая поддержка