Внешняя обработка в фоне: общий модуль, БСП и контроль выполнения

· 10 мин

Содержание

Фоновое задание из обработки запускают на сервере методом ФоновыеЗадания.Выполнить("ИмяМодуля.ИмяПроцедуры", Параметры). Процедура должна быть экспортной и лежать в неглобальном серверном общем модуле. Метод модуля объекта, а тем более модуля формы обработки, напрямую в фон не передать. Поэтому для внешней обработки нужен посредник: общий модуль в конфигурации или расширении, который в сеансе задания заново подключит обработку и вызовет её процедуру.

Если в базе есть БСП (Бухгалтерия 3.0, ЗУП 3.1, УТ 11 и другие типовые), есть путь без программирования посредника: зарегистрировать обработку как дополнительную с командой «Вызов серверного метода». Какой способ когда выбирать — в таблице.

СпособКогда подходитЧто нужноОграничения
ФоновыеЗадания.Выполнить + общий модульСвоя конфигурация, полный контроль над запуском и прогрессомОбщий модуль в конфигурации или расширенииВ типовой без расширения модуль не добавить
Дополнительная обработка БСП, команда «Вызов серверного метода»Типовая конфигурация, нужен запуск по кнопке или по расписаниюФункция СведенияОВнешнейОбработке() в модуле объектаНужны права на дополнительные обработки
ДлительныеОперации БСПОбработка встроена в конфигурациюМодуль ДлительныеОперации вашей версии БСПСигнатуры различаются в версиях БСП 2.x и 3.x

Как платформа выполняет фоновое задание

Фоновое задание — это отдельный сеанс информационной базы без интерфейса. В клиент-серверном варианте он работает в рабочем процессе кластера, в файловом — внутри процесса приложения, которое его запустило.

Из этого следуют правила, которые объясняют почти все проблемы:

  • 🧩 Отдельный сеанс. Всё, что было в сеансе пользователя, в задании отсутствует: подключённые внешние обработки, значения реквизитов формы, параметры сеанса, если их не установили заново.
  • 🔐 Права пользователя. Задание работает от имени того, кто его запустил, с его ролями.
  • 🖥️ Только сервер. Нельзя открыть форму, задать вопрос, показать предупреждение или диалог выбора файла. Пути к файлам — это пути на сервере 1С, а доступ к ним определяется учётной записью службы (по умолчанию USR1CV8).
  • 📦 Параметры передаются копией. В задание уходят только сериализуемые значения: примитивы, структуры, массивы, ссылки, таблицы значений, двоичные данные. Объект обработки, данные формы и COM-объекты передать нельзя.
  • ⏳ Асинхронность. Выполнить возвращает объект ФоновоеЗадание сразу, не дожидаясь окончания работы.

В файловой базе задание живёт внутри процесса клиента. Закрыли 1С — задание прервалось. В клиент-серверной базе оно продолжит работу и после закрытия формы или выхода пользователя.

Что подготовить в обработке и конфигурации

Для схемы с ФоновыеЗадания.Выполнить нужны три части.

  1. Общий модуль-посредник. Создайте в конфигурации или в расширении общий модуль, например ФоновыеВнешниеОбработки. Установите флаг «Сервер», флаги «Клиент» и «Глобальный» снимите. Флаг «Вызов сервера» не нужен: модуль вызывается из серверной процедуры формы.
  2. Экспортная процедура в модуле объекта обработки. Её вызовет посредник. Назовите её одинаково во всех своих фоновых обработках, например ВыполнитьВФоне, — посредник станет универсальным.
  3. Форма с командой запуска и реквизитами для идентификатора задания и адреса результата.

В типовой конфигурации на поддержке общий модуль удобнее добавить расширением. Конфигурация не снимается с поддержки, обновления ставятся как обычно. Порядок такой: Конфигурация → Расширения конфигурации, добавить расширение, создать в нём общий модуль, затем сохранить расширение в базу.

Расширение с общим модулем меняет конфигурацию базы. Делайте это вне рабочего времени и после резервной копии.

Как передать внешнюю обработку в задание

Сеанс задания не знает об обработке, открытой у пользователя. Ему нужно отдать двоичные данные файла .epf, а он сам подключит её через ВнешниеОбработки.Подключить.

Получить двоичные данные на сервере можно из свойства ИспользуемоеИмяФайла объекта обработки: при открытии файла платформа держит его копию, доступную серверу. Если в вашей схеме путь недоступен, передайте файл с клиента через НачатьПомещениеФайлаНаСервер и работайте с адресом во временном хранилище.

Код запуска через ФоновыеЗадания.Выполнить

Начнём с посредника. Он получает файл обработки и параметры, подключает обработку в сеансе задания и вызывает её экспортную процедуру.

// Общий модуль ФоновыеВнешниеОбработки (флаг «Сервер»)

Процедура ВыполнитьВнешнююОбработку(ДвоичныеДанныеОбработки, ПараметрыЗадания, АдресРезультата) Экспорт

	АдресОбработки = ПоместитьВоВременноеХранилище(ДвоичныеДанныеОбработки);
	// Третий параметр — безопасный режим. Истина запрещает работу с файлами, COM и внешними компонентами
	ИмяОбработки = ВнешниеОбработки.Подключить(АдресОбработки, , Истина);
	Обработка = ВнешниеОбработки.Создать(ИмяОбработки);

	Обработка.ВыполнитьВФоне(ПараметрыЗадания, АдресРезультата);

КонецПроцедуры

Теперь модуль формы обработки. Команда «Запустить» готовит адрес результата и стартует задание, обработчик ожидания следит за ним.

// Модуль формы. Реквизиты формы: ИдентификаторЗадания (УникальныйИдентификатор), АдресРезультата (Строка)

&НаКлиенте
Процедура Запустить(Команда)

	ЗапуститьНаСервере();
	ПодключитьОбработчикОжидания("ПроверитьЗадание", 2);

КонецПроцедуры

&НаСервере
Процедура ЗапуститьНаСервере()

	АдресРезультата = ПоместитьВоВременноеХранилище(Неопределено, УникальныйИдентификатор);

	ОбработкаОбъект = РеквизитФормыВЗначение("Объект");
	ДвоичныеДанные = Новый ДвоичныеДанные(ОбработкаОбъект.ИспользуемоеИмяФайла);

	ПараметрыЗадания = Новый Структура;
	ПараметрыЗадания.Вставить("ДатаНачала", Объект.ДатаНачала);
	ПараметрыЗадания.Вставить("ДатаОкончания", Объект.ДатаОкончания);

	Параметры = Новый Массив;
	Параметры.Добавить(ДвоичныеДанные);
	Параметры.Добавить(ПараметрыЗадания);
	Параметры.Добавить(АдресРезультата);

	Задание = ФоновыеЗадания.Выполнить(
		"ФоновыеВнешниеОбработки.ВыполнитьВнешнююОбработку",
		Параметры,
		"ПересчетЦен",                         // ключ: не даст запустить второй экземпляр
		"Внешняя обработка: пересчёт цен");    // наименование в списке заданий

	ИдентификаторЗадания = Задание.УникальныйИдентификатор;

КонецПроцедуры

Строку с именем метода пишите точно: ИмяОбщегоМодуля.ИмяПроцедуры, без скобок и параметров. Параметры передаются массивом в том порядке, в котором объявлены в процедуре.

Зачем адрес результата создают в форме

Задание не может вернуть значение напрямую. Обычный приём — создать в сеансе пользователя адрес во временном хранилище, привязанный к УникальныйИдентификатор формы, и передать его в задание. Задание записывает результат по этому адресу, форма забирает. Хранилище очистится само, когда закроют форму.

Логика обработки внутри фонового задания

Модуль объекта обработки содержит саму работу. Здесь удобно сообщать о прогрессе: сообщения, выведенные в задании, накапливаются и забираются формой.

// Модуль объекта обработки

Процедура ВыполнитьВФоне(ПараметрыЗадания, АдресРезультата) Экспорт

	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	Номенклатура.Ссылка КАК Ссылка
	|ИЗ
	|	Справочник.Номенклатура КАК Номенклатура
	|ГДЕ
	|	НЕ Номенклатура.ЭтоГруппа
	|	И НЕ Номенклатура.ПометкаУдаления";

	Выборка = Запрос.Выполнить().Выбрать();
	Всего = Выборка.Количество();
	Обработано = 0;

	Пока Выборка.Следующий() Цикл

		// ... полезная работа с Выборка.Ссылка ...
		Обработано = Обработано + 1;

		Если Обработано % 500 = 0 Тогда
			Сообщение = Новый СообщениеПользователю;
			Сообщение.Текст = СтрШаблон("Обработано %1 из %2",
				Формат(Обработано, "ЧГ=0"), Формат(Всего, "ЧГ=0"));
			Сообщение.Сообщить();
		КонецЕсли;

	КонецЦикла;

	ПоместитьВоВременноеХранилище(Новый Структура("Обработано", Обработано), АдресРезультата);

КонецПроцедуры

Пример: в справочнике 12 400 позиций, сообщение уходит каждые 500. Форма увидит 24 сообщения: последнее — «Обработано 12000 из 12400». Финальную цифру 12 400 вернёт структура результата.

Не вызывайте в этой процедуре формы, Вопрос, Предупреждение и диалоги выбора файла — задание упадёт с ошибкой, потому что интерфейса у него нет.

Если процедура пишет файлы или работает с COM, безопасный режим при подключении придётся отключить (Ложь в третьем параметре Подключить). Это требует административных прав и включает защиту от опасных действий. Отключайте только для своих проверенных обработок.

Как отслеживать и останавливать задание

Обработчик ожидания раз в 2 секунды спрашивает сервер о состоянии и забирает накопленные сообщения.

&НаКлиенте
Процедура ПроверитьЗадание()

	Состояние = СостояниеЗадания(ИдентификаторЗадания);

	Если Состояние = "Активно" Тогда
		Возврат;
	КонецЕсли;

	ОтключитьОбработчикОжидания("ПроверитьЗадание");

	Если Состояние = "Завершено" Тогда
		Результат = ПолучитьИзВременногоХранилища(АдресРезультата);
		ПоказатьПредупреждение(, "Готово. Обработано: " + Формат(Результат.Обработано, "ЧГ=0"));
	КонецЕсли;

КонецПроцедуры

&НаСервереБезКонтекста
Функция СостояниеЗадания(Идентификатор)

	Задание = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(Идентификатор);
	Если Задание = Неопределено Тогда
		Возврат "НеНайдено";
	КонецЕсли;

	Для Каждого Сообщение Из Задание.ПолучитьСообщенияПользователю(Истина) Цикл
		Сообщение.Сообщить();
	КонецЦикла;

	Если Задание.Состояние = СостояниеФоновогоЗадания.Активно Тогда
		Возврат "Активно";
	ИначеЕсли Задание.Состояние = СостояниеФоновогоЗадания.Завершено Тогда
		Возврат "Завершено";
	ИначеЕсли Задание.Состояние = СостояниеФоновогоЗадания.ЗавершеноАварийно Тогда
		ОбщегоНазначения.СообщитьПользователю(ПодробноеПредставлениеОшибки(Задание.ИнформацияОбОшибке));
		Возврат "Ошибка";
	КонецЕсли;

	Возврат "Отменено";

КонецФункции

Если в базе нет БСП, замените ОбщегоНазначения.СообщитьПользователю на создание СообщениеПользователю, как в модуле объекта выше.

Параметр Истина в ПолучитьСообщенияПользователю удаляет полученные сообщения, иначе форма будет показывать их повторно при каждой проверке.

Отмена и поиск заданий

  • ⛔ Отменить: найдите задание по идентификатору и вызовите Задание.Отменить(). Отмена не мгновенная: задание остановится, когда платформа сможет его прервать. Транзакция, начатая заданием, откатится.
  • 🔎 Найти все свои задания: ФоновыеЗадания.ПолучитьФоновыеЗадания(Новый Структура("Ключ, Состояние", "ПересчетЦен", СостояниеФоновогоЗадания.Активно)).
  • ⏱️ Дождаться синхронно на сервере: Задание.ОжидатьЗавершенияВыполнения(Таймаут). Метод доступен в платформе с номером не ниже 8.3.13; в более ранних использовался ОжидатьЗавершения.

Без программирования задания видно в типовых конфигурациях на БСП: Администрирование → Обслуживание → Регламентные операции → Регламентные и фоновые задания. В клиент-серверной базе сеанс задания также виден в консоли кластера в списке сеансов информационной базы.

Завершение сеанса фонового задания в консоли кластера обрывает операцию на середине, а незавершённая транзакция откатывается. Если задание массово меняет данные, лучше дать ему закончить или отменить его кодом.

Вариант для типовой конфигурации через дополнительные обработки

Когда посредника в конфигурацию добавлять не хочется, используйте механизм дополнительных обработок БСП. Платформа сама выполнит серверную команду на сервере и даст поставить её на расписание.

  1. В модуле объекта обработки опишите экспортную функцию СведенияОВнешнейОбработке(). В ней укажите вид «Дополнительная обработка» и команду с типом использования «Вызов серверного метода». Структуру описания удобно получить функцией ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке() из той же БСП.
  2. В том же модуле создайте экспортную процедуру ВыполнитьКоманду(ИдентификаторКоманды, ПараметрыВыполненияКоманды) — в ней и будет вся работа.
  3. Зарегистрируйте файл: Администрирование → Печатные формы, отчеты и обработки → Дополнительные отчеты и обработки, кнопка «Добавить из файла».
  4. На вкладке команд в карточке задайте размещение в разделе. Для серверной команды там же настраивается расписание — тогда она будет выполняться регламентным заданием без пользователя.

Плюс схемы — не нужны ни расширение, ни общий модуль. Минус — меньше контроля над прогрессом, а обработку придётся перерегистрировать после каждого изменения файла.

Почему задание не запускается или падает

Диагностика: выберите симптом

Как ускорить фоновую обработку больших объёмов

Одно задание обрабатывает данные в один поток. Когда объектов десятки тысяч, помогают такие приёмы:

  • ✂️ Делите на порции. Выберите ссылки запросом, разбейте на части по 1 000–5 000 и запустите несколько заданий с разными порциями. Ограничьте число одновременных заданий: 4 параллельных задания на сервере с 4 ядрами обычно дадут больше, чем 20.
  • 🔒 Короткие транзакции. Одна транзакция на весь объём держит блокировки и мешает пользователям. Записывайте каждый объект или пачку в своей транзакции.
  • ⚔️ Порции без пересечений. Если два задания пишут в одни и те же регистры с одинаковыми измерениями, они ждут друг друга на управляемых блокировках. Делите данные по организациям, складам или периодам.
  • 📉 Не грузите лишнее. Вместо Ссылка.ПолучитьОбъект() ради чтения одного реквизита берите его запросом. Объект получайте, только когда его надо записать.
  • 🕒 Реже опрашивайте задание. Проверка раз в 1–2 секунды не нагружает сервер, а опрос каждые 0,1 секунды создаёт лишние серверные вызовы. Для долгих операций интервал можно увеличивать.
  • 🌙 Тяжёлое — по расписанию. Пересчёты на сотни тысяч записей ставьте регламентным заданием на ночь.

Для параллельных порций родительское задание запускает дочерние и ждёт их через ФоновыеЗадания.ОжидатьЗавершенияВыполнения(МассивЗаданий) (платформа с номером не ниже 8.3.13). После ожидания проверьте состояние каждого дочернего: одно аварийное не остановит остальные.

Частые вопросы

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