Предыстория:
Как-то появилась необходимость быстро выполнить обработчики обновления после перехода на новый релиз. Стандартная обработка занимала неделю. Надо было уложиться в 2 стандартных выходных. Как же это сделать быстро и желательно без модификации конфигурации?
Можно написать обработку и запустить в нескольких сеансах вручную. Но обработка в разных сеансах будет обрабатывать одни и те же "куски" данных. Как просто выйти из этой ситуации?
Вариант решения: Каждый сеанс должен обрабатывать свой случайный диапазон данных.
Конечно, иногда они будут пересекаться. На этот случай перед обработкой диапазона устанавливаем управляемую блокировку. Да, время от времени блокировки сеансов будут "натыкаться" друг на друга, но при больших размерах выборки и малой порции это будет происходить редко.
Как использовать обработку:
1. В функцию "Получить запрос", помещаем свой запрос для получения обрабатываемых данных
&НаСервере
Функция ПолучитьЗапрос()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.ИсточникДанных = &ИсточникДанных";
Запрос.УстановитьПараметр("ИсточникДанных", Справочники.НастройкиОбменаДаннымиСУчетнымиСистемами.ПустаяСсылка());
Запрос.Текст = СтрЗаменить(Запрос.Текст, "ВЫБРАТЬ", "ВЫБРАТЬ ПЕРВЫЕ " + Формат(РазмерПорции*1000,"ЧГ="));
Возврат Запрос;
КонецФункции
2. При необходимости, в процедуре "ЗаблокироватьДанные" разрабатываем алгоритм блокировки:
&НаСервере
Процедура ЗаблокироватьДанные(ТаблицаДанных)
// блокировка данных, чтобы парралельный сеанс не обработал их повторно
// в некоторых случаях можно блокировку и не использовать, некоторые объекты обработаются повторно,
// в некоторых случая это допустимо, в некоторых опасно.
// К примеру, на основании одного документа создаётся второй, и повторная обработка приведёт к созданию двух
// документов вместо одного
// всё зависит от логики конкретной обработки
// тут для примера использую блокировку, хотя в этой задаче она не обязательна и будет вызывать лишние блокировки.
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("Справочник.Номенклатура");
ЭлементБлокировки.ИсточникДанных = ТаблицаДанных;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Ссылка", "Ссылка");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
Блокировка.Заблокировать();
КонецПроцедуры
3. В процедуре "ОбработатьОбъекты" непосредственно реализуем процесс обработки.
&НаСервере
Процедура ОбработатьОбъекты(ТаблицаДанных)
Для каждого Данные Из ТаблицаДанных Цикл
// ОБРАБОТКА ДАННЫХ
// Заменеяем на то что нам надо
ОбъНоменклатура = Данные.Ссылка.ПолучитьОбъект();
ОбъНоменклатура.ИсточникДанных = Справочники.НастройкиОбменаДаннымиСУчетнымиСистемами.SAP;
ОбъНоменклатура.ОбменДанными.Загрузка = Истина;
ОбъНоменклатура.Записать();
КонецЦикла;
КонецПроцедуры
4. Готовую обработку запускаем в нескольких сеансах.
5. Время от времени поглядываем на процесс, если она остановилась из-за блокировки, то запускаем заново.
Дополнительное описание работы обработки:
Основная рабочая лошадка - функция "ОбработатьПорцию". Она вызывается с помощью обработчика ожидания, пока возвращаемое значение отлично от 0-ля:
&НаСервере
Функция ОбработатьПорцию()
НачатьТранзакцию();
Попытка
ДанныеПорцииДляОбработки = ПолучитьДанныеПорцииДляОбработки();
ЗаблокироватьДанные(ДанныеПорцииДляОбработки);
ОбработатьОбъекты(ДанныеПорцииДляОбработки);
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки;
Возврат ПолучитьКоличествоОставшихсяДанных();
КонецФункции
Основная функция для обработки порции данных и выбора случайного диапазона:
ПолучитьДанныеПорцииДляОбработки()
&НаСервере
Функция ПолучитьДанныеПорцииДляОбработки()
Запрос = ПолучитьЗапрос();
ТаблицаДанныхИсходная = Запрос.Выполнить().Выгрузить();
// Определим случайный диапазон для обработки
ГСЧ = Новый ГенераторСлучайныхЧисел(ТекущаяУниверсальнаяДатаВМиллисекундах());
НомерСтрокиНачальный = ГСЧ.СлучайноеЧисло(0,ТаблицаДанныхИсходная.Количество()- 1);
НомерСтрокиКонечный = Мин(НомерСтрокиНачальный + РазмерПорции - 1, ТаблицаДанныхИсходная.Количество()-1);
// скопируем строки в этом диапазоне в результат
ТаблицаДанныхДляОбработки = ТаблицаДанныхИсходная.СкопироватьКолонки();
Для ТекНомерСтроки = НомерСтрокиНачальный ПО НомерСтрокиКонечный Цикл
НоваяСтрока = ТаблицаДанныхДляОбработки.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, ТаблицаДанныхИсходная[ТекНомерСтроки]);
КонецЦикла;
Возврат ТаблицаДанныхДляОбработки;
КонецФункции
Требования: Управляемые формы
Протестировано на: 1С:Предприятие 8.3 (8.3.13.1644)