Каталог решений - Ускорение перепроведения документов

Ускорение перепроведения документов

Ускорение перепроведения документов

В наличии

Описание незначительной доработки типовой конфигурации, которая привела к ускорению перепроведения документов на 40%.
Дорабатывалась УПП 1.3, но это должно без каких-либо изменений работать и в других конфигурациях с подобным подходом к проведению документов (КА, УТ 10)

Категория:

Описание

Необходимость в такой доработке возникла после того, как такая регулярная операция, как перепроведение месяца, перестала укладываться в ночь. Начальник IT сказал, что полгода назад всё было ОК, а значит, надо откатить все изменения, сделанные предыдущим программистом (я тогда только-только устроился на эту работу) за последние полгода, благо бэкап есть.
Идея интересная, чтобы не сказать больше, но я решил пойти другим путём: запустил замер производительности, перепровёл день и посмотрел, кто вылез в топ. Одним из рекордсменов оказалась процедура ОбщегоНазначения.УдалитьЗаписанныеДвиженияДокумента, которая вызывалась перед проведением каждого документа. Процедура записывает пустые наборы записей в каждый регистр, в котором проводимый документ является регистратором, что не есть хорошо, потому что обычно в большинстве этих регистров данный документ и так не делал движений.

Список регистров для очистки процедура получала от функции ПроведениеДокументов.МассивРегистровНужноОчистить, поэтому оставалось сделать так, чтобы эта процедура возвращала только те регистры, в которых есть движения.

Для этого в конфигурацию я добавил:

  • регистр сведений ГМ_НепустыеДвижения (по старой традиции этой конфигурации, истоков которой никто не помнит, все нетиповые объекты должны содержать префикс «ГМ_«) с измерениями Документ (ведущее, основной отбор) типа ДокументСсылка и НомерРегистра (основной отбор) типа Число(3,0,Неотрицательное).
  • параметр сеанса ГМ_РегистрыПоНомеру типа ФиксированныйМассив
  • параметр сеанса ГМ_НомераРегистровПоИмени типа ФиксированноеСоответствие
  • справочник ГМ_НомераРегистров с кодом типа Число длиной 3 (должно совпадать с длиной измерения НомерРегистра)

Затем подправил процедуру ПроведениеДокументов.МассивРегистровНужноОчистить:


Функция МассивРегистровНужноОчистить(ДокументОбъект) Экспорт
	
	МассивРегистров = Новый Массив();
	//Коровин 09.09.2014
	лствИмена=ПараметрыСеанса.ГМ_РегистрыПоНомеру;
	лНабор=РегистрыСведений.ГМ_НепустыеДвижения.СоздатьНаборЗаписей();
	лНабор.Отбор.Документ.Установить(ДокументОбъект.Ссылка);
	лНабор.Прочитать();
	Для каждого ТекЗапись Из лНабор Цикл
		МассивРегистров.Добавить("РегистрНакопления."+лствИмена[ТекЗапись.НомерРегистра-1]);
	КонецЦикла;
	лфлНетИнформацииОДвижениях=МассивРегистров.Количество()=0;//Мы, вероятно, в том периоде, где регистр ГМ_НепустыеДвижения пуст
	//Коровин 09.09.2014 - конец
	
	Для Каждого Движение ИЗ ДокументОбъект.Метаданные().Движения Цикл
		//Коровин 09.09.2014
		//МассивРегистров.Добавить(Движение.ПолноеИмя());
		Если лфлНетИнформацииОДвижениях ИЛИ Найти(Движение.ПолноеИмя(),"РегистрНакопления.")=0 Тогда
			МассивРегистров.Добавить(Движение.ПолноеИмя());
		КонецЕсли;
		//Коровин 09.09.2014 - конец
	КонецЦикла; 
	
	Возврат МассивРегистров;
	
КонецФункции

И добавил в привилегированный модуль процедуру, которую потом вызвал в начале процедуры ПередНачаломРаботыСистемы модуля обычного приложения:

Процедура ПодготовитьНомераРегистров() Экспорт
	лмИменаРегистров=Новый Массив;
	лствНомераПоИмени=Новый Соответствие;
	лЗапрос=Новый Запрос("ВЫБРАТЬ Код, Наименование ИЗ Справочник.ГМ_НомераРегистров УПОРЯДОЧИТЬ ПО Код");
	лВыборка=лЗапрос.Выполнить().Выбрать();
	лТекКод=0;
	Пока лВыборка.Следующий() Цикл
		лТекКод=лТекКод+1;
		Пока лВыборка.Код>лТекКод Цикл //На случай, которого не должно быть - когда коды идут не подряд
			лмИменаРегистров.Добавить();
			лТекКод=лТекКод+1;
		КонецЦикла;
		лмИменаРегистров.Добавить(лВыборка.Наименование);
		лствНомераПоИмени.Вставить(лВыборка.Наименование,лТекКод);
	КонецЦикла;
	Для каждого ТекРегистр Из Метаданные.РегистрыНакопления Цикл
		Если лствНомераПоИмени[ТекРегистр.Имя]=Неопределено Тогда //Новый регистр
			лЭлемент=Справочники.ГМ_НомераРегистров.СоздатьЭлемент();
			лЭлемент.Наименование=ТекРегистр.Имя;
			Если лЭлемент.Наименование<>ТекРегистр.Имя Тогда
				ВызватьИсключение "Наименование регистра "+ТекРегистр.Имя+" слишком длинное ("+СтрДлина(ТекРегистр.Имя)+" символов) и не влезает в справочник ГМ_НомераРегистров";
			КонецЕсли;
			лЭлемент.Записать();
			
			лмИменаРегистров.Добавить(ТекРегистр.Имя);
			лствНомераПоИмени.Вставить(ТекРегистр.Имя,лЭлемент.Код);
		КонецЕсли;
	КонецЦикла;
	ПараметрыСеанса.ГМ_РегистрыПоНомеру=Новый ФиксированныйМассив(лмИменаРегистров);
	ПараметрыСеанса.ГМ_НомераРегистровПоИмени=Новый ФиксированноеСоответствие(лствНомераПоИмени);
КонецПроцедуры

 И, наконец, добавил подписку на событие ПриЗаписи всех регистров накопления:

Процедура ПриЗаписиРегистраНакопления(Источник,Отказ,Замещение) Экспорт
	Если НЕ Отказ Тогда
		лНабор=РегистрыСведений.ГМ_НепустыеДвижения.СоздатьНаборЗаписей();
		лНабор.Отбор.Документ.Установить(Источник.Отбор.Регистратор.Значение);
		лНабор.Отбор.НомерРегистра.Установить(ПараметрыСеанса.ГМ_НомераРегистровПоИмени[Источник.Метаданные().Имя]);
		Если Источник.Количество() Тогда
			лЗапись=лНабор.Добавить();
			лЗапись.Документ=лНабор.Отбор.Документ.Значение;
			лЗапись.НомерРегистра=лНабор.Отбор.НомерРегистра.Значение;
			лНабор.Записать();
		ИначеЕсли Замещение Тогда
			//Как минимум РасчетСебестоимостиВыпуска в регистр НезавершенноеПроизводство пишет движения порциями,
			//так что без учёта флага Замещение есть риск сбросить признак, если последний дописанный набор был пустым
			лНабор.Записать();
		КонецЕсли;
	КонецЕсли;
КонецПроцедуры


В общем, работает всё так: при запуске 1С процедура ПодготовитьНомераРегистров заполняет параметры сеанса именами и номерами регистров накопления, создавая при необходимости нужные элементы в справочнике ГМ_НомераРегистров. При каждой записи непустого набора в какой-либо регистр накопления подписка на событие ПриЗаписи добавляет для этого регистра и регистратора набора запись в регистр ГМ_НепустыеДвижения. При записи пустого набора с установленным флагом Замещение такая запись, наоборот, удаляется.

При очистке движений документа изменённая процедура ПроведениеДокументов.МассивРегистровНужноОчистить читает из регистра ГМ_НепустыеДвижения все записи по этому документу и возвращает массив регистров, для которых записи есть. Если нет ни одной записи, то работает старый алгоритм — на тот случай, если перепроводимый документ был проведён до того, как в конфигурацию были внесены эти изменения. Это также означает, что из регистра ГМ_НепустыеДвижения можно смело вычищать записи по документам из закрытых периодов, которые трогать не предполагается, так как в случае необходимости перепроведение этих документов пройдёт нормально без всяких дополнительных действий.

Upd

В комментариях мне указали на то, что переименование регистра приведёт к проблемам. Тут я вижу три способа с этим разобраться:

1. Непосредственно перед сохранением изменений конфигурации, включающих в себя переименование регистра, переименовать этот регистр в справочнике ГМ_НомераРегистров. Самый простой и быстрый способ, который, однако, требует помнить о такой необходимости, потому что если сохранить изменения в конфигурацию базы данных, воспользоваться этим способом уже не выйдет.

2. Очистить регистр ГМ_НепустыеДвижения. Способ на случай, когда не воспользовались первым. Справочник ГМ_НомераРегистров тоже можно очистить (я бы очистил), но необязательно.

3. Доработать алгоритм так, чтобы в справочнике ГМ_НомераРегистров также хранилось имя таблицы базы данных соответствующего регистра. При переименовании регистра имя таблицы не поменяется и это можно будет использовать в процедуре ПодготовитьНомераРегистров.

Я эту проблему решать не стал, потому что не помню, чтобы хоть раз менял имя регистра (разве что с ИмяРегистра на УдалитьИмяРегистра, но к этому моменту в регистре, как правило, всё равно не было записей), но эта проблема на самом деле не единственная, есть и более вероятная и неприятная: некоторые обновления релиза включают в себя какую-нибудь обработку записей регистров. При этом могут быть перезаписаны наборы записей в том периоде, для которого в регистре ГМ_НепустыеДвижения нет записей и тогда при перепроведении тех документов этого периода, которые делали движения по перезаписанному регистру, движения по всем остальным регистрам задвоятся. Неприятность этой второй проблемы в том, что вы не узнаете о ней по матюкам программы, как непременно узнаете о первой. Зато если вы про неё в курсе, решить её можно способом №2 из решений первой проблемы.

В качестве профилактической меры можно после каждого обновления релиза запускать обработку, которая бы удаляла записи из регистра ГМ_НепустыеДвижения до определённой даты. Такую обработку по-любому стоит иметь для того, чтобы удалять заведомо старые записи, ибо маленькие регистры быстрее работают.

has been added to your cart:
Оформление заказа