При разработке сложных обработок в 1С часто возникает задача сохранения состояния между вызовами сервера. Чтобы не изобретать велосипед каждый раз, можно использовать готовый шаблон дополнительной обработки с сохранением параметров. Например, первый вызов рассчитывает большие таблицы значений, а последующие вызовы (по нажатию кнопок пользователем) должны работать с этими данными. Передавать огромные объемы данных на клиент и обратно — плохая практика, которая "вешает" интерфейс и нагружает сеть. Чтобы избежать зависаний, полезно реализовать индикацию прогресса выполнения фонового задания на управляемой форме. Рассмотрим детально, как использовать механизм временного хранилища для решения этой задачи, разберем внутреннюю механику и вопросы производительности.
Рассмотрим ситуацию: у нас есть несколько ТаблицаЗначений объемом по несколько тысяч строк. Они формируются один раз и используются в дальнейшем. Основной инструмент для этого в 1С — функции работы с временным хранилищем. Подобный механизм часто применяется в прикладных задачах, например, когда выполняется тестирование запросов к Ветис с сохранением параметров в хранилище.
Встают закономерные вопросы:
Для начала разберем базовый алгоритм использования. Мы не храним сами данные на клиенте, мы храним только адрес (строку-ссылку), по которому эти данные лежат на сервере.
Типовой паттерн работы выглядит следующим образом:
Посмотрим на пример реализации кода:
// Серверная функция для первоначального расчета
&НаСервере
Функция РасчитатьИПоместитьДанныеНаСервере()
ТЗ1 = ПосчитатьТЗ1НаСервере(); // Формируем таблицу 1
ТЗ2 = ПосчитатьТЗ2НаСервере(); // Формируем таблицу 2
// Упаковываем в структуру (один из вариантов)
Данные = Новый Структура("ТЗ1, ТЗ2", ТЗ1, ТЗ2);
// Помещаем в хранилище, привязывая к UUID формы
Адрес = ПоместитьВоВременноеХранилище(Данные, УникальныйИдентификатор);
Возврат Адрес;
КонецФункции
// Серверная процедура для работы с данными
&НаСервере
Процедура ОбработатьДанныеПоКнопке(Адрес)
// Получаем данные обратно
// В этот момент происходит десериализация
Данные = ПолучитьИзВременногоХранилища(Адрес);
// Работаем с копией данных
Данные.ТЗ1.Свернуть("Колонка1", "Колонка2");
// ВАЖНО: Если мы изменили данные и хотим сохранить изменения
// для следующих вызовов, их нужно поместить обратно!
// Если этого не сделать, в хранилище останется старая версия.
ПоместитьВоВременноеХранилище(Данные, Адрес);
КонецПроцедуры
// Клиентский код
&НаКлиенте
Процедура КомандаРасчет(Команда)
// Сохраняем адрес в реквизит формы (строка)
АдресХранилища = РасчитатьИПоместитьДанныеНаСервере();
КонецПроцедуры
&НаКлиенте
Процедура КомандаОбработка(Команда)
ОбработатьДанныеПоКнопке(АдресХранилища);
КонецПроцедуры
Давайте проанализируем, что физически происходит при вызове ПолучитьИзВременногоХранилища и почему это может влиять на производительность.
1. Физическое хранение и Сериализация
Временное хранилище — это не просто область в оперативной памяти. Это механизм, управляемый Сервисом сеансовых данных кластера 1С.
Когда вы вызываете ПоместитьВоВременноеХранилище, происходит сериализация данных. Таблица значений превращается в поток байтов. Если вы работаете с текстовыми данными, иногда может быть полезно дополнительное сжатие строк внутренними методами 1С для оптимизации передачи.
Физически эти данные могут находиться:
srvinfo), если объем данных велик или оперативной памяти не хватает.2. Цена операции "Получить"
Операция ПолучитьИзВременногоХранилища выполняет обратный процесс — десериализацию. Система читает байты и восстанавливает из них объект (например, ТаблицаЗначений) в памяти текущего серверного процесса.
Именно процесс сериализации/десериализации является основным потребителем ресурсов CPU. Чтение с диска (если данные ушли в своп) также добавляет задержки.
3. Кэширование
В платформе существует оптимизация. Данные могут кэшироваться в памяти рабочего процесса (rphost). Однако при выполнении тяжелых операций в несколько потоков, когда задействован конвейер обработки задач, данные могут перемещаться между процессами, и нагрузка на сервис сеансовых данных возрастает.
Разберем один из ключевых вопросов: как лучше хранить несколько таблиц?
Вариант А: Одна большая Структура
Вы помещаете Структура("ТЗ1, ТЗ2, ТЗ3", ...) по одному адресу.
Плюсы: Нужно хранить только один адрес.
Минусы: При вызове ПолучитьИзВременногоХранилища платформа десериализует всю структуру целиком. Даже если вам нужна только маленькая ТЗ1, система потратит ресурсы на восстановление огромных ТЗ2 и ТЗ3. Это лишняя нагрузка на память и процессор.
Вариант Б: Отдельные адреса для каждой ТЗ
Вы помещаете каждую таблицу отдельно и храните на клиенте структуру адресов или несколько реквизитов формы: АдресТЗ1, АдресТЗ2.
Плюсы: Вы получаете из хранилища только то, что нужно в конкретной процедуре.
Минусы: Чуть сложнее управлять адресами.
Рекомендация: Если таблицы большие (тысячи строк) и используются в разных сценариях независимо друг от друга, лучше хранить их по отдельности. Это существенно сэкономит ресурсы при частичных обращениях.
1. Изменяемость данных
Частая ошибка начинающих: изменение полученной таблицы без помещения обратно.
Функция ПолучитьИзВременногоХранилища возвращает копию данных. Если вы изменили таблицу (удалили строки, свернули), в хранилище останется старая версия. Чтобы сохранить изменения, нужно снова вызвать ПоместитьВоВременноеХранилище по тому же адресу.
2. Время жизни данных
Если вы привязали данные к УникальныйИдентификатор формы, они очистятся автоматически при закрытии формы.
Однако, если данные используются для длительных вычислений, лучше задействовать фоновые задания с отслеживанием результата выполнения без БСП, чтобы не блокировать интерфейс и иметь возможность получить результат по адресу хранилища после завершения задачи.
3. Альтернатива: Регистр сведений
Если объем данных исчисляется сотнями тысяч строк, или требуется сложная выборка (соединения, фильтры как в запросах), временное хранилище — не лучший выбор. В таких случаях целесообразнее использовать временный (или служебный) Регистр сведений.
В регистр добавляется измерение КлючСессии (GUID), и данные пишутся туда.
Преимущества регистра:
ПолучитьИзВременногоХранилища вполне оправдано и работает быстро.