1с повторное использование возвращаемых значений. Общие модули

/
Клиент-серверное взаимодействие и вопросы безопасности

Использование модулей с повторным использованием возвращаемых значений

Область применения: управляемое приложение, мобильное приложение, обычное приложение.

1. Общие модули с повторным использованием возвращаемых значений (далее: кэш) предусмотрены для кэширования результатов работы функций, которые в них размещены - на время сеанса или на время вызова . Их следует применять для экономии вычислительных ресурсов сервера и для минимизации клиент-серверного взаимодействия.

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

2. В то же время, чрезмерное (неоправданное) применение общих модулей с повторным использованием возвращаемых значений может приводить к излишнему потреблению памяти.

2.1. Недопустимо создать общие модули с повторным использованием, из которых возвращаются данные, вычисление которых выполняется быстрее, чем получение из кэша. Например, строковые константы. Кроме того, что получение строковой константы каждый раз будет работать гораздо быстрее, чем получение ее из общего модуля с повторным использованием, эти данные будут занимать память кэша.
Например, неправильно размещать в модуле с повторным использованием:

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

2.2. Следует помещать в кэш только такие данные, к которым потом будут часто обращаться.

В частности, следует иметь в виду, что кэш не хранит данные вечно. Закэшированное значение будет удалено из кэша через 20 минут после вычисления или через 6 минут после последнего использования (в зависимости от того, что наступит раньше*). Кроме этого значение будет удалено при нехватке оперативной памяти в рабочем процессе сервера, при перезапуске рабочего процесса и при переключении клиента на другой рабочий процесс. Поэтому если никто "не успел" воспользоваться данными из кэша, то этот ресурс был потрачен зря.

* Примечание: конкретные цифры могут варьироваться в зависимости от используемой версии платформы 1С:Предприятие.

2.3. Диапазон значений входных параметров функций, размещенных в общих модулей с повторным использованием, не должен быть широким.
Например, в конфигурации предусмотрена функция, получающая на вход контрагента. Если контрагентов в базе очень много, а сценарий работы пользователей таков, что вероятность того, что кто-то за 5 минут обратится к этому же контрагенту, очень невысокая, то ресурсы будут потрачены впустую. Кроме того, если эту «трату» умножить на количество одновременно работающих пользователей, то бесполезные расходы ресурсов становятся значительными.

3. Не следует изменять данные, полученные из кэша. В противном случае, возможны скрытые ошибки в работе программы, а также бесполезное расходование памяти (ресурсов кэша). Поэтому в качестве возвращаемых значений рекомендуется использовать значения, состояние которых изменить нельзя, например: ФиксированныйМассив , ФиксированнаяСтруктура .

Это ограничение вызвано тем, что кэш возвращает каждый раз не копию объекта, а ссылку на один и тот же объект в памяти. Например, если в массив, который возвращает функция с повторным использованием, при каждом вызове при проведении документов дописывать новое значение, то в результате кэш очень быстро «распухнет». Кроме того, при очередном сбросе кэша, добавленные значения будут потеряны и код, который на них опирался, будет работать некорректно.

4. Если в модуле с повторным использованием размещено несколько экспортных функций, которые не только вызываются «снаружи», но и вызывают друг друга, то следует иметь в виду, что результат «внутренних» вызовов не кэшируется.
Например, если в модуле ОбменДаннымиПовтИсп размещено две экспортных функции:

Функция АвтономнаяРаботаПоддерживается() Экспорт Возврат . . . КонецФункции Функция ЭтоАвтономноеРабочееМесто() Экспорт Возврат АвтономнаяРаботаПоддерживается() И . . . ; КонецФункции

которые последовательно вызываются из прикладного кода,

ОбменДаннымиПовтИсп.АвтономнаяРаботаПоддерживается(); ... = ОбменДаннымиПовтИсп.ЭтоАвтономноеРабочееМесто();

то функция АвтономнаяРаботаПоддерживается будет вычислена дважды.

Общие модули 1С — объект метаданных конфигурации 1С 8.3 и 8.2, который хранит в себе программный код, который часто вызывается в конфигурации. Функцию/процедуру можно вызвать из любого места конфигурации (если она экспортная).

Как использовать общий модуль

Хороший тон — поместить процедуру или функцию в общий модуль, если она вызывается в более чем одном месте. Во-первых, если процедура корректируется, её надо править только в одном месте. Во-вторых, этим достигается больший порядок в коде.

Типичный пример общего модуля — обработка проведения по какому-то регистру, получение количества разницы рабочих дней, пересчет курсов валют, пересчет количества/цены/суммы в табличной части и другие функции.

Свойства общих модулей

Одно из основных особенностей общих модулей от других модулей — нельзя объявлять общие переменные.

Получите 267 видеоуроков по 1С бесплатно:

Рассмотрим подробнее палитру свойств общего модуля:

  • Глобальный — если флаг установлен, функции и процедуры из этого модуля становятся доступны в глобальном контексте. Т.е. их можно вызвать в любом месте конфигурации, обращаясь без названия общего модуля. Однако добавляется условие — название процедур и функций в этом общем модуле должны быть уникальны в рамках глобального контекста.
  • Сервер — процедуры и функции данного общего модуля могут быть выполнены на сервере.
  • Внешнее соединение — программные коды данного общего модуля могут быть выполнены при подключении внешним источником (например, COM).
  • Клиент (управляемое приложение) — процедуры и функции данного общего модуля могут быть использованы в толстом клиенте в режиме управляемого приложения.
  • Клиент (обычное приложение) — программные коды данного общего модуля могут быть использованы в толстом клиенте в режиме обычного приложения.
  • Вызов сервера — флаг, разрешающий на клиенте использовать процедуры и функции из этого общего модуля.
  • — если установлена Истина, в этом общем модуле будет отключена проверка прав доступа.
  • Повторное использование — определяет настройки возвращаемых значений, если опция включена, то после первого выполнения система запомнит значение для данных входных параметров и будет возвращать уже готовое значение. Может принимать следующие значения: не используется — отключение, на время вызова — на время выполнения определенной процедуры, на время сеанса — пока пользователь не закрыл сеанс (программу).

Если Вы начинаете изучать 1С программирование, рекомендуем наш бесплатный курс (не забудьте

Часто нам приходится по ходу исполнения программы получать значения, которые хранятся в базе данных и не меняются годами. Яркий пример - значения констант. Отчасти, можно причислить сюда же поиск элемента справочника или узла плана обмена по коду, получение значений реквизитов объектов по ссылке.

Не думая, "на лету" такие задачи решаются конструкциями вида:

Если ДатаДокумента > Константы.ДатаНачалаПримененияПостановления1137.Получить() Тогда

В результате, каждый раз когда выполняется этот код - происходит "дерганье" базы данных.

Программисты более щепетильно относящиеся к своему коду, выполняют один запрос к базе данных, кэшируя все данные которые им понадобятся, однако такой подход не всегда приводит к желаемой разгрузке БД:

  1. Цикл выполнения кода может быть неявным (например, групповое перепроведение документов)
  2. Получить строгий набор данных (не больше и не меньше), которые потребуются позже, иногда затруднительно или вовсе невозможно.
  3. Кэшированные значения требуется использовать в разных вызовах/формах

Модуль с повторным использованием возвращаемых значений

Для решения описанных проблем в платформе есть модули с повторным использованием возвращаемых значений. Фактически, это обычный общий модуль (клиентский или серверный), в котором Повторное использование возвращаемых значений установлено в "На время вызова" или "На время сеанса". В самом модуле процедуры и функции описываются как обычно.

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

Возвращаемые значения, естественно, кэшируются в разрезе значений переданных параметров, т.е. при выполнении кода

Узел1 = НашМодуль.ПолучитьУзелОбменаСБухгалтерией("0001"); Узел2 = НашМодуль.ПолучитьУзелОбменаСБухгалтерией("0002");

Оба вызова действительно приведут к выполнению соответствующей процедуры и вернут разные ссылки, а при последующих попытках получить узел с кодом 0001 или 0002 - будут возвращаться соответствующие узлы без вызова процедуры и, как следствие, базы данных.

Значения кэшируются отдельно для каждого сеанса на клиенте или сервере (в зависимости от того клиентский или серверный модуль). Т.е. если есть особенности настройки прав доступа или другой зависимости полученного значения от текущего пользователя - все отработает корректно.

Чего делать нельзя

Есть одно ограничение. В качестве параметров функций можно указывать только простые типы. Неопределено, Null, Булево, Дата, Строка, Число, Ссылка. Никаких структур, таблиц значений, объектов и т.п. Если вы попытаетесь передать в качестве параметра, например, структуру - все отработает, но о повторном использовании полученного значения можете забыть.

Возвращаемое значение при этом может быть любого типа.

Также, стоит обращать внимание на размер данных которые вы кэшируете. Всю память сервера вы забьете вряд ли, но помнить о том, что ресурсы не бесконечны стоит.

Баг или фича от 1С

Интересное свойство есть у повторно используемых значений. Баг это или фича непонятно, но знать о нем не помешет. Если выполнить код следующего характера:

ЗначениеСтруктура1 = НашМодуль.ПолучитьСтруктуруЗначенийРеквизитов(СсылкаНаОбъект); ЗначениеСтруктура1.Наименование = "Новое наименование"; ЗначениеСтруктура2 = НашМодуль.ПолучитьСтруктуруЗначенийРеквизитов(СсылкаНаОбъект);

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

Что делать при изменении закэшированных данных

Есть всего лишь один "легитимный" метод обработать ситуацию с изменением закэшированного значения в базе данных. Это метод ОбновитьПовторноИспользуемыеЗначения(). Будут сброшены значения всех функций по всем параметрам всех модулей. Обновить по конкретным значениям параметров / функциям / модулям нельзя.

Соответственно, из-за такого волюнтаристского подхода, пользоваться этой функцией необходимо крайне осмотрительно: вся система после его использования какое-то время будет работать существенно медленнее.

Посягаем на святое: пишем запрос в цикле

Кроме очевидных вариантов использования функций с повторным использованием возвращаемых значений, есть немало интересных, универсальных, нестандартных подходов, среди которых:

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

Но есть еще один подход, на котором я хочу остановиться подробно. Это использование функции, содержащей вызов БД, с повторным использованием возвращаемого значения в цикле. Т.е. фактически это запрос в цикле. Вообще, нас учили так не делать, но бывают случаи, когда такое построение кода приведет к лучшей производительности, если:

  1. Количество различных значений входящих параметров, которые встретятся внутри цикла небольшое и подавляющее большинство сочетаний с высокой долей вероятности было получено ранее в этом сеансе.
  2. Заранее получить строгий набор сочетаний значений входящих параметров, которые встретятся в цикле затруднительно, а получение значений для всех возможных сочетаний значений входящих параметров приведет к считыванию большого объема данных из БД

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

Печать (Ctrl+P)

Объекты, расположенные в ветви дерева конфигурации Общие модули, предназначены для размещения в них текстов функций и процедур, которые могут вызываться из любого другого модуля конфигурации.
ВНИМАНИЕ! Общий модуль может содержать только определения процедур и функций .
Процедуры и функции общего модуля, для которых в заголовках указано ключевое слово Экспорт, являются одними из составных частей глобального контекста. Подробнее о написании процедур в общем модуле можно узнать в разделах «Формат исходных текстов программных модулей» и «Операторы» справки по встроенному языку.
Для редактирования общего модуля необходимо в палитре свойств объекта типа Общие модули окна Конфигурация в свойстве Модуль щелкнуть мышью ссылку Открыть. Текст общего модуля будет выдан для редактирования в редакторе текстов системы «1С:Предприятие» в режиме редактирования текста программного модуля.
Общий модуль, являясь частью конфигурации, сохраняется только в составе конфигурации.
Свойство Глобальный определяет, являются ли экспортируемые методы общего модуля частью глобального контекста.
Если свойство Глобальный установлено в значение Истина, то экспортируемые методы общего модуля доступны как методы глобального контекста.
Если свойство Глобальный установлено в значение Ложь, то в глобальном контексте создается свойство с именем, соответствующим имени общего модуля в метаданных. Данное свойство доступно только для чтения. Значением данного свойства является объект ОбщийМодуль . Через данный объект доступны экспортируемые методы данного общего модуля. Таким образом, обращение к методам неглобальных общих модулей выглядит как XXXXX.YYYYY, где XXXXX – это имя свойства, соответствующее контексту общего модуля, а YYYYY – имя экспортируемого метода общего модуля.
Пример:

РаботаСТорговымОборудованием.ПодключитьСканерШтрихкодов() ;

Различные контекст и общие модули

С помощью свойств общих модулей и инструкций препроцессора можно организовать выполнение различных методов общих модулей в нужном контексте.
Каждое свойство общего модуля отвечает за возможность компиляции (и исполнения) общего модуля в том или ином контексте.
Доступны следующие свойства, отвечающие за контекст, в котором доступны методы общего модуля:
Клиент (обычное приложение) – методы общего модуля будут доступны для толстого клиента в режиме обычного приложения;
● – методы общего модуля будут доступны для тонкого клиента, веб-клиента, а также для толстого клиента в
режиме управляемого приложения;
● Сервер – методы общего модуля будут доступны на сервере;
Внешнее соединение – методы общего модуля будут доступны во внешнем соединении.
Если устанавливаются одновременно несколько свойств, то это означает, что методы общего модуля будут доступны в нескольких контекстах.
Если у общего модуля установлено свойство Сервер и еще какое-либо свойство, то это означает, что общий модуль будет доступен одновременно на сервере и в выбранном клиенте. При этом необходимо понимать, что фактически это будет несколько вариантов скомпилированного кода (по числу выбранных клиентов и собственно для сервера).
При этом если метод, расположенный в таком общем модуле, вызывается со стороны клиента, то будет использована клиентская копия общего модуля, а если с сервера – серверная. В этом случае с помощью директив препроцессора (подробнее см. здесь) можно «оградить» сервер от того кода, который на нем не может исполняться.
Рассмотрим пример. В общем модуле (который может исполняться на тонком клиенте и на сервере) есть метод, который имеет несколько различное поведение на стороне тонкого клиента и на стороне сервера. Посмотрим, как это можно сделать:



#Если ТонкийКлиент Тогда
// Покажем предупреждение
ПоказатьОповещениеПользователя (“На клиенте”);
#КонецЕсли
КонецПроцедуры
Тогда на стороне сервера код приобретет следующий вид:
Процедура МетодОбщегоМодуля() Экспорт
// Тут размещается различный важный код
КонецПроцедуры
А на стороне тонкого клиента код будет иметь следующий вид:
Процедура МетодОбщегоМодуля() Экспорт
// Тут размещается различный важный код
// Покажем предупреждение
ПоказатьОповещениеПользователя(“На клиенте”);
КонецПроцедуры

Для передачи управления с клиента на сервер существует несколько способов:
● вызвать метод серверного общего модуля;
● в модуле формы или команды вызвать метод, который предваряется директивами компиляции &НаСервере, &НаСервереБезКонтекста

При этом из серверных процедур невозможно вызвать методы клиентских общих модулей (у которых не установлено свойство Сервер) и клиентские методы модуля формы или модуля команды. Управление вернется на клиента после того, как будет завершен самый внешний вызов серверного метода.
Исключение составляют методы модуля формы и модуля команды, которые предваряются директивами компиляции &НаКлиентеНаСервере , &НаКлиентеНаСервереБезКонтекста
Также следует упомянуть следующие моменты:
● Если общий модуль доступен более чем для одного клиента, то при написании программного кода следует учитывать максимальные ограничения, которые могут накладываться клиентами, либо использовать инструкции препроцессора для «изоляции» кода, специфичного для того или иного клиента.
● Инструкции препроцессора также имеют смысл тогда, когда один общий модуль имеет несколько контекстов исполнения, например, внешнее соединение и тонкий клиент или (что встречается значительно чаще) какой-либо клиент и сервер. В этом случае инструкции препроцессора будут обрамлять интерактивный код, который невозможно использовать на сервере, но возможно на клиенте (см. пример выше).
Подробнее об инструкциях препроцессора и директивах компиляции в разделе «Исполнение процедур и функций» справки по встроенному языку.
Свойство Вызов сервера предназначено для управления возможностью вызова экспортируемых методов серверного общего модуля из клиентского кода.
Если свойство установлено, то экспортируемые методы серверного общего модуля доступны для вызова со стороны клиента. Если свойство не установлено, то такие экспортируемые методы можно вызывать только из серверных методов (как методов серверных общих модулей, так и серверных методов модуля формы и модулей команд).
Совет . Рекомендуется устанавливать в значение Ложь свойство Вызов сервера в тех случаях, когда серверный общий модуль содержит методы, которые нежелательно вызывать с клиента (например, по причинам безопасности).
Примечание . Если одновременно установлены свойства Клиент (обычное приложение) , Клиент (управляемое приложение) , Внешнее соединение , то свойство Вызов сервера автоматически сбрасывается. Если устанавливается свойство Вызов сервера , то автоматически сбрасываются свойства Клиент (обычное приложение) , Клиент (управляемое приложение) и Внешнее соединение , если эти свойства были установлены одновременно.
Свойство Привилегированный предназначено для отключения контроля прав доступа при выполнении методов общего модуля.
ПРИМЕЧАНИЕ. Если свойство Привилегированный установлено, то общему модулю автоматически устанавливается свойство Сервер и сбрасываются остальные свойства (Клиент (обычное приложение) , Клиент (управляемое приложение) и Внешнее соединение ). Привилегированный общий модуль может исполняться только на сервере.

Повторное использование возвращаемых значений

Если общий модуль не является глобальным, то становится доступно свойство Повторное использование возвращаемых значений. Это свойство может принимать следующие значения:
● Не использовать – повторное использование возвращаемых значений для функций этого общего модуля не используется.
● На время вызова и На время сеанса – для общего модуля используется метод определения повторного использования данных. Суть этого метода заключается в том, что в ходе выполнения кода система запоминает параметры и результат работы функций после первого вызова функции. При повторном вызове функции с такими же параметрами, происходит возврат запомненного значения (из первого вызова) без выполнения самой функции. Если функция во время своего выполнения меняет значения параметров, то повторный вызов функции не будет это делать.
Можно выделить следующие особенности сохранения результатов вызова:
● если функция выполняется на сервере и вызывается из серверного кода, то значения параметров и результат вызова запоминаются для текущего сеанса на стороне сервера;
● если функция выполняется на толстом или тонком клиенте, то значения параметров и результатов вызова запоминаются на стороне клиента;
● если функция выполняется на стороне сервера, а вызывается из клиентского кода, то значения параметров вызова запоминаются и на стороне клиента, и на стороне сервера (для текущего сеанса).
Сохраненные значения удаляются:
● если свойство установлено в значение На время вызова:
● на стороне сервера – при возврате управления с сервера;
● на стороне клиента – при завершении работы процедуры или функции встроенного языка верхнего уровня (вызванной системой из интерфейса, а не из другой процедуры или функции встроенного языка);
● если свойство общего модуля установлено в значение На время сеанса:
● на стороне сервера – при окончании сеанса;
● на стороне клиента – при закрытии клиентского приложения.
Сохраненные значения будут удалены:
● на сервере, в толстом клиенте, во внешнем соединении, в тонком клиенте и в веб-клиенте с обычной скоростью соединения – через 20 минут после вычисления сохраняемого значения или через 6 минут после последнего использования;
● в тонком клиенте и веб-клиенте с низкой скоростью соединения – через 20 минут после вычисления сохраняемого значения;
● при нехватке оперативной памяти в рабочем процессе сервера;
● при перезапуске рабочего процесса;
● при переключении клиента на другой рабочий процесс.
После удаления значений вызов экспортируемой функции выполняется как при первом вызове.
На выполнение процедур данное свойство общих модулей не влияет – процедуры выполняются всегда.

Если у общего модуля установлено повторное использование возвращаемых значений, то на типы параметров экспортируемых функции накладывается ряд ограничений. Типы параметров могут быть только:
● Примитивными типами (Неопределено, NULL, Булево, Число, Строка, Дата ).
● Любыми ссылками на объекты базы данных.
● Структурами со значениями свойств вышеперечисленных типов. В этом случае идентичность параметров контролируется «по содержимому» структур.
Если экспортируемая функция возвращает какой-либо объект, то фактически возвращается ссылка на объект, хранимый в кеше. Если после получения этой ссылки произойдет изменение состояния объекта, то последующий вызов той же самой функции приведет к возврату ссылки на уже измененный объект без фактического выполнения функции. Такое поведение будет наблюдаться до удаления сохраненного значения (по любой причине). Другими словами – изменение состояния объекта, полученного в результате вызова функции из общего модуля с повторным использованием возвращаемых значений, не является основанием для фактического вызова функции. Также следует помнить, что кеш возвращаемых объектов индифферентен к
состоянию привелигированного режима в момент вызова функции с повторным использованием возвращаемых значений. Эта особенность может привести к следующей особенности поведения:
● Фактическое выполнение вызова функции с повторным использованием возвращаемых значений (первый вызов) выполнялось при включенном привелигированном режиме.
● При выполнении функции был получен объект, который не может быть получен с отключенным привелигированным режимом.
● Последующие вызовы функции выполнялись без установки привелигированного режима.
● Однако, до момента очистки кеша возвращаемых объектов или повторного фактического вызова, функция будет возвращать формально недоступный объект.
● Также верно и обратное поведение, когда первый вызов выполняется без установки привелигированного режима, а в привелигированном режиме не возвращается объект, который мог быть получен в привелигированном режиме.

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