Разбор от сообщества: Ваш журнал показывает deadlock. Разбираемся
Deadlock в журнале регистрации — ситуация, когда система зависает без видимых причин, а в окне «Состояние выполнения» — пустота. Разработчики с 10-летним стажем годами не сталкиваются, пока не попадают в проект с высокой конкурентной нагрузкой. Мы разберём механику блокировок внутри ЗаписьЖурналаРегистрации, покажем, как воспроизвести deadlock, и дадим конкретные инструменты для диагностики. Материал основан на реальном случае из сообщества — ссылка на источник в начале.
Природа deadlock в журнале регистрации
Многие считают, что вызов ЗаписьЖурналаРегистрации — атомарная операция, не подверженная блокировкам. Это не так. Внутри платформы журнал регистрации хранится в служебных таблицах, и при конкурентной записи в пределах одной транзакции возникает традиционный конфликт блокировок.
Как возникает deadlock: пошаговое воспроизведение
Представьте два параллельных сеанса, каждый из которых выполняет длительную транзакцию с записью в журнал. Если оба сеанса одновременно вызывают ЗаписьЖурналаРегистрации, а затем пытаются получить доступ к одним и тем же данным, SQL-сервер (или встроенная СУБД) вынужден разрешить взаимоблокировку, выбирая жертву. В итоге один из сеансов получает ошибку, а другой — продолжает работу, но состояние системы становится неконсистентным.
// Пример кода, который может привести к deadlock
Процедура ОбработатьПакетДанных(Пакет)
НачатьТранзакцию();
Попытка
// Работа с данными
Для Каждого Строка Из Пакет Цикл
// ... обновление регистров ...
КонецЦикла;
// Запись в журнал внутри той же транзакции
ЗаписьЖурналаРегистрации("Обработка", УровеньЖурналаРегистрации.Информация,
, , "Обработан пакет из " + Пакет.Количество() + " записей");
// Долгая операция, увеличивающая время блокировки
ВыполнитьДополнительныеРасчеты();
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ЗаписьЖурналаРегистрации("Ошибка", УровеньЖурналаРегистрации.Ошибка,
, , КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
КонецПроцедуры
Ключевой тезис: Deadlock в журнале регистрации — это не баг платформы, а следствие неправильного проектирования: длинная транзакция + запись в общий ресурс (журнал) = гарантированный конфликт при масштабировании.
Анализ типичных сценариев
На практике deadlock в журнале регистрации чаще всего проявляется в двух сценариях: массовые фоновые загрузки и интенсивная работа пользователей в период закрытия месяца.
Сценарий 1: Массовые операции в фоне
Фоновое задание загружает 100 000 строк из Excel и на каждую строку пишет в журнал. Если запустить два таких задания параллельно, они начнут конкурировать за запись в служебную таблицу. При этом время транзакции может достигать нескольких минут — блокировка удерживается всё это время.
Сценарий 2: Запись в журнал внутри транзакции с «тяжёлыми» вычислениями
Типичная ошибка — помещать вызов ЗаписьЖурналаРегистрации внутрь транзакции, которая содержит ещё и расчёты себестоимости или перепроведение документов. В такой ситуации блокировка журнала превращается в узкое место.
| Подход | Условия применимости | Риск deadlock |
|---|---|---|
| Запись в журнал внутри транзакции | Низкая конкурентность, короткие транзакции | Высокий при более 2–3 параллельных сеансов |
| Запись в журнал после фиксации транзакции | Всегда, если не требуется атомарность | Низкий |
| Асинхронная запись через отдельный фоновый процесс | Высокая нагрузка, большие объемы | Минимальный |
Диагностика и инструменты
Когда deadlock уже произошёл, стандартные средства 1С не показывают причину: в журнале регистрации фиксируется только ошибка «Транзакция завершилась с ошибкой». Чтобы поймать момент, нужно использовать технические приёмы.
Приём 1: Трассировка через замер производительности
Включите замер производительности (Сервис → Параметры → Замер производительности) и ловите событие «Транзакция заблокирована». Это покажет, какой объект участвует в блокировке. Однако внутри платформы имя таблицы журнала не отображается — только «Служебная таблица», что уже указывает на проблему.
Приём 2: Искусственное воспроизведение в тестовой среде
Напишите скрипт, который запускает в двух сеансах одну и ту же процедуру с ЗаписьЖурналаРегистрации внутри транзакции и искусственной задержкой. Если в рабочей среде вы видите падение, проводите тест на копии базы с реальными данными.
// Пример для тестирования: два сеанса выполняют одновременно
Процедура ТестDeadlock()
НачатьТранзакцию();
ЗаписьЖурналаРегистрации("Тест", УровеньЖурналаРегистрации.Информация, , , "Начало");
// Имитация долгой работы
Для Инд = 1 По 1000000 Цикл
// Пустой цикл
КонецЦикла;
ЗаписьЖурналаРегистрации("Тест", УровеньЖурналаРегистрации.Информация, , , "Конец");
ЗафиксироватьТранзакцию();
КонецПроцедуры
// Получение информации об ошибке после отлова
Процедура ОбработчикОшибки()
ЗаписьЖурналаРегистрации("Deadlock", УровеньЖурналаРегистрации.Ошибка,
, , КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
// Не записывайте в журнал внутри транзакции — вызов будет вне транзакции
КонецПроцедуры
Предупреждение: Никогда не вызывайте
ЗаписьЖурналаРегистрациивнутри обработчика исключения, если текущая транзакция ещё не отменена. Это может привести к повторному deadlockу.
Что делать прямо сейчас: чек-лист и типичные ошибки
Ситуация знакома многим, но не все знают, как её исправить без переписывания всего кода. Приводим пошаговый план.
Чек-лист для проверки существующего кода
- Найти все вызовы
ЗаписьЖурналаРегистрации— используйте поиск по конфигурации (Ctrl+Shift+F). - Проверить, находятся ли они внутри
НачатьТранзакцию()…ЗафиксироватьТранзакцию()— если да, переместите вызов после фиксации (или перед началом, если это не критично). - Для фоновых заданий — используйте отдельный фоновый процесс для записи лога, который не участвует в основной транзакции.
- Настроить изоляцию транзакций — на уровне SQL-сервера включите
READ COMMITTED SNAPSHOT(только для SQL Server). Это снизит конкуренцию за чтение, но не устранит deadlock при записи. - Мониторить длительность транзакций — через стандартный отчёт «Анализ активности» или через внешние инструменты (например, SQL Profiler).
Типичные ошибки
- Ошибка 1: Пытаться «поймать» deadlock через повторную попытку (retry) без выхода из транзакции. Платформа сама откатывает транзакцию, но повторный вызов внутри
Исключениеможет привести к бесконечному циклу. - Ошибка 2: Использовать
ЗаписьЖурналаРегистрациивнутри обработчика исключения, если текущая транзакция ещё не отменена. Это может привести к повторному deadlockу.
Попробовать НОПик →
Проверить свой уровень бесплатно →