Как работает кэширование "Повторное использование возвращаемых значений: На время вызова" в 1С и когда оно очищается?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

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

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

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

Рассмотрим, как платформа 1С обрабатывает кэширование в режиме "На время вызова":

  1. Место хранения кэша: Кэш может быть сформирован и храниться как на стороне клиента, так и на стороне сервера. Это ключевой момент, определяющий поведение системы.
  2. Процесс получения значения: Когда клиентский код обращается к экспортной функции модуля с установленным свойством НаВремяВызова, происходит следующий порядок поиска значения:
    1. Сначала платформа ищет значение в клиентском кэше. Если оно найдено (то есть, функция уже вызывалась с такими же параметрами на клиенте в рамках текущего контекста), то сохраненное значение немедленно возвращается, и серверный вызов не происходит. Это позволяет значительно сократить клиент-серверный трафик и время отклика.
    2. Если значение не найдено в клиентском кэше, выполняется вызов сервера. Затем платформа ищет значение в серверном кэше. Если оно присутствует там, то сохраненное значение возвращается клиенту. В этом случае серверные вычисления не производятся, но клиент-серверный вызов все же происходит.
    3. Если значение не найдено ни в клиентском, ни в серверном кэше, только тогда выполняется тело метода (функции) на сервере. Результат выполнения помещается в серверный кэш, передается на клиент, помещается в клиентский кэш и затем возвращается в то место клиентского кода, откуда был произведен вызов.

Таким образом, мы видим, что кэширование происходит многоуровнево, что позволяет максимально эффективно использовать сохраненные значения, минимизируя как вычисления, так и сетевой трафик.

Когда происходит очистка кэша?

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

  1. Очистка на стороне сервера: Сохраненные значения удаляются с сервера при возврате управления с сервера клиенту. Это означает, что как только сервер завершает обработку запроса от клиента (например, после выполнения серверной процедуры или функции, вызванной с клиента) и возвращает управление обратно на клиентскую сторону, весь кэш, сформированный в режиме НаВремяВызова для данного серверного вызова, очищается. То есть, кэш на сервере "живет" ровно столько, сколько длится один серверный вызов, и не сохраняется между разными серверными вызовами, инициированными с клиента.
  2. Очистка на стороне клиента: На клиентской стороне кэш удаляется при завершении работы процедуры или функции встроенного языка верхнего уровня. Что это значит? Это относится к процедурам и функциям, которые были вызваны системой из интерфейса (например, по нажатию кнопки, открытию формы, срабатыванию обработчика события), а не из другой процедуры или функции встроенного языка внутри вашего кода. Проще говоря, клиентский кэш сохраняется в рамках одного "пользовательского действия" или "сценария", инициированного интерфейсом. Как только этот сценарий завершается (например, закрывается форма, завершается выполнение обработчика команды), клиентский кэш очищается.

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

Преимущества использования кэширования "На время вызова"

Применение свойства ПовторноеИспользованиеВозвращаемыхЗначений в режиме НаВремяВызова при правильном подходе дает существенные выгоды:

  1. Сокращение числа обращений к базе данных: Для часто запрашиваемых, но редко изменяющихся данных (например, константы, реквизиты настроек, элементы справочников), кэширование позволяет получить информацию из памяти, минуя дорогостоящие запросы к СУБД. Это значительно снижает нагрузку на сервер базы данных.
  2. Снижение нагрузки на сервер приложений: Уменьшение количества повторных выполнений ресурсоемких вычислений напрямую влияет на загрузку процессора и оперативной памяти сервера 1С:Предприятия. Вместо выполнения сложного алгоритма, платформа просто возвращает сохраненный результат.
  3. Уменьшение клиент-серверного трафика: Если данные кэшируются на клиенте, повторные вызовы функций не требуют обмена данными с сервером. Это особенно актуально для удаленных пользователей или при медленных каналах связи, существенно улучшая отзывчивость пользовательского интерфейса.
  4. Улучшение общей производительности системы: В совокупности все вышеперечисленные факторы приводят к ускорению работы системы в целом, повышению комфорта для пользователей и увеличению пропускной способности.

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

Критически важные особенности и рекомендации по применению

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

Недопустимость изменения кэшированных значений

Это одна из самых критичных особенностей, о которой упоминается в сообщении 6 и подчеркивается в справочных материалах. Категорически не рекомендуется изменять объекты, полученные из кэша. Давайте выясним почему:

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

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

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

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

Разумное ограничение использования

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

Кэширование только для ресурсоемких операций

Мы должны осознанно подходить к выбору функций для кэширования. Не стоит кэшировать данные, которые вычисляются быстрее, чем извлекаются из кэша. Например, получение простой строковой константы или выполнение элементарных арифметических операций, как правило, не требует кэширования. Смысл имеет кэшировать результаты:

  1. Сложных вычислений.
  2. Запросов к базе данных, особенно при выборке значительных объемов данных или сложных структурах запроса.
  3. Данных, полученных из внешних источников (веб-сервисы, HTTP-сервисы, файловые обмены).

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

Частое обращение

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

Диапазон параметров

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

Внутренние вызовы функций

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


// В модуле "МойОбщийМодуль" с ПовторноеИспользованиеВозвращаемыхЗначений = НаВремяВызова

Функция ПолучитьНастройку(ИмяНастройки) Экспорт
    // ... логика получения настройки ...
    Возврат ЗначениеНастройки;
КонецФункции

Функция ПолучитьНастройкиПоГруппе(Группа) Экспорт
    // ...
    Настройка1 = МойОбщийМодуль.ПолучитьНастройку("Настройка1"); // Вызов по полному имени - будет использоваться кэш
    // Настройка2 = ПолучитьНастройку("Настройка2"); // Вызов без полного имени - кэш не будет использоваться
    // ...
    Возврат СписокНастроек;
КонецФункции

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

Типы параметров

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

Пример использования

Рассмотрим гипотетический сценарий, где кэширование НаВремяВызова будет особенно полезным, как было отмечено в сообщении 7 – при массовых сопоставлениях часто используемых значений в больших загрузках данных.

Предположим, мы загружаем большой объем данных из внешнего источника, и для каждой строки нам необходимо получить ОсновнуюВалютуРегламентированногоУчета или СтавкуНДС по определенному ВидуНоменклатуры. Эти значения, как правило, относительно стабильны в рамках одного сеанса или одной объемной загрузки.

Создадим общий модуль с именем МодульКэшированныхЗначений и установим для него свойство ПовторноеИспользованиеВозвращаемыхЗначений в НаВремяВызова.


// В общем модуле "МодульКэшированныхЗначений"
// Свойство "Повторное использование возвращаемых значений" установлено в "На время вызова"

Функция ПолучитьОсновнуюВалюту() Экспорт
    // Эта функция будет выполняться только один раз за серверный вызов/клиентский сценарий
    // и возвращать кэшированное значение при последующих обращениях.
    Возврат Константы.ОсновнаяВалютаРегламентированногоУчета.Получить();
КонецФункции

Функция ПолучитьСтавкуНДСПоВидуНоменклатуры(ВидНоменклатурыСсылка) Экспорт
    // Принимаем ссылку как параметр - примитивный тип для кэширования
    Если ВидНоменклатурыСсылка = Неопределено Тогда
        Возврат Неопределено;
    КонецЕсли;

    // Предположим, есть регистр сведений "НастройкиСтавокНДС"
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ ПЕРВЫЕ 1
        |    НастройкиСтавокНДС.СтавкаНДС
        |ИЗ
        |    РегистрСведений.НастройкиСтавокНДС КАК НастройкиСтавокНДС
        |ГДЕ
        |    НастройкиСтавокНДС.ВидНоменклатуры = &ВидНоменклатуры";

    Запрос.УстановитьПараметр("ВидНоменклатуры", ВидНоменклатурыСсылка);

    РезультатЗапроса = Запрос.Выполнить().Выбрать();

    Если РезультатЗапроса.Следующий() Тогда
        Возврат РезультатЗапроса.СтавкаНДС;
    Иначе
        Возврат Неопределено;
    КонецЕсли;
КонецФункции

Теперь представим код загрузки данных:


// В обработке или документе, где происходит загрузка

Процедура ЗагрузитьДанныеИзФайла(ИмяФайла)
    // ... код открытия и чтения файла ...

    ОсновнаяВалюта = МодульКэшированныхЗначений.ПолучитьОсновнуюВалюту(); // Выполнится 1 раз

    Для Каждого СтрокаДанных Из ТаблицаДанныхФайла Цикл
        // ... обработка строки ...

        ВидНоменклатуры = Справочники.ВидыНоменклатуры.НайтиПоНаименованию(СтрокаДанных.ВидНоменклатурыНаименование);

        // Если для данного ВидаНоменклатурыСтавкаНДС уже запрашивалась
        // в рамках текущего клиентского сценария (или серверного вызова),
        // значение будет взято из кэша, без повторного запроса к БД.
        СтавкаНДС = МодульКэшированныхЗначений.ПолучитьСтавкуНДСПоВидуНоменклатуры(ВидНоменклатуры);

        // ... дальнейшая обработка с использованием полученных значений ...
    КонецЦикла;

    // ... сохранение данных ...
КонецПроцедуры

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

Заключение

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

← На главную