В данной статье будет рассмотрена методика расследования конфликтов (взаимоблокировка, ожидание на блокировке, превышение времени ожидания) на управляемых блокировках на уровне платформы 1С:Предприятие.
Настройка технологического журнала
Для расследования конфликтов на управляемых блокировках, во-первых, необходимо настроить технологический журнал на сбор событий TLOCK, TTIMEOUT, TDEADLOCK, при необходимости дополнительно установив отбор по имени базы в свойстве p:processName
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<log location="Z:\Locks" history="8"> <event> <eq property="Name" value="TLOCK"/> <eq property="p:processName" value="MyBase"/> </event> <event> <eq property="Name" value="TTIMEOUT"/> <eq property="p:processName" value="MyBase"/> </event> <event> <eq property="Name" value="TDEADLOCK"/> <eq property="p:processName" value="MyBase"/> </event> <property name="all"/> </log> |
Классификация конфликтов блокировок
Можно выделить 3 варианта проблем связанных с блокировками:
- Превышение времени ожидания на блокировках (таймаут)
- Взаимоблокировка (deadlock)
- Ожидания на блокировках без возникновения таймаута или взаимоблокировки
Следует отметить, что первые две ошибки являются критическими и требуют немедленного исправления. Третья же, говорит лишь о том что в системе существуют ожидания на блокировках и, в зависимости от их характера и предъявляемого качества к информационной системе, могут быть вызваны как ошибками (избыточными блокировками, длительными транзакциями), так и необходимым условием последовательного доступа к одним и тем же ресурсам.
Совместимость блокировок
Без простейшего понимания совместимости блокировок достаточно сложно описать дальнейшие действия, поэтому, советую ознакомиться с этой статьей на сайте ИТС.
Вкратце укажу что несовместимыми блокировками будут являться такие блокировки, у которых одновременно выполняются следующие условия:
- Несовместимы режимы блокировок (значения Shared/Exclusive в свойстве Locks события TLOCK)
- Совпадают пространства блокировок (свойство Region события TLOCK)
- Пересекаются блокируемые диапазоны пространства блокировок (свойство Locks).
Особенно хочу отметить что в пункте 3 пересекаться должны именно диапазоны, а не блокируемые записи базы данных, в этом заключается существенная разница. Допустим, в конфигурации есть непериодический, независимый регистр сведений «Цены поставщиков» с измерениями «Контрагент», «Номенклатура» и ресурсом «Цена». Записи регистра приведены ниже в таблице:
Контрагент | Номенклатура | Цена |
---|---|---|
ООО «Наше Печенье» | Печенье с малиновой начинкой | 100 |
ООО «Наши Конфеты» | Конфета «Коровка» | 50 |
Если в первой транзакции будет установлена блокировка по пространству этого регистра с условием «Контрагент = ООО «Наше Печенье»», а во второй — по этому же регистру, но с условием «Номенклатура = Конфета «Коровка»», то произойдет конфликт блокировок несмотря на то что в базе данных отсутствуют пересекающиеся записи. Причина заключается в том что блокируемые диапазоны пересекаются (возможно наличие записи у которой будет «Контрагент = ООО «Наше Печенье»» и «Номенклатура = Конфета «Коровка»»). Рекомендую самостоятельно проверить это воспользовавшись обработкой из раздела «Практическая проверка материала»
Превышение времени ожидания на блокировках
Начинать расследование стоит со строки события TTIMEOUT. В свойстве t:connectID этого события указан номер соединения жертвы, а в поле WaitConnections указан номер соединения источника конфликта (то соединение, которое установило блокировку несовместимую с блокировкой жертвы). Следующим шагом необходимо найти какую управляемую блокировку хотела, но не смогла установить жертва. Для этого, зная значения двух вышеприведенных полей, надо найти строку события TLOCK, у которого будут такие же значения полей t:connectID и WaitConnections, она будет идти после события TTIMEOUT. В данной строке особый интерес представляют поля Regions (пространство блокировок) и свойство Locks, в котором указаны пространство и режим блокировки, а так же набор комбинаций полей и их значений, по которым жертва пыталась установить блокировку. Помимо этого, нам доступен контекст события на встроенном языке (какая строка кода вызвала установку этой блокировки). Далее нам необходимо получить информацию об источнике конфликта. Смысл заключается в том что необходимо найти ближайшую несовместимую блокировку (событие TLOCK), идущую до события TTIMEOUT, свойство t:connectID которого равен значению из поля WaitConnections события TTIMEOUT, а значение поля Regions совпадает со значением ранее найденного TLOCK жертвы. Найдя это событие, мы, аналогично событию жертвы, можем получить информацию об установленной блокировке: пространстве, режиме, полям и их значениям, а так же контекст на встроенном языке. Обладая знаниями о конфликтующих блокировках и контекстах их вызовов, необходимо проанализировать данный конфликт, установить причину возникновения конфликта и устранить ее. В случае если конфликт произошел, например, при проведении документа (или при другом действии, которое регистрируется в журнале регистрации), возможно получить дополнительную информацию из журнала регистрации сопоставив время, пользователя и номер сеанса (SessionID).
Пример разбора:
- Ищем событие TTIMEOUT, по свойствам t:connectID и WaitConnections определяем соединение жертвы — 31139, соединение источника — 31143
- Ищем TLOCK жертвы по значениям полей t:connectID=31139 и WaitConnections=31143. Определяем что жертва хотела установить исключительную блокировку про пространству DIMS регистра сведений InfoRg218 (информацию о регистре можно получить с помощью метода ПолучитьСтруктуруХраненияБазыДанных(), подробнее в статье «Получение информации о структуре хранения базы данных в терминах 1С:Предприятие и СУБД») по значениям полей Fld219=1 и Fld220=2
- По контексту события получаем информацию о месте вызова: Форма обработки «Обработка5», строка 61
- Ищем TLOCK источника, по значению поля t:connectID=31143 и пространству InfoRg218.DIMS, а также расположению в технологическом журнале (должно быть ближайшим предваряющим событием). При этом помним что блокировка должна быть несовместима (дополнительно к пространству блокировок должно выполняться условие на несовместимость по режиму блокировки и заблокированному диапазону). Такому условию удовлетворяет исключительная блокировка про пространству DIMS регистра сведений InfoRg218 и значению поля Fld219=1
- По контексту источника блокировки определяем где в конфигурации была установлена блокировка: Форма обработки «Обработка5», строка 15
- Анализируем и устраняем причину ошибки
Ожидания на блокировках без возникновения таймаута или взаимоблокировки
Схема разбора проблемы данного вида похожа на схему разбора таймаута (с небольшими отличиями). Во-первых, начинать разбор необходимо со строки события TLOCK, у которого значение свойства WaitConnections не пустое (но при этом не было связанных с этой блокировкой событий TTIMEOUT или TDEADLOCK, иначе это уже будет либо таймаутом, либо взаимоблокировкой соответственно). Данное событие является жертвой конфликта (несмотря на то что блокировка в итоге была установлена, жертве пришлось ждать освобождения ресурса, поэтому она «жертва»). Далее нам необходимо найти источник блокировки. Здесь следует поступить так же как и в случае с таймаутом: необходимо найти ближайшую несовместимую блокировку (событие TLOCK), идущую до события TLOCK жертвы, свойство t:connectID которого равен значению из поля WaitConnections, а свойство Regions соответствует событию жертвы. Далее, получив контексты событий, необходимо проанализировать и устранить причину возникновения проблемы.
Пример разбора:
- Ищем событие жертвы TLOCK. Как видно, установка управляемой блокировки жертвой заняла чуть более 5 секунд. По свойствам t:connectID и WaitConnections определяем соединение жертвы — 36680, соединение источника — 36619. По свойствам Regions и Locks получаем информацию о блокировке, по контексту события — информацию о месте вызова: Форма обработки «Обработка5», строка 68
- Ищем TLOCK источника, по значению поля t:connectID=36619 и пространству InfoRg218.DIMS, а также расположению в технологическом журнале (должно быть ближайшим предваряющим событием). При этом помним что блокировка должна быть несовместима (дополнительно к пространству блокировок должно выполняться условие на несовместимость по режиму блокировки и заблокированному диапазону). Опять же получаем информацию об установленной блокировке и месте ее установки: Форма обработки «Обработка5», строка 15
Взаимоблокировка
Расследование взаимоблокировки необходимо начинать с события TDEADLOCK. В поле t:connectID указан номер соединения, которое 1С:Предприятие выбрало в качестве жертвы (выполнена отмена транзакции). В свойстве DeadlockConnectionIntersections указаны номера соединений участников взаимоблокировки, пространства и режимы блокировок, которые сеансы хотели установить, а так же наборы полей и их значений для установки блокировок. Следующим шагом необходимо найти события TLOCK участников взаимоблокировки, следующие после события TDEADLOCK, это будет набором блокировок, которые не удалось установить участникам взаимоблокировки. Значения свойств t:connectID и WaitConnections должны присутствовать в наборе номеров соединений, перечисленных в поле DeadlockConnectionIntersections события TDEADLOCK. По свойствам Locks событий TLOCK так же можно получить информацию о блокировках, которые не удалось установить. Далее мы должны найти события TLOCK участников взаимоблокировки предшествующие событию TDEADLOCK, отбор так же производится по свойству t:connectID, а также с условием пересечения пространств блокировок (Regions) жертв и источников. При этом найденные пары блокировок должны быть несовместимыми между собой. По свойствам Locks в событиях TLOCK получим информацию о том какие блокировки были установлены участниками. Имея в своем распоряжении информацию об установленных участниками управляемых блокировках, а так же о блокировка которые не удалось установить, мы должны сделать вывод о виде взаимоблокировки (повышение уровня блокировки; захват ресурсов в разном порядке). Далее, используя контекст найденных TLOCK необходимо проанализировать и исправить ошибки: если захват в разном порядке — необходимо организовать захват ресурсов в одинаковом порядке; если повышение уровня блокировки — необходимо реализовать первоначальный захват ресурсов с максимальным уровнем.
Послесловие
К счастью, с жертвой блокировки всегда все понятно: если заполнено значение свойства WaitConnections, значит, данное событие оказалось жертвой, а наличие событий TTIMEOUT, TDEADLOCK или их отсутствие классифицирует вид конфликта. С источником все несколько сложнее, необходимо найти именно несовместимую, установленную источником, блокировку. Дополнительно при поиске конфликтов можно учесть время начала и окончания транзакций, например по событию SDBL или журналу регистрации. Также можно учесть время ожидания жертвой (если жертва ждала 10 секунд, то блокировка была установлена не менее чем за 10 секунд до события жертвы).
Стоит отметить что заполненное свойство WaitConnections не исключает блокировку из числа возможных источников для других блокировок (если она все же была установлена после ожидания, а не была завершена по таймауту или выбрана жертвой дэдлока). Это возможно, например, для каскада блокировок:
- «Транзакция 1» установила блокировку
- «Транзакция 2» хочет установить блокировку, но ждет «Транзакцию 1»
- «Транзакция 1» завершилась, «Транзакция 2» установила блокировку.
- «Транзакция 3» хочет установить блокировку, но ждет «Транзакцию 2»
В таком случае, «Транзакция 2» будет являться жертвой по отношению к «Транзакции 1» и источником по отношению к «Транзакции 3», и у нее будет заполнено свойство WaitConnections.
Ко всему прочему, возможна ситуация когда источник по той или иной причине наложил не одну несовместимую блокировку в течении транзакции:
- «Транзакция 1» установила блокировка по «Измерению 1»
- «Транзакция 1» установила блокировка по «Измерению 2»
- «Транзакция 2» пытается установить блокировку по «Измерению 1» и «Измерению 2» с такими же значениями.
Таким образом, обе блокировки источника являются несовместимыми с блокировкой жертвы. В этом случае предпочтительным способом будет найти и устранить ближайшую блокировку источника, а при тестировании, после устранения ближайшей блокировки, будет найдена и первая.
Так же хочется отметить что записи событий источника и жертвы даже в простом случае могут находиться в разных файлах технологического журнала (например, в файле другого часа, другого процесса и т.д.).
Практическая проверка материала
Как и раньше, приведу пример для практической проверки материала. Создадим базу данных, в которой установим режим управления блокировкой «Управляемый», основной режим запуска «Обычное приложение», режим использования модальных окон в «Использовать». Добавим в базу регистр сведений «АнализУправляемыхБлокировок» (непериодический, независимый). В регистре добавим измерения: «Измерение1» (тип Число), «Измерение2» (тип Число). Не забудьте настроить технологический журнал как указано в начале статьи. Теперь откройте обработку из вложения к данной статье в двух сеансах, выполните нижеприведенные кейсы и проанализируйте технологический журнал.
Таймаут (TTIMEOUT)
Для создания таймаута необходимо в первом сеансе нажать кнопку «Таймаут 1», во-втором — «Таймаут 2». Дождаться сообщения о таймауте во втором сеансе.
Длительное ожидание на блокировке
Так же как и для таймаута, в первом сеансе нажать «Таймаут 1», во-втором — «Таймаут 2». И не дожидаясь сообщения о таймауте, в первом сеансе нажать кнопку «ОК» модального окна.
Взаимоблокировка вида «захват ресурсов в разном порядке»
В первом сеансе нажать «Взаимоблокировка 1», во-втором «Взаимоблокировка 2», после чего сразу же нажать «ОК» модального окна в первом сеансе, а затем во-втором.
Взаимоблокировка вида «повышение уровня блокировки»
В первом, а затем во-втором сеансе нажать «Взаимоблокировка (повышение уровня блокировки)». После этого сразу же нажать «ОК» модального окна в первом сеансе, а затем во-втором.