gifts2017

Данные формы и оптимизация обмена с сервером

Опубликовал Яков Коган (Yashazz) в раздел Программирование - Практика программирования

Почему может сильно тормозить форма, где есть "ДанныеФормыКоллекция", "ДанныеФормыДерево" и им подобные, и как этого избежать.

"ДанныеФормы..." Синтакс-помощник не балует нас избыточной информацией, сообщая лишь, что объект этого рода, в общем случае, "предназначен для моделирования коллекций в данных управляемой формы" и "может использоваться в реквизитах управляемой формы". Для "ДанныеФормыКоллекция", по идее, моделируется таблица значений, для "ДанныеФормыДерево" - дерево значений, и так далее. Общеизвестно, на ИТС и в статьях разжёвано, что эти объекты живут на клиенте с целью адекватно отобразить, в т.ч. в связке с элементами УФ, те или иные данные; а также, что они доступны и на сервере как минимум при передаче всего контекста формы. Моделировать можно коллекции прикладных объектов из БД, а можно и свои, временные, созданные прямо в форме для служебных и рабочих надобностей. Вот о них и пойдёт речь - о моделях коллекций, являющихся реквизитами управляемой формы и отражающих некие не единичные объёмы данных.

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

Сделаем внешнюю обработку, в ней форму, и для этой формы создадим два реквизита - с типом "Таблица значений" и с типом "Дерево значений" (разумеется, они покажутся в скобках как несуществующие на клиенте). Создадим по 4 реквизита-колонки к каждому из этих, дадим им типы - строковый (250), число (15.2), дата (дата-и-время), булево. Желающие могут поэкспериментировать со ставшими возможными в 8.3 "произвольными", а также с вложением всяких других моделей коллекций типа "матрёшка", но мы будем работать с этими простыми данными, чья возможность сериализации штатным образом очевидна. На интерфейсе, в элементах формы, ничего делать не станем.

Заполним таблицу значений - сначала по 1000 строк, потом будем пробовать больше. Дерево значений, а точнее, моделирующая его коллекция, понадобится как "гиря", сразу дающая разницу в объёмах данных на порядки. Строковую колонку везде забьём мусором под все 250 символов.

Итак, что нам интересно? Допустим, мы делаем внешнюю универсальную обработку, в чьих реквизитах формы никак не задействованы сущности БД, но по ряду причин очень нужно использовать большие модели коллекций. Например, это обзор файлов на клиентском ПК - дерево каталогов и некая таблица значений нужных юзверю файлов. Главное, что нам совершенно незачем обрабатывать все данные на сервере, мы добровольно отказались от индексации таблицы значений и вообще хотим в первую очередь оптимизировать передачу данных между клиентом и сервером. Подчеркну - дерево большое, таблица значений сравнительно маленькая.

Исходная задача - всего лишь по каким-то нашим нуждам поработать с таблицей значений, "вынимаемой" из её модели. Да, можно возразить, что мол "чего тебе не хватает" - и сортировка есть, и поиск, и обход колонок можно смоделировать - но вот допустим, для эксперимента. Я, может, хочу СКД-образную выборку запилить по таблице, с ветками "И"-"ИЛИ", по заданной юзверем логике.

Вроде бы это просто - вызвать контекстную серверную процедуру и там выполнить "ДанныеФормыВЗначение", вуаля. Но здесь и начинаются подводные камни, потому как вместе с безусловно нужной нам моделью таблицы значений на сервер в рамках контекста тащится и огроменная "ДанныеФормыДерево", которая вообще там не нужна.

К сожалению, разработчики платформы не дали нам возможности избирательно управлять передачей контекста формы между клиентом и сервером. Флаг "Использовать всегда" для реквизитов этих моделей вообще не играет роли, а другого и нет, и в результате вызов сервера превращается в тормоза и недопустимо высокий объём отправляемых данных.

Очевидно, что вызов должен быть внеконтекстным. Но тогда коллекции недоступны, и их надо как-то в целевую серверную процедуру передать. Тут начинается разница - в 8.2 можно передать "ДанныеФормы" как аргумент на сервер, их передача происходит без ошибок и без потерь, но на ИТС читаем, что это очень некомильфо, что в принципе логично, т.к. кто его знает, как оно там размещается, где в памяти болтается, куда на сервере приложений девается и как себя поведёт при изменении без указания "Знач".

Всякие "ХранилищеОбщихНастроек.Сохранить" или "ЗначениеВСтрокуВнутр" неприменимы на клиенте и отпадают.

Казалось бы, используем "ПоместитьВоВременноеХранилище" (естественно, с соблюдением и пониманием, какие ключи нужны и сколько времени оно проживёт - ну, нам на один вызов, и хватит). Тем более, что в СП никаких ограничений на этот счёт не указано, а значит, находясь в форме, вполне можно поработать с временным хранилищем. И в 8.2 это удаётся без проблем, и мы получаем вожделенный результат. Сделали внеконтекстно вызываемую процедуру, на клиенте помещаем модель-коллекцию в хранилище, передаём адрес как аргумент этой процедуре, а в ней, на сервере, получаем из хранилища. Все счастливы, всё работает, объём данных минимален благодаря тому, что мы сами решаем, какие объекты помещать и передавать - т.е. только и исключительно нужную нам таблицу, а никакое не дерево.

Но в 8.3 нас ждёт неприятный сюрприз: 8.3.4 просто заявляет, что подобные объекты "не могут быть помещены в хранилище", а 8.3.5 громко падает с ошибкой. Скромно осмелюсь полагать, что это просто временная недоработка писателей платформы, ибо если данные можно было передавать в 8.2, то что мешает повторить достигнутое? Поэтому надеюсь, что примерно к 8.3.10 это аккуратно доделают, а не оставят как сейчас и не объявят "фичей", подогнав заумное обоснование.

Что делать? А делать сериализацию своими силами. Это даже лучше, всё под нашим контролем. Приведу кусок кода с контекстным и внеконтекстным вызовами, которые дали мне статистику.

&НаСервереБезКонтекста
Процедура Сгонять_СерверБезКонтекста(стро)
   
рЧтениеХМЛ=Новый ЧтениеXML;
   
рЧтениеХМЛ.УстановитьСтроку(стро);
   
рТаблицаЗначенийС=СериализаторXDTO.ПрочитатьXML(рЧтениеХМЛ);
   
Сообщить("К-во табзнач: "+рТаблицаЗначенийС.Количество());
КонецПроцедуры

&НаКлиенте
Процедура СгонятьБезКонтекста(Команда)
   
рЗаписьХМЛ=Новый ЗаписьXML;
   
рЗаписьХМЛ.УстановитьСтроку();
   
СериализаторXDTO.ЗаписатьXML(рЗаписьХМЛ,рТаблицаЗначений);
   
Сгонять_СерверБезКонтекста(рЗаписьХМЛ.Закрыть());
КонецПроцедуры


&НаСервере
Процедура Сгонять_Сервер()
   
Сообщить("К-во табзнач: "+рТаблицаЗначений.Количество());
КонецПроцедуры

&НаКлиенте
Процедура Сгонять(Команда)
   
Сгонять_Сервер();
КонецПроцедуры


В результате получаем адекватную передачу "ДанныеФормыКоллекция" на сервер, и опять же, избирательно, что экономит нам объёмы и время. Причём это именно полноценный объект, с которым вполне можно работать, хотя мы даже не указали сериализатору тип значения, читаемого из xml, он сам догадался.

Поставив ряд экспериментов, можно убедиться, что при прочих равных передача контекста всей формы это не худший, а порой и оптимальный вариант, но только если а) в ней нет чего-то тяжёлого и/или б) всё или почти всё реально нужно на сервере. В случаях же, когда на форме преизрядно разных данных, и среди них объёмистые модели коллекций, проще использовать сериализацию только нужных данных и их передачу во внеконтекстные серверные процедуры. В таблице на приложенном рисунке приведены результаты экспериментов - вызовов вышеуказанного кода - и краткие пояснения. Извиняюсь, что не включаю таблицу в текст статьи - но html-редактора ИС в очередной раз не превозмог (((

Зелёным цветом показаны успешные варианты, когда мы сэкономили, а красным - когда наоборот)

Заметим, что объём передаваемых данных в нашем случае порождён обычной строкой, куда свернулся xml, а значит, её тоже можно чем-нибудь "ужать", и вопрос, как всегда, во времязатратах относительно выигрыша по размеру.

Также заметим, что в 8.2 объём принятых данных не меняется, тогда как в 8.3 при нашем способе принимается столько же, сколько передавалось. Не берусь объяснить сей эффект - надо больше знать о "глубинах".

Однозначного рецепта нет, решать приходится по ситуации, но способ максимально облегчить обмен, поработать на сервере только с нужными моделями коллекций, и взятыми из них реальными коллекциями, у нас есть. Успеха!


См. также

Подписаться Добавить вознаграждение

Комментарии

1. Яков Коган (Yashazz) 29.09.14 22:43
Да, и понятное дело, что мы на сервер кидаем копию, "слепок" с данных формы, а они сами тихо лежат на клиенте, и если их надо изменить-обработать, то с сервера потом придётся тащщить обратно той же сериализацией и на клиенте обновлять, например, через КопироватьДанныеФормы(Десериализованное,СвоёМестное).

Если я написал баян, просьба кинуть тапком, т.е. ссылкой. Самому слабо верится, что эти моменты ранее не разбирали.
2. BabySG (BabySG) 01.10.14 19:22
(0)
Но в 8.3 нас ждёт неприятный сюрприз: 8.3.4 просто заявляет, что подобные объекты "не могут быть помещены в хранилище", а 8.3.5 громко падает с ошибкой. Скромно осмелюсь полагать, что это просто временная недоработка писателей платформы, ибо если данные можно было передавать в 8.2, то что мешает повторить достигнутое? Поэтому надеюсь, что примерно к 8.3.10 это аккуратно доделают, а не оставят как сейчас и не объявят "фичей", подогнав заумное обоснование.

Смысл в том, что в 8.2 ОШИБОЧНО разрешалось запихивать подобные данные в хранилище. В 8.2 (ранних версиях) туда можно было еще и COM объект запихнуть.
А дальше удивлялись - а куда он делся и почему COM не работает :)
Еще есть вариация, когда возвращалось НЕОПРЕДЕЛЕНО (Кстати, разработчики типовых тоже наступили на эти грабли)
А еще есть ответ от разработчиков, который датируется 2011 годом. Но, как я помню. Вы не участвуете тут https://partners.v8.1c.ru/forum/message/886492#m_886492, поэтому цитата:
Во временном хранилище могут хранится значения помеченные в синтакс помощнике как "Сериализуется".

Что, кстати, также описаное в СП для метода ПоместитьВоВременноеХранилище
Описание:
Сохраняет сериализуемое значение во временное хранилище.
3. Яков Коган (Yashazz) 01.10.14 19:58
(2) ОК, была мысль и что "ошибочно разрешили", недопилив концепт, согласен; но почему штатный сериализатор легко справляется без потерь и проблем? Почему нельзя было это же сделать для временного хранилища?

И кстати, какой криминал в сериализации некоей модели данных, коей являются ДанныеФормы, если там в её реквизитах не болтается ничего предосудительного, а всё сплошь простые типы или ссылки? Неужели это так преступно и жутко?
4. BabySG (BabySG) 02.10.14 08:24
(3) Смысл в том, что данные не "сериализируются" в этом случае, а именно модифицируются.
Отдаленно можно сравнить с тем, что СправочникОбъект нельзя передать на сервер с клиента (и наоборот), хотя платформа сама-то как-то "понимает", что делать при записи из формы.
PS. Тут еще такой момент: данные сеанса (а именно в них хранится временное хранилище) могут мигрировать между сервера кластера, и совершенно не факт, что поднятые данные формы будут иметь отношения к какой-либо форме - ее может не быть на другом сервере (например, форма пересоздалась после падения процесса на сервере). Эта же прчина мешает запихать в ВХ COM объект - что делать, если на сервере нет контекста подключения или вообще попали на сервер с Linux?

PPS. А сама задача предварительной обработки большого массива данных через данные формы - крайне не рекомендуется разработчиками платформы. Крайне негативно влияет на оптимизацию передачи данных (что и видно из статьи). Рекомендуют делать небольшую подготовку и отправлять на сервер для полной обработки. Конечно, не всегда есть возможность отпраить туду все, поэтому приходится в каждом случае придумывать свое решение. Универсального нет (вот у нас есть алгоритм, кторый работает по трем веткам, все зависит от объема данных)
5. Яков Коган (Yashazz) 02.10.14 10:28
(4) По идее - целиком согласен. Я ведь и в комментарии приписал, что хочу манипулировать со слепком, с мёртвой копией, которая вообще уже оторвана от формы, контекста, исходника, и никакой логической связи ни с чем не держит. Просто некая коллекция. Если мне понадобится "обратная связь", буду сам прыгать, по каким-то идентификаторам совмещать.
И хранилище тут можно лепить привязанное куда угодно, согласен.
Насчёт COM - тоже абсолютно согласен, очевидные вещи.

Дык в том и фишка, что я хочу кидать небольшой объём и не тащить контекст. Согласитесь, могут, ещё как могут быть огромные данные, чисто по логике бизнес-задачи, пришедшие с клиента. Тот же COM, или ещё что, и не всегда можно выкрутиться.

Кстати, до кучи: почему у обычного "ДеревоЗначений" нету метода "Скопировать", не знаете случайно? Только передача по ссылке и фиг сделаешь независимую копию, кроме как построчно...
6. BabySG (BabySG) 02.10.14 11:33
(5) В том случае, если нужно что-то передать с клиента на сервер (например, тот же файл) - то, как раз, его и передаем через хранилище.
Как вариант, мы можем его обработать на клиенте и создать из оригинального новый уменьшеный и его уже передать на сервер. В этом случае данные формы мы не трогаем, гоняем только то, что сами выбрали.
Но нужно понимать - в каждом конкретном случае, для каждой задачи: свое решение. Тут не получится сделать универсально (данные формы, по сути, и есть универсально, но не будет работать на больших объемах)
7. Яков Коган (Yashazz) 02.10.14 13:02
(6) Именно. И задача иногда бывает именно требующая решения тех проблем, о которых я пишу. Например, не можем сразу забросить файл - юзверь хочет сначала посмотреть его содержимое в виде красиво оформленного интерфейса, отметить флажками нужное к обработке, и уж тогда айда на сервер. И бывает, что некоторые операции с клиентскими ипостасями этих интерфейсных объектов требуют втихую некоторым объёмом поработать на сервере.
8. Сергей Галюк (dj_serega) 02.10.14 15:15
А если протестить две ТЗ и два ДЗ где будет выигрыш?
9. Яков Коган (Yashazz) 02.10.14 15:18
(8) Смотря какого они размера. Тут фишка не столько в типе объекта, сколько в его наполнении. Но по моим ощущениям древовидные объекты сериализуются медленнее. В моём примере дерево просто служило эталонной тяжестью, могло ведь быть и строго наоборот - крохотное деревцо и огромная таблица значений.
10. Сергей Галюк (dj_serega) 02.10.14 15:39
(9) Yashazz, если я правильно понял то:
есть 2 ТЗ + 2 ДЗ (по 1000) то один из объектов лучше контекстом обрабатывать?
11. Яков Коган (Yashazz) 02.10.14 16:48
(10) Если надо обработать 1 из 4 - лучше внеконтекстно. Если 3 из 4 или все - лучше контекстно. Нет однозначного рецепта, всё зависит от наполнения - может, там в ячейках нескольких строк прячутся свои монстры - всякие подтаблицы да массивы, да структуры, да ещё картинки...
12. doxflow (bonv) 02.10.14 18:08
&НаКлиенте
Процедура СгонятьБезКонтекста(Команда)
    рЗаписьХМЛ=Новый ЗаписьXML;


В веб-клиенте это не будет работать.
13. Яков Коган (Yashazz) 02.10.14 18:29
(12) Да. Есть такая печаль. Там останется только моделировать это всякими костылями вроде массивов структур и прочая.
14. John Smith (PiccaHut001) 07.10.14 18:39
(0) "Поэтому надеюсь, что примерно к 8.3.10 это аккуратно доделают, а не оставят как сейчас и не объявят "фичей", подогнав заумное обоснование." - зря надеетесь, 1с так делают ВСЕГДА
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа