Каждый администратор крупных информационных систем на базе платформы «1С:Предприятие» рано или поздно сталкивается с ситуацией, когда один или несколько пользователей начинают испытывать сильнейшие задержки при работе («тормоза»), хотя остальные коллеги работают в штатном режиме. При этом нагрузка на процессоры сервера СУБД или сервера приложений возрастает до 30-40%, а в технологическом журнале регистрируется непрерывный, лавинообразный поток событий SDBL. Самое странное заключается в том, что поток запросов не прекращается, даже если пользователь отошел от компьютера и сеанс находится в состоянии полного простоя.
В этой статье мы подробно разберем, почему возникает такое «залипание», как правильно настроить технологический журнал для диагностики проблемы по нескольким рабочим станциям одновременно, и как устранить первопричину подобного поведения системы.
Прежде чем перейти к практике, проанализируем внутренние механизмы платформы. Аббревиатура SDBL расшифровывается как System Database Layer — это собственный внутренний слой платформы 1С, который выступает посредником между объектной моделью метаданных (документы, справочники, регистры) и реляционной базой данных (MS SQL Server, PostgreSQL и др.). Именно SDBL транслирует запросы, написанные на встроенном языке 1С, в SQL-запросы, понятные конкретной СУБД.
Когда пользователь активно работает с базой данных (проводит документы, открывает отчеты), генерация событий SDBL — это абсолютно нормальный процесс. Каждое такое событие сопровождается контекстом выполнения (событие Context в технологическом журнале), указывающим на конкретную строку кода в модуле.
Однако если пользователь бездействует, а цепь событий SDBL продолжает бесконечно генерироваться без пауз, это сигнализирует об аномалии. Рассмотрим основные причины такого поведения:
Попытка — Исключение.ПодключаемыйОбработчикОжидания позволяет запускать процедуры с определенным интервалом времени. Если внутри обработчика выполняется тяжелый запрос к базе данных, время выполнения которого превышает интервал самого таймера, запросы начинают наслаиваться друг на друга, порождая бесконечный поток SDBL.Для локализации проблемы нам потребуется собрать технологический журнал (ТЖ). Однако собирать логи со всего сервера приложений — не лучшая идея, так как файлы ТЖ моментально забьют дисковое пространство гигабайтами мусора. Нам нужно ограничить сбор информации только проблемными компьютерами (например, машинами с сетевыми именами exp и exp2).
Выясним важную деталь синтаксиса файла logcfg.xml. Все условия отбора, прописанные внутри одного тега <event>, объединяются по логическому «И» (AND). Соответственно, мы не можем написать два отбора по имени компьютера внутри одного тега <event>, так как событие не может происходить на двух компьютерах одновременно. Для реализации логического «ИЛИ» (OR) нам необходимо создать несколько отдельных блоков <event> внутри одного тега <log>.
Рассмотрим правильный пример конфигурационного файла logcfg.xml:
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://v8.1c.ru/v8/tech-log">
<log location="C:\v8\logs\sdbl_heavy" history="12">
<!-- Отслеживаем события SDBL для компьютера exp -->
<event>
<eq property="name" value="SDBL"/>
<eq property="t:computerName" value="exp"/>
</event>
<!-- Отслеживаем события SDBL для компьютера exp2 -->
<event>
<eq property="name" value="SDBL"/>
<eq property="t:computerName" value="exp2"/>
</event>
<!-- Сюда же обязательно добавляем сбор контекста для этих машин -->
<event>
<eq property="name" value="Context"/>
<eq property="t:computerName" value="exp"/>
</event>
<event>
<eq property="name" value="Context"/>
<eq property="t:computerName" value="exp2"/>
</event>
<property name="all"/>
</log>
</config>
Эта настройка позволит нам получить чистый лог, содержащий информацию только по двум потенциально проблемным рабочим станциям. Параметр history="12" ограничит хранение логов двенадцатью часами, защитив диск от переполнения.
Когда у пользователя снова начнутся тормоза, нам необходимо зафиксировать активность на уровне системы управления базами данных (например, MS SQL Server). Для этого воспользуемся встроенным средством Activity Monitor (Монитор активности) в SQL Server Management Studio (SSMS) или запустим трассировку через Extended Events.
Нам нужно отфильтровать процессы по имени базы данных 1С и найти идентификатор сессии (SPID), который потребляет аномально много ресурсов процессора или выполняет огромное число операций чтения (Logical Reads). Зафиксировав SQL-текст запросов, мы сможем понять, к каким именно таблицам идет обращение. Например, в SQL-запросе могут фигурировать таблицы документов вида _Document150 или регистров сведений _InfoReg20345.
Сопоставить эти физические таблицы СУБД с метаданными 1С можно с помощью получением структуры хранения БД через встроенную функцию платформы:
// Получаем соответствие имен таблиц в СУБД объектам метаданных
СтруктураБазы = ПолучитьСтруктуруХраненияБазыДанных();
После сопоставления SQL-запросов с метаданными 1С мы переходим к анализу собранного технологического журнала. Ищем повторяющиеся блоки событий SDBL и анализируем предшествующие им события Context. Контекст четко укажет на строку кода встроенного языка, которая инициировала этот цикл.
Разберем практическую ситуацию. В результате анализа выясняется, что система циклически обращается к таблицам ветеринарных свидетельств. Ранее в системе функционировала подсистема интеграции с ветеринарным контролем (например, УВС — Управление ветеринарными сертификатами). В дальнейшем от использования этой подсистемы отказались, однако в коде остался незакомментированный фоновый вызов, выполняющийся при определенных условиях работы пользователя.
Посмотрим на пример неоптимального кода, который приводит к бесконечному циклу генерации SDBL при потере связи со старой подсистемой:
// Пример устаревшего и опасного кода во внешнем модуле
Процедура ПроверитьСтатусыСертификатов() Экспорт
// Алгоритм пытается обработать массив документов в бесконечном цикле
Пока Истина Цикл
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 10
| ВетПаспорт.Ссылка КАК Ссылка
|ИЗ
| Справочник.ВетеринарныеДокументы КАК ВетПаспорт
|ГДЕ
| ВетПаспорт.СтатусОбмена = ЗНАЧЕНИЕ(Перечисление.СтатусыВыгрузки.Подготовлено)";
Выборка = Запрос.Выполнить().Выбрать();
Если НЕ Выборка.Следующий() Тогда
// Если документов нет, выходим из цикла
Прервать;
КонецЕсли;
// Попытка отправить запрос во внешнюю систему, которая уже отключена
Попытка
ВыгрузитьДанныеВоВнешнийСервис(Выборка.Ссылка);
Исключение
// Ошибка перехватывается, но прерывания цикла нет!
// В итоге мы получаем бесконечный цикл и лавину SDBL запросов.
ЗаписьЖурналаРегистрации("Интеграция", УровеньЖурналаРегистрации.Предупреждение,,, "Ошибка связи");
КонецПопытки;
КонецЦикла;
КонецПроцедуры
Как мы видим, из-за отсутствия выхода из цикла в блоке Исключение при неработающем внешнем сервисе код превращается в «вечный двигатель», нагружающий сервер СУБД бессмысленными запросами к справочнику ветеринарных документов.
Решение проблемы: необходимо полностью удалить или закомментировать («зарэмить») вызовы неиспользуемых устаревших интеграций, отключить неактуальные регламентные задания и обработчики ожидания. После комментирования проблемного участка кода бесконечный поток SDBL прекратится, нагрузка на процессоры сервера упадет, а сеансы пользователей перестанут зависать.
Для предотвращения подобных сбоев в будущем рекомендуется внедрить следующие регламентные меры:
ПодключаемыйОбработчикОжидания. По возможности заменяйте их фоновыми заданиями, выполняющимися на сервере.