У нас уже накоплен опыт подобных проектов сложных обновлений, о нем писали здесь в трех частях:
- Промежуточные конфигурации, часть 1: зачем они нужны и как их правильно подобрать для обновления нетиповой 1С;
- Промежуточные конфигурации 1С, часть 2: что делать, если база не успевает обновиться;
- Промежуточные конфигурации 1С, часть 3: нетривиальные примеры обновления — случаи из жизни.
В этих статьях уже описаны некоторые наши наработки в типовых конфигурациях, назовем их базовыми оптимизациями:
- Ожидание генерации объектов расчетов.
- Исправлены ошибки в обработчиках.
- Изменен максимум выборки обрабатываемых данных.
- Отключена очистка обработанных очередей.
- Отключение версионирования объектов.
- Увеличено количество попыток выполнения обработчиков.
Начинаем изучение проблемы с первого прогона на копии базы объемом 580 ГБ. Замер обновления с базовыми оптимизациями составил 659 часов:

659 часов 24 минуты 34 секунды
Почему так долго? А можно побыстрее?
Наша первая итерация оптимизаций состоит из самых стандартных попыток что-нибудь ускорить.
Реструктуризация базы
В первую очередь начинаем сокращать то, что сократить проще всего — этапы реструктуризации (на скриншоте выделены желтым исходные замеры).

Зафиксировали замеры по всем этапам
По нашему опыту, время на реструктуризацию можно сократить в несколько раз за счет проведения реструктуризации на оптимизированном механизме платформы v2.
За счет чего так происходит:
- Старый механизм использует только внутренние средства платформы 1С и при любом изменении создает новые таблицы и полностью копирует в них все данные.
- Новый механизм изменяет таблицы «на месте» для самых частых операций (добавление/удаление полей, индексов), а в остальных случаях переносит данные одним мощным SQL-запросом, целиком перекладывая эту задачу на СУБД. Для работы оптимизированного механизма обязательным условием является установленная Java 8 на сервере 1С:Предприятия. Именно она служит инструментом для выполнения сложных операций.
Почему не использовать оптимизированный механизм всегда? Новый механизм может быть менее стабилен на некоторых специфических конфигурациях, в редких случаях возможны сбои. При этом откат изменений при ошибке невозможен, в отличие от старого механизма.
Распараллеливание обработчиков обновления
Для ускорения выполнения обработчиков обновления в некоторых типовых конфигурациях применяется многопоточная обработка данных. В актуальных версиях практически все тяжелые обработчики уже многопоточны, чего не скажешь о старых версиях.
По результатам первого прогона мы проводим анализ и выявляем длительные однопоточные обработчики, которые можно распараллелить:
- Из отчета «Прогресс отложенного обновления» выделили обработчики с наибольшим количеством объектов, зарегистрированных к обновлению.
- Из них отобрали обработчики, в свойстве которых не указано, что он выполняется многопоточно.
Прогресс отложенного обновления

Свойства обработчика
На первой же промежуточной 2.5.8.443 мы обнаружили несколько длительных обработчиков, которые запускались в однопоточном режиме:
- Справочник «Маршрутные карты» — к обновлению зарегистрировано 246247 элементов.
- Справочник «Номенклатура» — 71793 элементов.
- Регистр накопления «Выпуск продукции» — 501732 регистратора.
Для этих обработчиков мы не нашли «противопоказаний» к тому, чтобы переписать их на многопоточные.
P.S.: Не все обработчики можно сделать многопоточными! Например, расчет какого-нибудь значения в обработке данных может быть зависим от других обработанных объектов этого же типа.
Распределение запасов — регистры обеспечения
Перенесли обработчики формирования данных по регистрам Распределения запасов. Этот алгоритм мы описывали на примере УТ в статье «Промежуточные конфигурации 1С, часть 3: нетривиальные примеры обновления — случаи из жизни», под заголовком «Случай первый». Здесь всё то же самое.
Общая суть в том, что механизм обеспечения в редакции 2.5 менялся дважды, и получается, что при переходе с 2.4 на 2.5.22 заполнение регистра накопления «Распределение запасов - Движения» не нужно, и можно его исключить. А два других регистра, накопления «Запасы и потребности» и сведений «Распределение запасов», заполнить один раз в версии 2.5.17. Необходимо убедиться, что данные регистров не используются в других обработчиках обновления.
Провели анализ и обнаружили, что данные этих регистров используются в нескольких обработчиках обновления регистров (Состояния внутренних заказов, Состояния заказов клиентов, Состояния заказов на производство в очереди заказов, Состояния этапов производства) в версиях 2.5.8, 2.5.12.
Эти регистры мы сможем обновить только после заполнения регистров распределения запасов в 2.5.17, значит их обработчики тоже надо перенести в 2.5.17.
Теперь снова надо проверить, используются ли данные этих регистров в других обработчиках. Не получится ли так, что они потянут за собой все остальные обработчики обновления?😅

Повезло — эти регистры не использовались в других обработчиках обновления в версиях 2.5.8, 2.5.12, 2.5.17.
Приведем пример переноса обработчика РегистрыСведений.СостоянияЗаказовНаПроизводство.ОбработатьДанныеДляПереходаНаНовуюВерсию
В 2.5.8, 2.5.12 в переносимых обработчиках обновления ставим возврат:
Модуль менеджера регистра сведений
В 2.5.17 переносим вызов и процедуры обработчика:
Общий модуль ОбновлениеИнформационнойБазыУП
Модуль менеджера регистра сведений
Наш обработчик зависит от выполнения других обработчиков, поэтому добавляем типовые проверки и свою.

Модуль менеджера регистра сведений
Тут надо пояснить как работает цепочка перенесенных обработчиков при запуске отложенных обработчиков третьей промежуточной 2.5.17:
- Заполняется регистр накопления «Запасы и потребности», основными (монопольными) обработчиками, в нем уже зарегистрированы данные к обработке, функция ОбработкаДанныхЗавершена() по этому регистру вернет «Ложь».
- Регистр сведений «Распределение запасов» пустой, для него нет зарегистрированных данных к обработке — они появятся только после того, как будет заполнен регистр «Запасы и потребности». Функция ОбработкаДанныхЗавершена() по этому регистру вернет «Истина», потому что она проверяет как раз таки отсутствие зарегистрированных к обработке данных.
- После заполнения регистра «Запасы и потребности» не останется зарегистрированных к обработке данных, функция ОбработкаДанныхЗавершена() для этого регистра вернет «Истина».
- Для регистра «Распределение запасов» начинает выполняться регистрация данных к обработке.
Между 3 и 4 шагом получится так, что по одному регистру уже нет данных к обработке, а по-другому — еще нет, и обе функции вернут «Истина». Если в этот момент будет выполняться обработчик, он пройдет по типовым условиям и отработает некорректно.
Чтобы это не произошло, мы добавили проверку на константу, которую устанавливаем после регистрации данных к обработке для регистра «Распределение запасов».
Да, третья промежуточная 2.5.17 значительно видоизменилась, однако в итоге времени на обновление понадобилось даже меньше, чем при первом прогоне.
Неожиданный бонус
В версии 2.5.17 изменилась логика заполнения регистра «Состояния этапов производства», и в соответствие с ней часть записей оказалась не нужна. Для этого в типовой конфигурации добавили обработчик обновления, который удаляет данные:
Модуль менеджера регистра сведений
Для соблюдения целостности данных нам надо в правильном порядке выполнить перенесенные обработчики: сначала из 2.5.8 и 2.5.12, после из 2.5.17.
Но раз обработчик удаляет данные, то зачем нам их обрабатывать перед этим?
Мы решили выполнить сначала обработчик из 2.5.17, а затем уже наши перенесенные.
К удалению зарегистрировалось 1 млн записей. Поэтому вместо 2.4 млн записей мы обработали только 1.4 млн, за счет чего еще сократили время.
Модуль менеджера регистра сведений
За счет переноса обработчиков и выполнения реструктуризации оптимизированным механизмом нам удалось драматично сократить время на обновление первой промежуточной с 415 часов до 59.
Итог первой итерации оптимизаций
К этапу отложенных обработчиков 3 промежуточной ушло суммарно 190 часов. Не стали дожидаться завершения, так как этот результат все еще не устраивал.
Тут важно отметить, что все попытки обновления выполнялись на стороне заказчика. Он был настроен обновить базу на своем сервере и не желал передавать базу на нашу сторону на более производительный сервер. Но после первой итерации оптимизации, увидев, что результат еще не достигнут, все же согласился на передачу для замеров к нам.
Пока шло согласование мы приступили к следующей итерации ускорения обновления.
Вторая итерация: поиск альтернативных способов оптимизации обновления 1С и отключение итогов
Напоминаю, что наша цель — сократить часы на обновление на ВСЕ промежуточные, включая финальную, до 48 часов.
Необходимо применить альтернативные способы ускорения обновления. Из вариантов, которые могут дать большую эффективность, рассматриваем вариант использования прямых запросов к ИБ.
Немного теории: что можно сделать через прямые запросы
При работе с серверными базами данных сервер приложений 1С преобразует все действия с объектами в запросы: получение, изменение, создание, удаление — все эти операции транслируются в СУБД. Для каждой операции над каждым объектом отдельный запрос — обработка большого количества объектов происходит не быстро.
В то же время язык запросов позволяет выполнить операции массово над большим количеством объектов.
Идея! Мы можем использовать это, заменив типовой код обработчиков обновления на адаптированный запрос к базе.

Понятно, что есть обработчики, которые сложно и трудозатратно адаптировать — такие мы трогать не будем. Нас интересуют обработчики, в которых нет сложных расчетов, больших запросов получения данных, и в которых выполняются простые операции, например, заполнение реквизитов.
Немного практики, как работать с таблицами
Не будем описывать структуру хранения таблиц и работу с данными в них — этот материал потянет еще на одну статью. Вся информация есть на ИТС.
Мы написали функции получения структуры хранения данных объекта для работы с таблицами. Для нашей задачи нас интересуют:
- имя таблицы — для обращения к нужной таблице;
- тип данных — для выборки данных необходимого типа;
- поля — для обращения к полям (реквизиты, измерения, и т.п.) таблицы.
И несколько вспомогательных функций: получение идентификатора ссылки, получение полей, преобразование значения в шестнадцатеричный формат.
Получение структуры хранения данных объектов в формате СУБД
Для подключения к базе использовали драйвер Server Native Client.

Подключение к базе
Приведем пример обработчика, который мы переписали в первой промежуточной 2.5.8 в регистре накопления «Распоряжения на передачу из производства». Всего к обработке зарегистрировано 480987 регистраторов.
Прогресс отложенного обновления
Обработчик всего лишь заполняет измерение «Организация», но делает это очень долго даже многопоточно.
Замеры выполнения обработчиков обновления
Время выполнения обработчика на первом прогоне — 14.5 часов (длительность в колонке «Длительность» не соответствует времени, вычисляемому в колонке «Интервал запуска», но об этом будет позже — в третьей итерации оптимизаций)
Обработчик всего лишь выполняет заполнение измерения «Организация».

Код типового обработчика обновления
Переписываем обработчик на язык запросов:

Шаблоны запросов
В шаблонах, далее в коде, происходят замены на имена таблиц, полей, типов данных, которые мы получаем нашими функциями, описанными выше.
В итоге получаются два запроса, при выполнении которых:
- Выберутся все записи регистра, зарегистрированные к обновлению (соединение с таблицей изменений регистра).
- В этих записях значение поле «Организация» заменится на значение поле «Организация» из документа регистратора.
- Из таблицы изменений удалятся записи, зарегистрированные к обновлению.
При изменении данных регистра накопления таким способом таблицы итогов станут неактуальными, поэтому необходимо произвести пересчет итогов стандартными средствами.
Некоторые обработчики удалось не полностью переписать на запросы, их оставили комбинированными (оставили и типовую многопоточную обработку, и нашу).
При комбинированном выполнении необходимо учитывать, что типовое многопоточное распараллеливание «не видит», что мы что-то делаем прямыми запросами. Поэтому важно, чтобы сначала отработала наша часть, а затем уже типовое распараллеливание.
Также необходимо обработать исключительные ситуации, чтобы выполнение обработчика не останавливалось. Чтобы это реализовать, нам нужно было отслеживать статусы обработчика. Типовой регистр статусов обработчиков обновления мы решили не трогать, добавили свой.

Типовой обработчик обновления с доработками для выполнения запроса
Перед выполнением запроса устанавливаем статус обработчика, не забывая установить блокировку.
Обработчик выполнения запроса
После оптимизации запросов к базе получили заметное сокращение времени. Время выполнения обработчика 8 минут (при интервале запуска 18 минут. А на что ушли еще 10 минут - об этом в третьей итерации оптимизаций)
Замеры выполнения обработчиков обновления
Отключение итогов обновляемых регистров
Очень много времени уходило на обработчики обновления регистров, хоть они и были многопоточными. Решили попробовать отключить использование итогов у обновляемых регистров.
Подробнее о целесообразности отключения итогов и как это сделать мы писали в этой части статьи: «Промежуточные конфигурации 1С, часть 2: что делать, если база не успевает обновиться», часть «Пересчет итогов, отключение использования итогов», программный способ отключения.
Освежаем память — при отключении итогов будут недоступны таблицы итогов (виртуальные таблицы остатков и оборотов), при обращении к ним возникнет исключительная ситуация. Поэтому отключаем их только у регистров, у которых не используются виртуальные таблицы в обработчиках обновления.
В данном проекте в промежуточных подверглись отключению итогов следующие регистры:
- В 2.5.8: Выпуск продукции, Товары к отгрузке, Товары к поступлению.
- В 2.5.17: Запасы и потребности.
Многопоточные обработчики опять накладывают свои особенности работы:
- Включить итоги надо только после завершения обработки всех данных, иначе сокращения затрат по времени не будет.
- Необходимо учесть, что признак завершения обработки данных может быть одновременно в нескольких потоках, а запустить пересчет итогов надо только в одном.
Для этого мы добавили свои константы, отражающие факт отключения итогов и факт пересчета итогов по каждому регистру (не спрашивайте почему константы — так сложилось исторически). Константы устанавливаются в соответствующих операциях, и, так же как в случае с регистром, с установкой блокировки для предотвращения одновременного пересчета в нескольких потоках.
Пример отключения и включения ниже на скриншоте
Ниже приводим результаты замеров.
До отключения итогов:

- Товары к отгрузке — 13ч 40мин.
- Товары к поступлению — 14ч 10мин.
После отключения итогов:

- Товары к отгрузке - 11ч 40мин.
- Товары к поступлению - 13ч 15мин.
Итог второй итерации оптимизации
Отключение итогов, на первый взгляд, не дало существенного ускорения. А вот выполнение прямых запросов к ИБ позволило ускорить выполнение некоторых обработчиков с нескольких часов до нескольких минут, а некоторых даже до нескольких секунд.
Итог второй итерации оптимизаций
Однако после оптимизации замеры по всем четырем промежуточным показали 75 часов. Это все еще недостаточно быстро.
Этот и следующие замеры происходили уже на нашем сервере.По замерам видно, что использование производительной дисковой подсистемы сильно сказалось на замерах этапа реструктуризации:
- исходный замер ~ 63 часа
- замер с использование реструктуризации v2 ~ 20 часов
- замер с использование реструктуризации v2 на дисках NVMe ~ 3 часа
Третья итерация: финишная прямая
Для нужного результата остается ускорить еще примерно на 30 часов.
Вынужденное ожидание запуска обработчиков
В результатах выполнения обработчиков обновления мы обратили внимание на данные по оптимизированным обработчикам из первой итерации. Было видно, что длительность сильно не соответствует интервалу запуска.
На скриншоте ниже замер одного из обработчиков. По нему видно, что по датам обработчик шел 1.5 дня, а по времени — 2.5 часа.
Замеры выполнения обработчиков обновления
В этом обработчике регистра обеспечения мы ждали выполнение других обработчиков по запасам. Для этого в обработчик добавили условия на проверку их завершения.

Но хоть и стояли условия, отладка показывала, что есть какой-то лаг во времени выполнения обработчика.
Оказалось, что для многопоточных обработчиков (реализованных через типовой функционал БСП), выборка данных для обработки происходит не в процедуре обработчика, как для однопоточных. Выборка данных происходит в универсальной процедуре поиска данных, которая выполняется до обработчика. Каждое выполнение поиска занимает определенное время на выборку данных. Затем уже выбранная порция разбивается на потоки, и запускается обработчик обновления. А обработчик не срабатывает из-за условий проверки, но тратит время на их прохождение.
Решение оказалось довольно простым — после запуска отложенных обработчиков обновления приостановить обработчики, которые нужно выполнить после выполнения обработчиков по запасам.
Приостанавливаем обработчики
После выполнения обработчика меняем статус зависимых обработчиков на «Выполняется»
После этого интервал запуска стал сильно меньше, длительность работы обработчика уменьшилась. На примере нашего обработчика — сокращение с 2,5 часов до 44 минут.
Замеры выполнения обработчиков обновления
К сожалению, в результатах замеров не фиксируется время на выполнение поиска данных для обработки, поэтому невозможно точно определить, сколько времени мы на этом сэкономили.
Увеличение выборки до 10000
В самом начале статьи мы писали про наши базовые оптимизации — об изменении максимального количества выборки обрабатываемых данных.
Вплоть до финального этапа оптимизаций это не помогало в третьей промежуточной 2.5.17 — выполнение шло медленнее, чем с выборкой по 1000 записей.
Один из первичных замеров
Решили проверить еще раз, чтобы понять, почему способ не помогает. Включили настройку использования оптимизации в нашем общем модуле, которая влияет значение количества записей в выборке.
Общий модуль ОбновлениеИнформационнойБазы
Долго разбираться не пришлось — в этот раз данные обрабатывались намного быстрее.
Причиной ускорения стало отключение итогов. С отключенными итогами обработка данных регистра «Запасы и потребности» «взлетела».
Замеры выполнения обработчиков обновления
Перенос «неважных» обработчиков в последнюю промежуточную
Финальный прогон обновления базы со всеми оптимизациями составил 43 часа.
Итоги последней итерации оптимизации
Теперь времени на обновление хватало, но при обновлении рабочей базы всегда что-то может пойти не так. Поэтому желательно иметь «запас на всякий случай».
Такой запас мы смогли получить путем переноса длительного «неважного» обработчика обновления в последнюю промежуточную конфигурацию.
Во второй промежуточной 2.5.12 обработчик обновления справочника «Технологические операции» при выполнении выдавал слишком много ошибок неудавшихся попыток установки блокировки (техническая реализация типового обработчика). Из-за этого замедлялся процесс обновления. Данные этого справочника не использовались в других обработчиках обновления в этой и в остальных промежуточных. Значит, его можно перенести в последнюю промежуточную 2.5.22.
Общий модуль ОбновлениеИнформационнойБазыУП
От этих изменений общее время выполнения обработчиков не изменится. При этом до последней промежуточной обновление дойдет быстрее, и, если выделенного времени на обновление не останется, то процесс можно будет продолжить уже на базе с работающими пользователями.
ПЛОХИЕ ДАННЫЕ НЕПЛОХИЕ
Начиная с 2.5.7 появился функционал выявления проблем с данными во время работы обработчиков.
В более старых версиях, если обработчик обновления сталкивался с «кривыми» данными (например, незаполненным обязательным реквизитом), обновление просто прерывалось с критической ошибкой.
В новых версиях механизм стал более дружелюбным:
- Если во время выполнения обработчика произошла ошибка, то обрабатываемый объект попадет в список «плохих данных» и отметится как обработанный.
- Обработчик не зависнет, а продолжит обрабатывать следующие данные.
- В ходе обновления можно посмотреть такие данные — система выводит список объектов, которые вызвали проблему, причину ошибки и рекомендацию (как правило — это исправить вручную\перепровести).

В целом это очень полезно и удобно, когда обновление выполняется без промежуточных конфигураций.
Но это не подходит в нашем случае.
Как мы ранее уже упоминали в статье «Промежуточные конфигурации, часть 1: зачем они нужны и как их правильно подобрать для обновления нетиповой 1С», в разделе «Как правильно подготовить промежуточные конфигурации 1С»: в промежуточных конфигурациях сохранена только структура метаданных и доработки для корректной работы обработчиков, в них отсутствует вся доработанная логика, поэтому попытка исправления ошибки путем проведения документа может привести к другим ошибкам.
При этом после выполнения всех обработчиков будет установлен статус успешного выполнения обновления. Можно легко пропустить информацию о наличии проблем при обновлении.
В список «плохих данных» часто попадают объекты, при записи которых возникла блокировка, что бывает во время работы обработчиков обновления, или объекты, которые зависят от заполненности еще необработанных данных. И повторная обработка объекта, вероятно, выполнилась бы корректно.
Поэтому в своих проектах мы отключаем данный функционал, чтобы объект пытался обработаться до победного.
Отметка выполнения обработки «плохих» данных в обработчике обновления
Если же объект категорически отказывается обрабатывается из-за некорректного заполнения, то причину надо исправить еще до начала обновления. Разумеется, этот анализ надо проводить еще при первом прогоне промежуточных, внимательно изучать журнал регистрации и список ошибок при обновлении.

***
Подписывайтесь на наши публикации, будем продолжать делиться интересными кейсами по обновлению нетиповых 1С.
Вступайте в нашу телеграмм-группу Инфостарт