При работе в конфигурациях на платформе 1С:Предприятие 7.7, особенно при миграции на современные версии Microsoft SQL Server (например, 2008 R2), разработчики и системные администраторы могут столкнуться с парадоксальной ситуацией. При попытке сохранить новый элемент справочника в интерактивном диалоге (форме элемента) система аварийно прерывает операцию с сообщением: «при проведении транзакции произошла ошибка объект заблокирован». При этом запись нового элемента непосредственно через форму списка проходит без каких-либо ошибок, а модули формы и объекта полностью пусты и не содержат программных транзакций или блокировок.
Разберем детально механизм возникновения этой неполадки, выясним причину разницы в поведении формы списка и диалога, а также рассмотрим пошаговые методы локализации и исправления ошибки.
Когда мы создаем запись непосредственно в строке формы списка, платформа 1С 7.7 выполняет минимальный набор проверок и формирует простой SQL-запрос на вставку записи. Однако при сохранении элемента через диалоговое окно активизируется внутренний платформенный алгоритм контроля целостности иерархии:
PARENTID (для многоуровневых справочников) или PARENTEXT (для подчиненных справочников) вверх до корневого уровня, чтобы рассчитать глубину вложенности и проверить права доступа.PARENTID указывает на несуществующий в базе идентификатор (запись родителя была физически удалена или не перенеслась), системный механизм валидации контекста также падает при попытке наложить блокировку на отсутствующую запись.X-lock на таблицу SCxxx), любая последующая проверка в этой же транзакции приводит к взаимной блокировке. В MS SQL 2005 данный механизм вел себя мягче, поэтому на старых версиях СУБД с теми же поврежденными данными ошибка могла не проявляться.Для точного обнаружения битых строк и зацикленных групп проанализируем таблицу справочника напрямую через SQL Server Management Studio. Имя таблицы справочника в базе данных 1С 7.7 имеет формат SCxxx (узнать точный номер таблицы можно в файле метаданных 1cv7.dd или 1cv7.dds).
Выполним поиск групп, которые ссылаются сами на себя в качестве родителя:
SELECT ID, DESCR, PARENTID
FROM SC1234
WHERE PARENTID = ID AND ISFOLDER = 1;
Проверим наличие записей, у которых указан несуществующий родитель («осиротевшие» элементы и группы):
SELECT ID, DESCR, PARENTID
FROM SC1234
WHERE PARENTID <> ' 0 '
AND PARENTID NOT IN (SELECT ID FROM SC1234);
Если запросы возвращают строки, значит, физическая целостность дерева справочника нарушена, что и блокирует транзакцию при интерактивном сохранении.
Рассмотрим способы устранения найденных нарушений в зависимости от доступности базы данных и регламентного окна на обслуживание.
Спр = СоздатьОбъект("Справочник.Номенклатура");
Если Спр.НайтиПоКоду("000123") = 1 Тогда
Спр.ИспользоватьРодителя(ПолучитьПустоеЗначение("Справочник.Номенклатура"));
Спр.Записать();
КонецЕсли;
Если проверка целостности показала отсутствие явных петель, но транзакция по-прежнему падает из-за конфликта блокировок на уровне СУБД при работе нескольких пользователей, отключим эскалацию блокировок для конкретной таблицы справочника.
Выполним в консоли СУБД команду, запрещающую повышать уровень блокировки со строк до блокировки таблицы:
ALTER TABLE SC1234 SET (LOCK_ESCALATION = DISABLE);
Проанализируем также уровень совместимости базы данных (Compatibility Level). В среде SQL Server 2008 R2 при использовании патчей («секретных релизов») рекомендуется использовать совместимость 100 (SQL Server 2008), избегая искусственного понижения до уровня 80 (SQL Server 2000), так как старый режим совместимости на новом ядре СУБД нередко провоцирует сбои во встроенном оптимизаторе запросов.
В качестве финальной профилактической меры выполним реиндексацию таблиц и пересчет счетчиков служебной таблицы _1SSYS, чтобы исключить дублирование внутренних уникальных идентификаторов платформы.