1с сериализованные параметры фонового задания имеют слишком большой размер
Длительные операции на сервере
Область применения: управляемое приложение.
1. При разработке конфигураций следует избегать длительных вызовов из клиентского кода в серверный. Все длительные серверные вызовы, которые могут выполняться более 8 секунд в обычных сценариях работы пользователя, следует выполнять асинхронно, с помощью фонового задания.
К таким операциям относятся: формирование отчета, групповая обработка объектов, загрузка или выгрузка данных в другое приложение, заполнение больших табличных частей и т.п.
В противном случае такие вызовы могут привести к потере работоспособности приложения или затруднению работы с ним:
- браузер может предложить прекратить длительно выполняющийся сценарий, после чего приложение станет неработоспособным;
- веб сервер может прервать длительное обращение к серверу 1С:Предприятия и вернуть ошибку 504 (шлюз не отвечает);
- в случае длительного выполнения операции, у пользователя нет возможности отменить ее.
2.1. Общий подход к асинхронному выполнению длительных серверных операций с помощью фонового задания:
-
Код, выполняющий длительную обработку данных, располагается в модуле менеджера объекта* или в общем модуле. Результат своей работы он помещает во временное хранилище;
а для прочих мест – выводится блокирующая форма ( РежимОткрытияОкна = БлокироватьОкноВладельца ), на которой размещена декорация с анимированной картинкой и кнопка «Отмена» :
2.2. Асинхронное формирование отчета требуется только для тех отчетов, которые
- разработаны без использования СКД или с использованием СКД, но с переопределенной процедурой формирования отчета (переопределен обработчик кнопки «Сформировать» или в обработчике модуля отчета ПриКомпоновкеРезультата устанавливается СтандартнаяОбработка = Ложь ).
- и формирование которых, как правило, занимает длительное время.
Поведение таких отчетов должно быть максимально похожим на поведение отчетов на базе СКД, а именно:
- форму отчета не следует блокировать на время его формирования;
- пользователь может изменить настройки и переформировать отчет, не дожидаясь окончания его формирования;
- при закрытии формы отчета, формирование отчета прерывается.
3. При использовании в конфигурации Библиотеки стандартных подсистем в распоряжении разработчика имеются вспомогательные функции и процедуры общих модулей ДлительныеОперации , ДлительныеОперацииКлиент , а также процедура УстановитьСостояниеПоляТабличногоДокумента общего модуля ОбщегоНазначенияКлиентСервер .
Пример выполнения функции в фоновом задании при использовании в конфигурации Библиотеки стандартных подсистем. В модуле менеджера объекта размещена функция, которая выполняет поиск настроек и возвращает их:
Функция ОпределитьНастройкиУчетнойЗаписи(АдресЭлектроннойПочты, Пароль) Экспорт
.
Возврат Настройки;
КонецФункции
В форме объекта выполняется вызов этой функции в фоновом задании в три этапа:
1) запуск фонового задания на сервере,
2) подключение обработчика завершения фонового задания на клиенте,
3) обработка результата выполнения фонового задания.
&НаКлиенте
Процедура НастроитьПараметрыПодключенияАвтоматически()
// 1. Запуск фонового задания на сервере.
ДлительнаяОперация = НачатьПоискНастроекУчетнойЗаписи();
// 2. Подключение обработчика завершения фонового задания.
ПараметрыОжидания = ДлительныеОперацииКлиент.ПараметрыОжидания(ЭтотОбъект);
Оповещение = Новый ОписаниеОповещения("ПриЗавершенииПоискаНастроек", ЭтотОбъект);
ДлительныеОперацииКлиент.ОжидатьЗавершение(ДлительнаяОперация, Оповещение, ПараметрыОжидания);
КонецПроцедуры
&НаСервере
Функция НачатьПоискНастроекУчетнойЗаписи()
ПараметрыВыполнения = ДлительныеОперации.ПараметрыВыполненияФункции(УникальныйИдентификатор);
Возврат ДлительныеОперации.ВыполнитьФункцию(ПараметрыВыполнения, "Справочники.УчетныеЗаписиЭлектроннойПочты.ОпределитьНастройкиУчетнойЗаписи",
АдресЭлектроннойПочты, Пароль);
КонецФункции
// 3. Обработка результата выполнения фонового задания.
&НаКлиенте
Процедура ПриЗавершенииПоискаНастроек(Результат, ДополнительныеПараметры) Экспорт
Если Результат = Неопределено Тогда // Пользователь отменил задание.
Возврат;
КонецЕсли;
Если Результат.Статус = "Ошибка" Тогда
ВызватьИсключение Результат.КраткоеПредставлениеОшибки;
КонецЕсли;
Настройки = ПолучитьИзВременногоХранилища(Результат.АдресРезультата);
УдалитьИзВременногоХранилища(Результат.АдресРезультата);
УстановитьНастройкиУчетнойЗаписи(Настройки);
Методическая рекомендация (полезный совет)
3.1. При каждом выполнении фонового задания его результат помещается во временное хранилище на время жизни формы:
ПараметрыВыполнения = ДлительныеОперации.ПараметрыВыполненияФункции(УникальныйИдентификатор);
ДлительныеОперации.ВыполнитьФункцию(ПараметрыВыполнения, ПараметрФоновогоЗадания);
Если длительная операция выполняется пользователем многократно, пока эта форма открыта, то временные хранилища накапливаются, что вызывает рост потребления памяти. Поэтому для уменьшения расхода оперативной памяти в большинстве случаев рекомендуется очищать временное хранилище сразу после получения результата фонового задания:
Настройки = ПолучитьИзВременногоХранилища(Результат.АдресРезультата);
УдалитьИзВременногоХранилища(Результат.АдресРезультата); // Данные во временном хранилище больше не требуются.
Если же результат фонового задания требуется сохранять на протяжении нескольких серверных вызовов, то необходимо передавать фиксированный адрес заранее инициализированного временного хранилища:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ)
АдресРезультатаФоновогоЗадания = ПоместитьВоВременноеХранилище(Неопределено, УникальныйИдентификатор); // Резервируем адрес временного хранилища
КонецПроцедуры
&НаСервере
Функция НачатьПоискНастроекУчетнойЗаписи()
ПараметрыВыполнения = ДлительныеОперации.ПараметрыВыполненияФункции(УникальныйИдентификатор);
ПараметрыВыполнения.АдресРезультата = АдресРезультатаФоновогоЗадания; // всегда используем одно и то же временное хранилище
Возврат ДлительныеОперации.ВыполнитьФункцию(ПараметрыВыполнения,
"Справочники.УчетныеЗаписиЭлектроннойПочты.ОпределитьНастройкиУчетнойЗаписи",
АдресЭлектроннойПочты, Пароль);
КонецФункции
4. Если в конфигурации реализуются алгоритмы, инициирующие запуск фоновых заданий или запись данных информационной базы без участия пользователя (например, регулярное обновление информации в открытой форме), то в них следует проверять, что в текущем сеансе не установлен монопольный режим. В противном случае, следует блокировать попытки выполнения таких действий. Например:
Если МонопольныйРежим() Тогда
Возврат;
КонецЕсли;
5. В некоторых случаях возникает необходимость в выполнении длительных операций, требующих установки монопольного режима доступа к информационной базе. Например:
Обновление данных ИБ при первом интерактивном запуске программы после обновления конфигурации; Выгрузка данных информационной базы в файл для перехода в сервис; Использования монопольного режима для снижения времени выполнения массовых операций по изменению данных;При этом необходимо сначала устанавливать монопольный режим, а затем выполнять запуск фонового задания, которое реализует саму длительную операцию. В этом случае фоновым заданием будет унаследован монопольный режим, ранее установленный из пользовательского сеанса (см. документацию к платформе).
На время выполнения этого фонового задания следует блокировать весь интерфейс приложения, открывая форму ожидания завершения операции в режиме РежимОткрытияОкна = БлокироватьВесьИнтерфейс. Блокировать интерфейс приложения требуется потому, что на время выполнения задания полноценная работа пользователя с приложением уже невозможна:
- Если пользователь(*) попытается записать какой-либо объект, это приведет к ошибке (из-за установленного монопольного режима);
- В ряде случаев могут запускаться фоновые задания в качестве реакции на действия пользователя случае (при поиске в динамическом списке, при вводе по строке, формировании отчетов и пр.), которые также завершатся с ошибкой.
Кроме того, на самой форме ожидания длительной операции не следует размещать элементы управления, которые могут приводить к запуску таких фоновых заданий. Например: поля ввода, динамические списки и отчеты.
* Примечание: ошибки записи также возникают в тех случаях, когда объекты записываются программно, например, из обработчиков ожидания. В них также следует проверять монопольный режим согласно п.5.
1С:Предприятие > 8.1.8, клиент-серверная архитектура, MS SQL.
Общий неглобальный серверный модуль. Обработка с одной формой, одной кнопкой и одним табличным полем.
Нажав кнопку на форме, запустить некий долгий алгоритм расчета так, чтобы при этом форма была доступна для нажатия других кнопок, менюшек, перемещения.
По окончании расчета передать результат расчета в табличное поле и отобразить результат.
Методика:
Как показала практика, из отработавшего фонового задания вернуть штатным путем каке-либо данные невозможно. Имеется 2 способа, позволяющих это
делать:
1) фоновое задание пишет результат своей работы в БД, обработка отслеживает, что фоновое задание завершилось и считывает из БД результат. В качестве места хранения результата предлагается
использовать регистр сведений, где измерение хранит UID экземпляра фонового задания, а ресурс имеет тип ХранилищеЗначений.
2) извращенный способ – передать значение через программную генерацию исключения (оператор «ВызватьИсключение»).
Решение:
Описывать способ с регистром сведений не буду, так как это достаточно просто реализовать, а минусом является необходимость добавления в конфигурацию регистра сведений. Поэтому опишу вариант с
«ВызватьИсключение»:
Процедура общего модуля:
Модуль формы обработки:
Результат:
Жмем кнопку. 15 секунд наслаждаемся перетаскиванием формы и прочими действиями (кроме закрытия).
Потом резко (само! шайтан! :) заполнится табличное поле на форме, чего и следовало ожидать.
Нагрузочные испытания проведены на передаче таблицы значений из 2 полей с количеством строк 100 тыс.
шт. – успешно.
Надеюсь, статья будет вам полезна.
Выражаю благодарность всем, кто помог мне осуществить данную операцию.
С уважением, Алексей Шачнев.
Вместо обработки ожидания использовал отправку датаграммы из фонового задания форме посредством MS WinSock (ActiveX)
Нужно было выполнить заполнение дерева в фоне (что бы не блокировать основной интерфейс). Нашел эту статейку.
Собственно удалось выполнить фоновое задание без "ВызватьИсключение".
8.2.19.68 (Управляемое приложение)
Лучше придумать другой алгоритм "диспетчерзиации" задач между запущенными заданиями. Так, чтобы не требовалось гонять половину базы в оперативной памяти (а это и происходит при передаче параметров фонового задания).
(3) Этот вариант оч. долго согласовывать, и скорее всего не одобрят, плюс доступы на SQL выпрашивать. И не совсем понятно как объектные сущности в таблице значений разложить в таблице SQL много шаманства по моему будет
(8) формально он у тебя есть - ты же в базу ходишь как-то
>И не совсем понятно как объектные сущности в таблице значений разложить в таблице SQL много шаманства по моему будет
мы не в курсе накой черт тебе 12 лямов записей
(7) Если РЗ=ФЗ, то думал об этом, но к сожалению в моей задаче это не сработает
(15) Передается из одной процедуры выполняемом в серверном общем модуле, в фоновое задание, которое то же на сервере выполняется.
(16) Сериализация-десериализация ТЗ способны устроить кошмар любой системе.
>Как эта ТЗ формируется? Очень сложно?
запускают чего-то типа расчет себестоимости - и привет
тз ->хз. можно еще попробовать хз в строку внутреннюю.
либо тз с хз и в временный файл.
было не так давно в одной отраслевой упп.
модуль проведение документа.
внутри 2-го обход ТЧ и набивание ТЗ построчно.
теперь простая арифметика:
ТЗ = 10 000 000 строк
клиент "думает" до 2 Гб и падает
что делает клиент? звонит во франь, консультант франя заходит по rdp на сервак и (о чудо!) проводит документ. За 10 минут. Сервак 64-бит. А значит 3,5 Гб на клиента скребя ложкой по дну ему хватает.
какой вывод сделал франь?
купите 64-бит сервер 1с.
(17) Запись движений в базу (сколько строк столько и движений) :)
+(33) например, в фоновое передавать только организацию, подразделение и там группу какой-нить номенклатуры, а таблицу оборотов (предположим, что речь про обороты) получать из базы уже там
(33) нет, так как запрос делается по движениям в базе, которые потом либо меняются либо удаляются (после запроса), если один фон начнет менять движения, а второй начнет их только читать перед подготовкой ТЗ, то в итоге они будут работать с разными таблицами значений
(35) Пусть фоны читают ТЗ и ждут, пока все флаги в служебном регистре сведений не установятся в "Можно писать"
(37) управляемый режим давно уже включен, "ДЛЯ ИЗМЕНЕНИЯ" игнорится, наверное можно управляемую блокировку установить, только по таймауту тож легко вылететь
(36) Наверное в регистр в ресурс с типом ХранилищеЗначения проще положить саму табличку, спасиб за идею с регистром, ща буду пробовать
(38) При положении в ХЗ пойдет сериализация в XDTO + кодирование в base64, которое, я боюсь, выполнено через опу..
При извлечении из ХЗ пойдет обратный процесс.
21-й век на дворе, а все возитесь с таблицами значений?
Пригласите специалиста.
(29) нет, зачем франч предложил купить 64бит сервер, если он уже есть? на который консультант зашел через рдп.
(41) Пишется и читается вне транзакции, блокировки вообще не сработают, плюс (если делать в транзакции) большая вероятность таймаута
(31) распараллеливается запись одного набора?
и в наборе 12 миллионов строк?
что-то я не очень понял.
странно что все пытаются придумать что делать с тз, вместо того, чтобы удостовериться в том, что в принципе выбрана правильная методика решения задачи.
(35) разбей данные на не пересекающиеся куски и читай-пиши, сколько угодно и когда угодно
(51) Мне то они не нужны, типовой расчет себестоимости в упп 1.3, по методологии расчета вопрос тож не ко мне, не я ее утверждал.
(40) Да, не получилось в ХранилищеЗначения в рег. сведений поместить, 200 тыс. записей легко прошли сек. за 20, а на 5 млн отвалилось по ошибке потока (
(58) Вы пытаетесь сделать какое-то "еврейское" распараллеливание - думаю, что ничего у Вас не выйдет. Предполагаемая модель для распределения задач по "потокам" - убога и нежизнеспособна.
Вместо того, чтобы пытаться с помощью "молотка и такой-то матери" как-то передать ТЗ "потоку" - гораздо перспективней будет подойти к вопросу с другой стороны.
Проанализировать алгоритм, определить на какие атомарные операции его можно разбить - и уже от этого "плясать"
(59) Угумс. Типы, не имеющие XDTO-сериализации - прекрасно помещаются в хранилище значения. Есть и обратные примеры - когда тип помещается в хранилище, но не имеет XDTO-сериализации.
Исследования методом "научного тыка" показали, что, помещение в хранилище значения - эквивалентно ЗначениеВСтрокуВнутр(), а не XDTO-сериализации.
(60) Все такие умные, что ППЦ.
Ну, разбей хотя бы алгоритм удаления движений по нескольку регистров на поток - заипешься код писать, а потом (самое главное) ошибки ловить. Это примитив.
А мы тут про расчет себестоимости.
(62) Многопоточность это вообще сложно.
(64) И что? Есть типы, которые ее не имеют - но в хранилище помещаются. И наоборот, есть типы которые не помещаются в хранилище, но пишутся в XDTO
(61)
Исследования методом "научного тыка" показали.
(66) Вкратце - перебирались почти все типы встроенного языка (вплоть до ОбъектМетаданных, ИсторияРаботыПользователей, РасширенноеИмяXML - и прочего безобразия).
Для каждого типа делалась попытка:
(68) Масштабно, епта.
Результаты опубликуй.
ТЗ В XDTO есть? Нет? А если найду?!
(69) Если найду - опубликую.
Еще выяснилось, что просто ЗаписатьXML() - который метод глобального контекста - тоже сильно разнится с СериализаторXDTO.ЗаписатьXML().
ТЗ в XDTO есть :) Но это не значит, что помещение в хранилище значения - есть XDTO
(70)
ЗаписатьXML() - который метод глобального контекста - тоже сильно разнится с СериализаторXDTO.ЗаписатьXML().
Подозреваю, что просто ЗаписатьXML() от 8.0 остался, пока XDTO не было.
(0) Я через выгружал в текстовый файл и булками загружал во временную таблицу.
(71) Ты случайно с ЗаписатьXDTO не путаешь?
(68) Дааа, ты чертовски прав..
1) Создал справочник с реквизитом ХЗ.
Простите за кодировку, было лениво морочиться.
(76) Так там не один кусок памяти, а набор объектов (строки ТЗ, колонки) содержащих друг на друга, наверно. Может стоит указать объект СжатиеДанных?
(76) Кстати а как насчет использования ЗаписьFastInfoset
(76) Вот тут я конечно тот еще басист - но что-то мне подсказывает, что если просто сохранять кусок памяти - то его потом будет оооочень сложно прочитать следующей версией, когда в плюсовом классе будет изменен состав членов.
Например, в той же Java - если сделать implements Serializable и не реализовывать свои writeObject и readObject - то именно так и получится. А если реализовывать writeObject и readObject - то это уже не будет "просто кусок памяти".
В которую будут преобразованы двоичные данные :)
(74)
Вы забыли указать степень сжатия при создании хранилища.
(83) protobuf, как и json - всего-лишь форматы. Ничего нового в такое понятие как сериализация они не приносят.
К тому же, даже из вашей ссылки на вики:
> По замыслу разработчиков сначала должна быть описана
> структура данных, которая затем компилируется в классы,
Т.е. опять-таки нет речи о том, чтобы просто взять кусок оперативной памяти и зафигачить в файл (а потом просто прочитать из файла).
На основании объекта класса строится DTO-представление, которое уже и пишется файл.
Не лучше все движения запихнуть во временную таблицу с полем порядковый номер. И забирать уже из временной по условию (от 1 до милиона) - первый поток, от милиона+1 до двух милионов - второй поток и тд.
(86) Для этого всего-лишь нужно умудриться передать в другой сеанс менеджер временных таблиц)
(86) Каким же образом из разных сеансов (фоновых заданий) получать временную таблицу созданную в родительском сеансе?
(82) Я не сомневался в тебе. Сжатие данных скорее всего сожмет эту строку deflate-ом.
(85) Если это массив структур то можно взять и кусок памяти, так как они храняться все в одном непрерывном куске.
Что касается объектов, то они разбросаны по всей памяти.
Таблица типизирована, то можно записывать как в DBF для данных с определенным размером либо указывая размер данных в первых байтах например для строк.
Либо записывать в аналог иерархической бд
(93) Я не соглашусь с тобой.
Имхается мне, что все объекты в 1С - связные списки (даже массивы и структуры) - нет падения скорости вставки на больших объемах.
Мегазадача. а если попробовать как-то получать из разных сеансов адрес ячеек в ОЗУ, занятых исходной таблицей (которую инициировал родительский сеанс)?
(95) Аххх, если бы узнать адреса объектов 1С. мммм, какие возможности, но. Мечты. мечты.Можно создавать локальные и глобальные временные таблицы. Локальные временные таблицы видимы только во время текущего сеанса, а глобальные — во всех сеансах. Временные таблицы не подлежат секционированию.
Инструкции SQL могут обращаться к временной таблице по заданному в инструкции CREATE TABLE значению аргумента table_name, например:
В рамках выполнения проекта столкнулся с интересной задачей ускорения загрузки данных из других информационных баз. Задача загрузки данных предполагала выполнение к внешней базе несвязанных между собой запросов, результаты которых помещаются в одну таблицу значений. Когда на оптимизацию запроса рука уже не поднималась, приступил к ускорению загрузки с помощью распараллеливания процессов. Отмечу, что элементы кода в данном посте приведены для клиент-серверного варианта и укрупнено для общего понимания подхода.
Что у нас в 1с Предприятии 8.2 имеется для распараллеливания & это фоновые задачи. Метод, который будет вызываться в фоновой задаче, должен быть прописан в серверном общем модуле и быть экспортным. Естественно нам понадобиться в фоновую задачу передавать и забирать значения.
Зачем нам в фоновую задачу передавать адрес во временном хранилище. Наша фоновая задача должна куда-то положить результат, причем так чтобы мы знали где его потом взять.
Для того чтобы запустить фоновые задачи выполняется следующий код:
Перед запуском фоновой задачи через ФоновыеЗадания.Выполнить() мы формируем массив параметров. Значения из массива параметров переходят в метод фонового задания в качестве параметров. В МассивЗапущенныхЗаданий хранятся все фоновые задачи, которые мы запустили. Теперь надо подождать их ожидания.
ФоновыеЗадания.ОжидатьЗавершения(МассивЗапущенныхЗаданий);
После того как все задачи были завершены, можем приступить к получению из них данных. Для этого мы проходим по всем адресам в хранилище, которые хранятся в массиве МассивАдресовВХранилище. После получения результата фонового задания перегоняем его в общую таблицу.
Вопрос определения оптимального количества потоков выходит за рамки данного поста. А после получения некоторых результатов на рабочих данных пока что выходит и за рамки моего сознания . Но если у вас есть идеи как посчитать нужное количество потоков, пишите в комментариях, с радостью почитаю.
Читайте также: