При разработке в среде 1С:Предприятие 8 часто возникает необходимость разгрузить интерфейс пользователя и перенести тяжелые расчеты в фоновое задание. Типичный сценарий: на клиенте или на сервере подготавливается некоторая таблица данных, которую затем нужно передать в фоновый сеанс для обработки. Попытка использовать для этой цели стандартное Временное хранилище часто приводит к тому, что на стороне фонового задания метод ПолучитьИзВременногоХранилища() возвращает Неопределено. Для организации асинхронных процессов можно использовать примеры реализации длительных операций в БСП.
В этой статье мы подробно разберем, почему возникает эта проблема, как устроена архитектура работы с временным хранилищем в контексте фоновых заданий, а также рассмотрим три практических способа передачи таблиц значений любой сложности и объема.
Давайте проанализируем ситуацию, с которой столкнулся автор темы. В его коде адрес временного хранилища получается в родительском сеансе, передается в параметры фонового задания, но при попытке чтения возвращается пустое значение. Причина кроется в фундаментальном правиле работы платформы 1С: данные во временном хранилище жестко привязаны к конкретному сеансу пользователя.
Когда вы запускаете фоновое задание, платформа создает для его выполнения абсолютно новый, изолированный сеанс на сервере. Этот сеанс выполняется в своем собственном контексте, часто на другом рабочем процессе rphost или даже на другом физическом сервере в кластере. Из-за физической изоляции сеансов фоновое задание не имеет доступа к адресам временного хранилища, созданным в родительском (клиентском) сеансе. Любая попытка прочитать эти данные вернет Неопределено.
Важное исключение: работа с временным хранилищем в фоновых заданиях поддерживается платформой 1С только в одну сторону — из фонового задания обратно в родительский сеанс. Для этого:
Для передачи же данных в обратную сторону — из родительского сеанса в фоновое задание — этот механизм напрямую использовать нельзя. Рассмотрим, как решить эту задачу правильно.
Объект типа ТаблицаЗначений в 1С является сериализуемым. Это означает, что платформа умеет автоматически преобразовывать его в XML-последовательность для передачи между границами распределенных систем и процессов. Мы можем передать таблицу значений напрямую в качестве параметра фонового задания.
Рассмотрим, как реализовать этот подход на примере интеграции с БСП (Библиотекой Стандартных Подсистем) и механизмом дополнительных отчетов и обработок:
// Код на стороне родительского сеанса (вызов фонового задания)
// Формируем таблицу значений, которую необходимо обработать в фоне
ТаблицаДанных = ПолучитьТаблицуДляОбработки();
ПараметрыКоманды = Новый Структура;
// Помещаем таблицу напрямую в структуру параметров
ПараметрыКоманды.Вставить("ТаблицаДляОбработки", ТаблицаДанных);
// Запускаем фоновое задание через БСП
ДополнительныеОтчетыИОбработкиКлиент.ВыполнитьКомандуВФоне(
ИдентификаторКоманды,
ПараметрыКоманды,
Обработчик
);
На стороне фонового задания в модуле объекта дополнительной обработки мы принимаем параметры и извлекаем таблицу значений:
// Код внутри модуля внешней обработки
Процедура ВыполнитьКоманду(ИдентификаторКоманды, ПараметрыВыполнения) Экспорт
// Извлекаем переданную таблицу значений напрямую из параметров
Если ПараметрыВыполнения.Свойство("ТаблицаДляОбработки") Тогда
ТаблицаСтрок = ПараметрыВыполнения.ТаблицаДляОбработки;
Если ТипЗнч(ТаблицаСтрок) = Тип("ТаблицаЗначений") Тогда
// Выполняем полезную работу с таблицей
ОбработатьДанныеВФоне(ТаблицаСтрок);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Ограничение метода: При запуске фонового задания платформа выполняет глубокую сериализацию параметров в XML/XDTO, отправляет их менеджеру кластера rmngr, который затем пересылает их рабочему процессу rphost. Если ваша таблица значений содержит сотни тысяч строк, процесс сериализации и десериализации может перегрузить оперативную память сервера, вызвав ошибку Out Of Memory (OOM) или аварийное завершение рабочих процессов 1С.
Если таблица значений имеет значительный объем, оптимальным решением будет ее сохранение во временный файл на диске с последующей передачей пути к этому файлу в фоновое задание.
Давайте разберем по шагам, как реализовать этот безопасный сценарий:
ЗначениеВФайл или через XML/JSON).Важное архитектурное требование: В клиент-серверном варианте работы фоновое задание может запуститься на любом из серверов кластера 1С. Поэтому локальные пути вроде C:\Temp\data.tmp использовать нельзя — файл просто не будет найден. Временный файл должен быть сохранен в общей сетевой папке (UNC-путь), к которой предоставлен полный доступ на чтение и запись для учетной записи (например, usr1cv8), под которой запущены службы 1С на всех серверах кластера.
Рассмотрим пример реализации:
// Родительский сеанс: подготовка и сохранение данных
ТаблицаДанных = ПолучитьКрупнуюТаблицу();
// Формируем уникальное имя файла в сетевом хранилище
СетевойКаталог = "\\SharedServer\1C_Temp\";
УникальноеИмяФайла = СетевойКаталог + Строка(Новый УникальныйИдентификатор) + ".bin";
// Записываем таблицу значений в файл
ЗначениеВФайл(УникальноеИмяФайла, ТаблицаДанных);
// Передаем путь к файлу в фоновое задание
ПараметрыКоманды = Новый Структура;
ПараметрыКоманды.Вставить("ПутьКФайлуДанных", УникальноеИмяФайла);
ДополнительныеОтчетыИОбработкиКлиент.ВыполнитьКомандуВФоне(
ИдентификаторКоманды,
ПараметрыКоманды,
Обработчик
);
Ниже представлен код фонового задания, считывающий и удаляющий этот файл после обработки:
// Код фонового задания
Процедура ВыполнитьКоманду(ИдентификаторКоманды, ПараметрыВыполнения) Экспорт
Если ПараметрыВыполнения.Свойство("ПутьКФайлуДанных") Тогда
ПутьКФайлу = ПараметрыВыполнения.ПутьКФайлуДанных;
ФайлНаДиске = Новый Файл(ПутьКФайлу);
Если ФайлНаДиске.Существует() Тогда
// Восстанавливаем таблицу значений из файла
ТаблицаСтрок = ЗначениеИзФайла(ПутьКФайлу);
Попытка
// Обрабатываем полученные данные
ОбработатьДанныеВФоне(ТаблицаСтрок);
Исключение
ЗаписатьЛогОшибки(ОписаниеОшибки());
КонецПопытки;
// Обязательно удаляем временный файл для освобождения дискового пространства
Попытка
УдалитьФайлы(ПутьКФайлу);
Исключение
// Логируем невозможность удаления, если файл заблокирован процессом
ЗаписьЖурналаРегистрации("ОчисткаФайлов", УровеньЖурналаРегистрации.Предупреждение,,, "Не удалось удалить файл: " + ПутьКФайлу);
КонецПопытки;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Когда речь заходит о миллионах строк данных, использование дисковых файлов или сериализации в параметры может приводить к нестабильной работе системы. Наиболее надежным корпоративным стандартом является использование транзакционной базы данных (СУБД) как буфера обмена.
Для реализации этого метода мы создаем в конфигурации специальный вспомогательный Регистр сведений (например, БуферОбменаФоновыхЗаданий). Он должен содержать следующие ключевые поля:
ИдентификаторПакета (тип УникальныйИдентификатор) — для разделения данных различных потоков и заданий.НомерСтроки (тип Число) — для сохранения порядка строк таблицы.Алгоритм работы выглядит следующим образом:
УникальныйИдентификатор пакета.ИдентификаторПакета.Рассмотрим пример записи данных в регистр-буфер:
// Родительский сеанс: запись в буферный регистр
ИдентификаторПакета = Новый УникальныйИдентификатор;
ТаблицаДанных = ПолучитьСверхбольшуюТаблицу();
// Записываем данные в регистр сведений порциями для экономии памяти
НаборЗаписей = РегистрыСведений.БуферОбменаФоновыхЗаданий.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.ИдентификаторПакета.Установить(ИдентификаторПакета);
Для Каждого СтрокаТЗ Из ТаблицаДанных Цикл
НоваяЗапись = НаборЗаписей.Добавить();
НоваяЗапись.ИдентификаторПакета = ИдентификаторПакета;
НоваяЗапись.НомерСтроки = ТаблицаДанных.Индекс(СтрокаТЗ) + 1;
ЗаполнитьЗначенияСвойств(НоваяЗапись, СтрокаТЗ);
КонецЦикла;
НаборЗаписей.Записать();
// Передаем только ID пакета в параметры
ПараметрыКоманды = Новый Структура;
ПараметрыКоманды.Вставить("ИдентификаторПакета", ИдентификаторПакета);
ДополнительныеОтчетыИОбработкиКлиент.ВыполнитьКомандуВФоне(
ИдентификаторКоманды,
ПараметрыКоманды,
Обработчик
);
Фоновое задание считывает данные из БД, выполняет логику и очищает за собой таблицу СУБД:
// Код фонового задания
Процедура ВыполнитьКоманду(ИдентификаторКоманды, ПараметрыВыполнения) Экспорт
Если ПараметрыВыполнения.Свойство("ИдентификаторПакета") Тогда
IDПакета = ПараметрыВыполнения.ИдентификаторПакета;
// Считываем данные запросом
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ *
|ИЗ
| РегистрСведений.БуферОбменаФоновыхЗаданий КАК Буфер
|ГДЕ
| Буфер.ИдентификаторПакета = &IDПакета
|УПОРЯДОЧИТЬ ПО
| Буфер.НомерСтроки";
Запрос.УстановитьПараметр("IDПакета", IDПакета);
ТаблицаСтрок = Запрос.Выполнить().Выгрузить();
Попытка
// Обрабатываем данные
ОбработатьДанныеВФоне(ТаблицаСтрок);
Исключение
ЗаписатьЛогОшибки(ОписаниеОшибки());
КонецПопытки;
// Очищаем за собой буферный регистр
НаборЗаписей = РегистрыСведений.БуферОбменаФоновыхЗаданий.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.ИдентификаторПакета.Установить(IDПакета);
НаборЗаписей.Записать(); // Запись пустого набора удалит данные из БД
КонецЕсли;
КонецПроцедуры
Передача временных данных в фоновые сеансы 1С требует понимания архитектуры работы серверных процессов платформы. Использование временного хранилища «напрямую» невозможно из-за строгой изоляции сеансов. Применяя описанные нами методы (параметры, файлы или регистры сведений), вы сможете эффективно и безопасно передавать таблицы значений любого объема, гарантируя высокую производительность и отказоустойчивость вашей системы. При необходимости параллельной обработки больших массивов информации можно использовать шаблон многопоточной обработки данных на базе БСП.