Mazmunga o'tish
Tez Base

Управляемая блокировка 1С не работает: шесть причин, по которым замок не ставится

· Chop etilgan:

Блокировки и дедлоки

Симптом выглядит так: в модуле проведения стоит БлокировкаДанных, код прошёл ревью, а данные ведут себя как без защиты. Два сеанса записывают один элемент, выживает последняя запись. Остаток уходит в минус при живой проверке достаточности. Появляется двойной резерв на одну ячейку.

Разбор таких историй почти всегда упирается в одно: замок, на который все рассчитывали, не ставился. Метод Заблокировать() о своём неучастии не сообщает. Если блокировка не установлена или не действует, вызывающий код отрабатывает без ошибок и предупреждений - исключение вы получите только при конфликте живого замка, а мёртвый замок не конфликтует ни с кем.

Ниже шесть мест, где блокировка теряется молча. Порядок - от дешёвых проверок к дорогим. Список собран на ревизии складской системы крупной розничной сети: из 95 мест, где по смыслу операции нужен замок, 41 опиралось на процедуры, которые выглядят как блокировка, но замка не ставят.

Причина 1: объект работает в автоматическом режиме блокировок

У конфигурации есть свойство “Режим управления блокировкой данных”, и у каждого объекта метаданных - своё такое же. В смешанном режиме (“Автоматический и управляемый”) часть объектов остаётся в автоматическом, и транзакции по ним платформа ведёт автоматически: сериализацию обеспечивают замки СУБД через уровень изоляции, а расставленные кодом управляемые замки в такой транзакции ничего не сериализуют.

Вызов при этом проходит без ошибок. Код написан, ревью довольно, защиты нет.

Проверка занимает минуту: откройте свойства объекта в конфигураторе и посмотрите режим. В той ревизии, с которой начался этот текст, в автоматическом режиме оставалось 27 объектов из 405 - и исключили их из проверки с формулировкой “там только журналы и инфраструктура”, которую никто не перепроверял. Если хоть один такой объект участвует в движении товара, картина блокировок по нему другая, чем предполагает код.

Причина 2: захват спрятан за флагом, который никто не включает

Самый злой случай, потому что процедура называется правильно и внутри выглядит правильно:

Процедура ЗаблокироватьОстатки(Номенклатура, Ячейка, УстанавливатьБлокировку = Ложь)

    Блокировка = Новый БлокировкаДанных;
    Элемент = Блокировка.Добавить("РегистрНакопления.ОстаткиТоваров");
    Элемент.УстановитьЗначение("Номенклатура", Номенклатура);
    Элемент.УстановитьЗначение("Ячейка", Ячейка);
    Элемент.Режим = РежимБлокировкиДанных.Исключительный;

    Если УстанавливатьБлокировку Тогда
        Блокировка.Заблокировать();
    КонецЕсли;

КонецПроцедуры

Объект блокировки собирается целиком, режим исключительный, всё на месте. Захват стоит под условием, умолчание у флага - Ложь, и все старые вызовы проходят мимо Заблокировать().

Появляется такой флаг из осторожности: кто-то добавил параметр, включил его в одном новом месте и не решился трогать существующие вызовы. Через год про флаг никто не помнит, а имя процедуры продолжает обещать замок. Человек, читающий тело сверху вниз, видит Исключительный и до условия долистывает не всегда.

Отдельно про смену умолчания. Если у такой процедуры позже меняют значение флага по умолчанию, поведение всех вызовов меняется без единого символа диффа в местах вызова. Ревью видит одну строку и пропускает правку, которая трогает всю систему. Умолчание у параметра блокировки ставится в строгую сторону, а вызовы чинятся явно, даже когда их сто.

Причина 3: обёртка сама открывает и сама фиксирует транзакцию

Управляемый замок живёт до конца транзакции, в которой установлен. Если процедура-обёртка при вызове вне транзакции открывает собственную, берёт замок и тут же фиксирует - замок снимается раньше, чем вызывающий код успевает прочитать хоть одну запись. Формально блокировка была. Практически её не существовало ни одного полезного такта.

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

Причина 4: разделяемый режим там, где защищаются от второго писателя

Разделяемый замок не пускает писателей к читателю. Двух писателей между собой он не сериализует: две параллельные записи одного элемента спокойно проходят обе, и от потерянного обновления такой замок не защищает вовсе.

Правило короткое: перед записью берётся исключительный режим. Разделяемый нужен, когда вы читаете данные и хотите, чтобы их не поменяли под вами, - и не годится, когда меняете сами.

Причина 5: проверка стоит выше границы транзакции

Проверка достаточности написана правильно, читает актуальные данные и выполняется корректно. Стоит она на несколько строк выше НачатьТранзакцию() - и этого хватает, чтобы защита не работала.

Два потока приходят к проверке одновременно, оба видят остаток 1, оба решают, что списание допустимо, оба списывают. Замок внутри транзакции этому не мешает: к моменту его установки решение уже принято снаружи. На разобранной системе расстояние в шесть строк между проверкой и границей транзакции стоило трёх классов отказов сразу - отрицательного остатка, двойного резерва и продажи сверх наличия.

Этот шаблон не ловится ни линтером, ни статическим анализом: синтаксис в порядке, вызовы на месте. Находится он только чтением по цепочке вызовов с вопросом “где здесь начинается транзакция”. Возьмите главную проверку достаточности своей предметной области - остаток, лимит, свободное место - и посмотрите, с какой стороны от НачатьТранзакцию() она стоит.

Причина 6: сериализацию делало блокирующее чтение, и его выключили

Пока на MS SQL Server выключен параметр READ_COMMITTED_SNAPSHOT, читающая транзакция берёт на строках разделяемые замки и стоит в очереди к пишущим. Побочный эффект: код вида “прочитал остатки, подумал, записал” выстраивается в очередь сам собой, даже когда управляемого замка в нём нет. Сериализация есть, замка в коде нет.

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

По дедлокам переключение при этом помогает заметно: на той же системе пик взаимоблокировок упал с 73 в час до 7-8, ночная сумма - примерно с 200 до 20-27. Две метрики дали один порядок улучшения, и обе настоящие. Опасность в том, что видимая часть - минус девяносто процентов дедлоков - приезжает сразу, а тихие гонки в местах с фантомными замками всплывают неделями. Поэтому перед включением снимка на чтение сначала проходят причины 1-5, и только потом переключают. В обратном порядке вы получите улучшение по дедлокам и тихую порчу данных одним действием.

Проверить, как настроен ваш SQL Server и что из настроек меняет поведение блокировок, быстрее готовой обработкой Чек-ап СУБД под 1С, чем собирать запросы по одной.

Проверка на вечер

Порядок, который повторяется на любой чужой конфигурации без подготовки.

  1. Поиском по конфигурации посчитайте вызовы Заблокировать(). Отдельно посчитайте процедуры, в именах которых есть “Заблокировать” или “Блокировк”. Если процедур заметно больше, чем захватов, - в конфигурации есть обёртки, и в тело каждой придётся заглянуть.
  2. В каждой обёртке найдите условие над захватом. Параметр с умолчанием Ложь и ветка без захвата - кандидаты в пустышки.
  3. Сверьте режим замка с назначением: перед записью - исключительный.
  4. Проверьте, что обёртка не фиксирует транзакцию сама.
  5. Найдите проверки достаточности и их положение относительно НачатьТранзакцию().
  6. Посмотрите список объектов в автоматическом режиме блокировок и решите про каждый, участвует ли он в конкурентных операциях.

Вывод из всей ревизии один: имя процедуры и зелёное ревью не доказывают ничего. Забытый замок находится по отсутствию вызова, поддельный по отсутствию вызова не находится никогда - его находит только чтение тела и границы транзакции.

Что здесь не разобрано

Сознательно за кадром остались два соседних сюжета. Как искать виновника, когда блокировки живые и конфликтуют по-настоящему, - это разбор взаимоблокировок по частотам, там метод счёта вместо разбора одного случая. И отдельный класс, где движения ни при чём, а дерутся таблицы итогов, - дедлок на итогах регистра.

История нагрузки - кто держал базу ночью и чьи сеансы стояли за конфликтами - снимается обработкой Анализ нагрузки кластера 1С: она пишет снимки по расписанию и отвечает именами держателей вместо голых счётчиков.

Если минусовые остатки и потерянные обновления уже стали регулярными, а ревизию блокировок делать некому - напишите нам. Начинаем с экспресс-аудита: за пару дней станет видно, где в вашей конфигурации замки настоящие, а где декорация.

Остатки уходят в минус, хотя блокировки в коде стоят

Найдём места, где замок не ставится или не защищает, и приведём модель блокировок в порядок

Narxi
150 000 ₸ danloyiha uchun; soliqlarsiz; bepul auditdan keyin qat'iy belgilanadi
Muddati
от 3 дней

Yozishga erta? O'zingiz o'lchang

Чек-ап СУБД под 1С: 40+ проверок с вердиктом по каждой

Ishlov berishni ochish

INFOSTART TECH EVENT 2026: конференция для 1с-специалистов. если собираетесь, регистрируйтесь по нашей ссылке. Регистрация →

Buni ham o'qing

Ускорит ли внешняя компонента на C++ ваш код 1С: как посчитать до того, как писать

Медленное место в 1С хочется вынести в компоненту на C++, но выигрыш бывает и в тысячу раз, и отрицательный. Разбираем, из чего складывается время компоненты, сколько стоит граница в изолированном режиме и в процессе 1С, с какого объёма компонента окупается и в каких задачах её лучше не писать. Все числа из замера 31 задачи на платформе 8.3.27 с проверкой одинаковости результата.

Почему нейросеть повторяет одно и то же: карта внимания модели, снятая на встроенном языке 1С

Модель смотрит в вопрос, пока пишет первые три символа ответа, а дальше почти только на себя. Если выключить у неё одну голову внимания из четырёх, верных ответов не остаётся, а больше трети ответов уходят в повтор вроде "стстстст". Разбираем, как снять карту внимания функцией на сорок строк, что она показала на сорока вопросах и что из этого следует для тех, кто работает с большими моделями.

Нейросеть для проверки кода 1С: как проверить её на своём коде до внедрения

Прежде чем отдавать нейросети ревью кода 1С, её стоит принять на своём коде: у нас одна и та же локальная модель нашла 11 дефектов из 13 на учебных процедурах и 0 из 5 на рабочих. Разбираем порядок приёмки: какие два числа считать, из чего собрать проверочный набор, как поднять стенд без интернета и по каким признакам видно, что замер врёт.