В процессе разработки конфигураций на платформе 1С:Предприятие 8 программисты часто сталкиваются с выбором: довериться автоматическим механизмам платформы или управлять процессами вручную. Одним из таких камней преткновения является свойство Записывать модифицированные в объекте Движения документа. На первый взгляд, автоматика кажется удобной, однако в типовых решениях (ERP, УТ, КА, БП) вендор последовательно отказывается от этого механизма в пользу явного управления записью. В этой статье мы подробно разберем, какие технические и архитектурные причины стоят за этим решением.
Механизм Записывать модифицированные (или программный аналог WriteModified) предназначен для того, чтобы платформа самостоятельно определяла, какие наборы записей регистров были изменены в ходе процедуры ОбработкаПроведения, и записывала их в базу данных в самом конце транзакции. Казалось бы, это идеальный вариант: разработчику не нужно заботиться о вызове метода Записать() для каждого регистра. Но давайте проанализируем, почему этот подход не применим в высоконагруженных системах.
Рассмотрим ситуацию, когда запись движений происходит автоматически в конце транзакции. Если в процессе записи возникнет ошибка (например, нарушение уникальности записей или ошибка в подписке на событие), разработчику крайне сложно локализовать момент сбоя. Когда мы используем явный вызов Движения.ИмяРегистра.Записать(), мы точно видим в отладчике, на какой строке кода происходит обращение к СУБД. Это делает код детерминированным и предсказуемым. Автоматическая запись превращает финал процедуры проведения в «черный ящик», где ошибки могут всплывать в самый неожиданный момент, затрудняя диагностику.
Выясним, как платформа на самом деле понимает, что набор записей «модифицирован». Платформа не сравнивает текущее состояние коллекции в памяти с данными в таблице базы данных (это было бы слишком ресурсоемко). Флаг Модифицированность устанавливается в значение Истина, как только к коллекции применяется любое действие, изменяющее ее состав: добавление записи, удаление или изменение свойств существующих элементов.
Проанализируем пример: если мы прочитали движения из базы, полностью их очистили и заполнили теми же самыми данными, платформа пометит такой набор как модифицированный. В результате будет произведена перезапись в БД, хотя фактически данные не изменились. Это создает избыточную нагрузку на дисковую подсистему и сервер базы данных. При ручном управлении мы можем реализовать алгоритм «сравнить и только потом записать».
В высоконагруженных системах критически важен порядок захвата ресурсов. При использовании автоматической записи платформа записывает все измененные регистры «пачкой» в конце транзакции. Порядок этой записи может быть неочевидным. Если один документ записывает сначала РегистрНакопления.Запасы, а затем РегистрНакопления.Продажи, а другой документ (или фоновое задание) делает это в обратном порядке, возникает риск взаимоблокировки (Deadlock).
Явная запись позволяет нам строго регламентировать порядок обращения к таблицам. Мы можем записать наиболее конфликтные регистры в первую очередь или, наоборот, максимально отложить их запись, чтобы минимизировать время удержания блокировки. Посмотрим на пример правильной последовательности:
// Сначала записываем те регистры, по которым проверяются остатки
Движения.ТоварыНаСкладах.Записать();
// Проверяем остатки, если ошибка - отказ
Если Не ПроверитьОстатки() Тогда
Отказ = Истина;
Возврат;
КонецЕсли;
// И только потом записываем остальные, менее критичные регистры
Движения.Продажи.Записать();
В современных типовых конфигурациях активно используется режим Удаление движений: Не удалять автоматически. Это позволяет реализовать стратегию минимальной записи. Суть ее заключается в том, что при перепроведении документа мы сначала считываем старые движения, сравниваем их с новыми и записываем набор в базу только в том случае, если обнаружены значимые отличия. Механизм Записывать модифицированные слишком груб для такой тонкой настройки — он просто видит факт обращения к коллекции и инициирует запись.
Разберем, как вендор обходит ограничения платформы в БСП (Библиотеке стандартных подсистем). Вместо того чтобы полагаться на флаги модифицированности, используется структура ДополнительныеСвойства. В нее передаются инструкции о том, какие регистры нужно записывать в текущем контексте (например, при отложенном проведении или при обмене данными). Это позволяет коду «принимать решение» о записи, основываясь на бизнес-логике, а не на техническом факте изменения коллекции в памяти.
Проанализировав ситуацию, можно сделать вывод, что механизм Записывать модифицированные создавался как средство быстрого старта для простых задач. Однако для профессиональной разработки он имеет ряд критических недостатков:
Поэтому в реальных проектах мы рекомендуем придерживаться следующих правил:
Удаление движений в значение Не удалять автоматически.Движения.ИмяРегистра.Записать() в конце процедуры проведения.Такой подход обеспечит вашему решению высокую производительность, стабильность в многопользовательском режиме и простоту в поддержке.