Было дано:
Сервер IBM на 2х четырехядерных XEON по 2Ггц, 16 Гб оперативной MS SQL 2000 1с 8.1 УПП 1.2.5 с изменениями в формах и модулях ключевых документов и справочников, несколько десятков внешних обработок и печатных форм — замена типовых. средний размер базы за время обновления 110-120 Гб содержание базы на начало обновления: производство, торговля(крупный и мелкий опт) и бухгалтерия с 2006 года; несколько тысяч контрагентов; от 10000 до 20000 торговых документов в месяц. перерыв в работе: с 19 часов субботы до 8 утра воскресенья. Ну и иногда 15-20 минут в день в районе обеда и 1 час где-то в промежуток между 23 и 4 часов ночи.
В результате: 1с 8.1 УПП 1.2.28 со всеми изменениями и рабочими внешними обработками размер базы 100 гб за счет частичной свертки базы за 2 года (пока без удаления документов — база будет ещё меньше).
После некоторых событий досталась мне одному УПП 1.2.5. Её конечно до этого периодически обновляли, но в тот момент последний доступный релиз был версии 1.2.19. Сначала я решил обновить сразу до последнего релиза. Но короткий анализ показал, что этого лучше не делать по нескольким причинам:
-
При обновлении структуры конфигурации, а не только кода, требуется реструктуризация соответствующих метаданных. При больших объемах данных это занимает очень много времени.
-
Дикое количество обновлений в типовых механизмах существенно усложняет перенос собственных изменений, т. к. нужно понять суть обновления и проследить как это повлияет на твой код и будет ли он вообще работать.
-
После обновления базы рекомендуется (а иногда необходимо) запускать пакет специальных обработок. Данный пакет обработок от релиза к релизу дополняется, но иногда полностью удаляется и пишется с нуля.
- Иногда происходят тотальные изменения в конфигурации, требующие предварительной подготовки БД (например, переход с учета авансов на счетах, на учет на регистрах)
(подробнее читать тут http://www.infostart.ru/public/18596/)
На первых 3 пункта по отдельности можно было забить (а до 4го ещё тогда не дошел), но все вместе их игнорировать сложно.Опущу технологию обновления — в общих чертах она совпадала с:
Резонные вопрос — а о чем тогда писать дальше? А вот есть плохо раскрытый пункт про работу руками...в том смысле, что перенос изменений в обновленные объекты. Я хотел написать про облегчение/уменьшение внесения как раз этих ручных операций. Далее пойдет описание событий, кому не интересно переходите к Заключению.
Что облегчило первый перенос - так это почти полностью комментированные изменения. Если этого не было сделано, то сделать это нужно обязательно. Выявить изменения можно запустив Сравнение конфигураций — Конфигурации поставщика и Конфигурации базы данных. У меня уже это было сделано.
Долго ли коротко, последовательно начал переносить изменения. Просто копировать код не получилось. Обновились и модули, в которые вносились изменения, и вызываемые процедуры из общих модулей, и сама структура метаданных. Таким образом, сначала приходилось разбираться зачем сделали изменение, а потом уже частенько заново писать код. Но сложнее всего было с формами: заново вносить изменения в форму — нудная и долгая работа. И опять — это был не конец. Остались внешние печатные формы и обработки, которые использовали типовые функции и процедуры. В них точно также пришлось разбираться заново и искать вызов типовых процедур (менялись названия, модули размещения и кол-во параметров). Особо хочется поблагодарить разработчиков 1с за замену функции ЗначениеНеЗаполнено на ЗначениеЗаполнено... Но вот, наконец, было закончен перенос изменений и я выгрузил cf файл. Как раз к новогодним праздникам и поспел. Обновил конфигурацию на основной базе, объединил с cf файлом...и запустил обновление конфигурации БД... Через несколько часов выявилась и критичность 1 пункта — времени. Конфигуратор отвалился с радостным сообщение о нехватке памяти... Есть такая беда с фрагментированностью оперативной памяти (или как-то так, на старых платформах приводило к ошибке SDBL). Лечится перезагрузкой. В общем, перезагрузил и всё заново. Если бы не такие большие праздники — не успел бы. Хорошо хоть с пакетом обработок проблем не случилось (хотя чуть позже столкнулся с проблемой для Бухгалтерии — пакет обработок выдавал ошибку после длительного процесса). Обновление случилось на грани и заняло несколько сотен часов.
Повторять такое обновление мне хотелось — начал думать как облегчить следующее.
Для начала я решил уменьшить количество измененного кода в типовых модулях. Открываем конфигуратор — какая интересная ветка в конфигурации — «Подписки на события» http://v8.1c.ru/overview/Term_000000153.htm. Достаточно много изменений в конфигурации были сделаны для того, чтобы проверять и ограничивать изменение данных. Именно для таких изменений хорошо подходит подписки на события. Завел общий модуль для подписок и насоздавал оных для замены части измененного кода. Обычно изменения превращались в процедуры, которые:
устанавливает значение переменной Отказ, которая регулирует дальнейшую запись или проведение объекта;
устанавливает какое-то конкретное значение в объекте перед/при его записи
выводит некоторое сообщение
делает дополнительные движения
Но все изменения таким образом переделать не получилось. Остались изменения, связанные с работой форм, полностью самописные функции и процедуры и им подобные. Для того, чтобы облегчить перенос этих изменений при обновлений нужно было уменьшить их объем. Для этого создал общий модуль НашМодуль и загнал туда все самописные функции и процедуры, естественно с ключевым словом Экспорт, если необходимо. Но на этом останавливаться не стал — куски дописанного кода заменил вызовом функций и процедур, которые поместил опять-таки в НашМодуль. Далее последовали куски измененного кода — небольшие изменения я оставил на месте пометив в комментариях в том числе и причину изменений, а вот большие изменения перенес в НашМодуль вместе с последующим типовым кодом. В измененные типовые процедуры добавил вызовы этих процедуры и функций, а после них подходящие по смыслу переходы: Возврат, Прервать, Продолжить. Всё, конечно, изменить малой кровью не получилось, но при последующих обновлениях перенос измененного кода существенно облегчился. Теперь достаточно получить список измененных два раза (относительно основной конфигурации и относительно обновленной), переписать куда-нибудь (хоть в блокнот) список объектов с парой строк измененного кода и после обновления проверить работоспособность функции и процедур в модуле НашМодуль.
Следующее, что я сделал — это вдруг увидел замечательную обработку «Декомпиляция и анализ форм с генерацией кода формы» http://www.infostart.ru/public/22147/. С помощью этой обработки я перевел все изменения в формах в код, который запихнул в тот же самый НашМодуль, а их вызов в модули форм в процедуры ПриОткрытии или ПередОткрытием. И вуаля! Никаких изменений в формах — только код, обновлять который уже много легче.
И всё вроде бы было замечательно...Но осталось несколько десятков внешних обработок и печатных форм. Заново в них исправлять из-за смены модуля, где сидит нужная функция — то ещё удовольствие. Решение было логическим продолжением предыдущих — вызов типовых был перенесен в НашМодуль, а во внешних обработках используется обращение к НашемуМодулю. Таким образом, после обновления достаточно будет проверить НашМодуль и не лазить по всем обработкам.
Закончив все предварительные изменения в конфигурации, я перешел к следующему обновлению. Делал я его уже более классическим способом через файл обновления. Только теперь мне не пришлось сравнивать формы и заново переносить гигантские куски кода или переделать типовые процедуры в нескольких местах. Обычно изменения сводились к добавлению 1-5 строчек в начало или конец процедуры... Вместо предыдущих сотен часов - обновление заняло около 8 часов!!! Причем теперь время обновления не зависит от дотошного знания изменений в конфигурации, т. к. изменений этих в типовых объектах — минимум. Кроме того, не приходится каждый раз обновлять множество внешних обработок!
В дополнении хочу дать ссылку на ещё один способ, прекрасно дополняющий, всё что здесь написано: http://www.infostart.ru/public/16980/ .
Заключение
Итак, что нужно делать, чтобы облегчить обновление?
0. Свести к минимуму все изменения в конфигурации: пытаться найти решение типовыми методами, пытать пользователей зачем им это нужно, а потом убедить, что не нужно.
1. Комментировать все изменения в конфигурации. Найти отличия от типовой можно через Сравнение конфигураций в режиме Конфигурация поставщика — Конфигурация базы данных
2. Вынос изменений, связанных с записью и обновлением объектов в Подписки на события
3. Уменьшение объема измененного кода в типовых процедурах за счет выноса их в собственный общий модуль. При этом, в типовых должны оставаться только вызовы процедур и функций из этого общего модуля.
4. Замена вызовов типовых процедур и функций в самописном коде (в т.ч. и во внешних обработках) на вызов процедур из собственного модуля. Эти процедуры по сути «ярлык» для вызова типовых. Преимущество в том, что нужно проверить только их, а не искать множество вызовов в куче самописного кода.
5. Использование обработки «Декомпиляция и анализ форм с генерацией кода формы» http://www.infostart.ru/public/22147/ для изменения форм объектов — не нужно сравнивать формы и переносить изменения в них, а только вызов процедуры.
В результате скорость обновления нетиповой конфигурации возрастает в несколько раз (в моем случае с пары сотен часов, до 8)
Приложение 1. Примеры процедур и функций
Пример процедуры обработки подписки "Перед записью" справочника номенклатуры. Процедура запрещает любые изменения в группах справочника, чтобы кривые руки на мышках, например, не перетащили ОС в Продукцию...Исключение составляют пользователи с полными правами.
Процедура НЕТРОГАЕМГРУППЫ(Источник,Отказ) Экспорт
Если Источник.ОбменДанными.Загрузка ИЛИ ПользователиИнформационнойБазы.ТекущийПользователь().Роли.Содержит(Метаданные.Роли.ПолныеПрава) Тогда
Возврат;
КонецЕсли;
Если Источник.ЭтоГруппа Тогда
Отказ=Истина;
КонецЕсли;
КонецПроцедуры
Пример, когда при записи реализации в её свойства записывается основной менеджер контрагента, если ещё не заполнено
Процедура УстановкаТорговогоПредставителяРеализацииПриЗаписи(Источник, Отказ) Экспорт //mav
Если Источник.ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
Если (НЕ ПользователиИнформационнойБазы.ТекущийПользователь().Роли.Содержит(Метаданные.Роли.нашБухгалтер)
И НЕ ПользователиИнформационнойБазы.ТекущийПользователь().Роли.Содержит(Метаданные.Роли.ПолныеПрава))
И ПроверкаДатыНаЗапрет(Источник.Дата) Тогда
Сообщить("Число дней превышено. Проводить нельзя.");
Отказ=Истина;
Возврат;
КонецЕсли;
Запрос=Новый Запрос();
Запрос.Текст="ВЫБРАТЬ
| ЗначенияСвойствОбъектов.Значение
|ИЗ
| РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
|ГДЕ
| ЗначенияСвойствОбъектов.Свойство.Код = ""00032 ""
| И ЗначенияСвойствОбъектов.Объект = &Ссылка";
Запрос.УстановитьПараметр("Ссылка",Источник.Ссылка);
Результат=Запрос.Выполнить().Выбрать();
Если Результат.Следующий() Тогда
Если ЗначениеЗаполнено(Источник.Сделка) Тогда
Запрос.УстановитьПараметр("Ссылка",Источник.Сделка);
Результат1=Запрос.Выполнить().Выбрать();
Если Результат1.Следующий() Тогда
Если Результат1.Значение<>Результат.Значение Тогда
Сообщить("Не совпадают менеджеры. Заказ-"+Результат1.Значение+", Реализация-"+Результат.Значение);
КонецЕсли;
Иначе
Если Результат.Значение<>Источник.Контрагент.ОсновнойМенеджерПокупателя Тогда
Сообщить("Не совпадает Основной менеджер-"+Источник.Контрагент.ОсновнойМенеджерПокупателя+" и Реализация-"+Результат.Значение);
КонецЕсли;
КонецЕсли;
Иначе
Если Результат.Значение<>Источник.Контрагент.ОсновнойМенеджерПокупателя Тогда
Сообщить("Не совпадает Основной менеджер-"+Источник.Контрагент.ОсновнойМенеджерПокупателя+" и Реализация-"+Результат.Значение);
КонецЕсли;
КонецЕсли;
Иначе
Менеджер=Справочники.Пользователи.ПустаяСсылка();
Если ЗначениеЗаполнено(Источник.Сделка) Тогда
Запрос.УстановитьПараметр("Ссылка",Источник.Сделка);
Результат=Запрос.Выполнить().Выбрать();
Если Результат.Следующий() Тогда
Менеджер=Результат.Значение;
Иначе
Менеджер=Источник.Контрагент.ОсновнойМенеджерПокупателя;
КонецЕсли;
Иначе
Менеджер=Источник.Контрагент.ОсновнойМенеджерПокупателя;
КонецЕсли;
Если ЗначениеЗаполнено(Менеджер) Тогда
Рег=РегистрыСведений.ЗначенияСвойствОбъектов.СоздатьМенеджерЗаписи();
Рег.Активность=Истина;
Рег.Значение=Менеджер;
Рег.Объект=Источник.Ссылка;
Рег.Свойство=ПланыВидовХарактеристик.СвойстваОбъектов.НайтиПоКоду("00032 ");
Рег.Записать();
//Сообщить("В реализации установлен менеджер "+Менеджер);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Пример использования декомпиляции форм для создания дополнительных элементов формы. В данном случае добавляется новая колонка в форму реализации. Реквизит "Дата выработки" был предварительно добавлен в реквизиты табличной части документа - это совершенно не мешает обновлению
Процедура mavСформироватьВнешнийВидЗаказПокупателя(ЭлементыФормы) Экспорт
//-------------------------------------
Колонка = ЭлементыФормы.Товары.Колонки.Вставить(19,"ДатаВыработки");
Колонка.УстановитьЭлементУправления( Тип("ПолеВвода") );
Колонка.Имя = "ДатаВыработки";
Колонка.Данные = "ДатаВыработки";
Колонка.Ширина = 15;
Колонка.ТекстШапки = "Дата выработки";
//.....................................
Колонка.ЭлементУправления.ВертикальноеПоложение = ВертикальноеПоложение.Верх;
Колонка.ЭлементУправления.КнопкаОчистки = ложь;
Колонка.ЭлементУправления.ОграничениеТипа = Новый ОписаниеТипов("",,,);
//Колонка.ЭлементУправления.ТипЗначения = Новый ОписаниеТипов("Строка",,,);
КонецПроцедуры
Пример для пункта 4. Создание "ярлыков" на типовые модули:
Функция mavПолучитьКурсВалюты(Валюта, ДатаКурса) Экспорт
Возврат(МодульВалютногоУчета.ПолучитьКурсВалюты(Валюта, ДатаКурса));
КонецФункции