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

Каверзный вопрос уровня middle: «Функция общего модуля с повторным использованием вернула старое значение. Почему?»

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

Что это и зачем

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

Зачем это нужно? Есть функции, которые вызываются десятки раз за операцию и каждый раз лезут в базу за одним и тем же: курс валюты на дату, настройка учёта, ставка НДС, ссылка на «Организацию по умолчанию». Считать это заново — пустая трата серверных вызовов и запросов. Кеш убирает повторную работу.

  • На время вызова — кеш живёт в пределах одного клиентского вызова сервера и очищается по его завершении. Безопаснее: данные не «протухают» надолго.
  • На время сеанса — кеш живёт до конца сеанса пользователя. Максимальная экономия, но и максимальный риск получить устаревшее значение.

Как это выглядит в коде

Само тело функции ничем не примечательно — «магия» включается свойством модуля, а не кодом:

// Общий модуль "КешируемыеЗначения", свойство: Повторное использование = На время сеанса

Функция СтавкаНДС(ДатаДокумента) Экспорт
    Запрос = Новый Запрос;
    Запрос.Текст =
        "ВЫБРАТЬ Ставка ИЗ РегистрСведений.СтавкиНДС.СрезПоследних(&Дата) ГДЕ Ставка > 0";
    Запрос.УстановитьПараметр("Дата", ДатаДокумента);
    Выборка = Запрос.Выполнить().Выбрать();
    Возврат ?(Выборка.Следующий(), Выборка.Ставка, 20);
КонецФункции
// Первый вызов — тело выполнится, сходит в базу
Ставка1 = КешируемыеЗначения.СтавкаНДС(Дата('20260101'));
// Второй вызов с ТЕМ ЖЕ параметром — тело НЕ выполнится, результат из кеша
Ставка2 = КешируемыеЗначения.СтавкаНДС(Дата('20260101'));
// Другой параметр — снова выполнение и свой отдельный элемент кеша
Ставка3 = КешируемыеЗначения.СтавкаНДС(Дата('20250101'));

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

Как это работает под капотом

Платформа хранит таблицу соответствия «параметры → результат» на сервере. При вызове она сначала ищет запись по параметрам; нашла — отдаёт готовое, не входя в тело; не нашла — выполняет функцию, кладёт результат в таблицу и возвращает. Для режима «на время вызова» таблица очищается после завершения серверного вызова, для «на время сеанса» — живёт, пока жив сеанс (или пока платформа сама не вытеснит запись по внутренним лимитам времени/объёма — на это полагаться не стоит).

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

Где стреляет в ногу: устаревший кеш

Главная ловушка. Представьте: бухгалтер поменял ставку НДС в справочнике прямо во время работы. Функция с режимом «на время сеанса» уже закешировала старую ставку — и до перезахода пользователя будет упорно возвращать старое значение. Данные в базе новые, а функция врёт.

// Утро: пользователь получил ставку — она осела в кеше сеанса
Ставка = КешируемыеЗначения.СтавкаНДС(ТекущаяДата()); // 20%

// Днём администратор изменил запись в регистре СтавкиНДС на 18%
// Но в этом же сеансе:
СтаваяЖе = КешируемыеЗначения.СтавкаНДС(ТекущаяДата()); // всё ещё 20% из кеша!

Отсюда практические правила: кешировать только то, что меняется редко (настройки, курсы, константы). Для данных, которые могут меняться в течение дня, брать «на время вызова», а не «на время сеанса». А если нужно принудительно сбросить кеш — есть метод ОбновитьПовторноИспользуемыеЗначения(), который очищает кеш повторного использования в текущем сеансе.

// После записи, меняющей закешированные данные, сбрасываем кеш сеанса
ОбъектНастройки.Записать();
ОбновитьПовторноИспользуемыеЗначения();

Как отвечать

Сильный ответ: «Это кеш результата функции по её параметрам. Включается свойством общего модуля: на время вызова или на время сеанса. Нужен, чтобы не пересчитывать одно и то же — например, курс валюты или настройку, которую дёргают десятки раз. Главная опасность — устаревание: если закешированные данные изменятся, функция вернёт старое значение. Поэтому кеширую только редко меняющееся, для менее стабильного беру время вызова, а при необходимости сбрасываю кеш через ОбновитьПовторноИспользуемыеЗначения()».

Если спросят «в чём разница режимов» — вызов живёт в пределах одного обращения к серверу, сеанс — до конца работы пользователя; сеанс экономит больше, но и рискованнее.

Частые ошибки на собеседовании

  • Не знать про риск устаревания и хвалить кеш как «бесплатное ускорение без минусов».
  • Путать, что кешируется. Ключ — параметры; вызовы с разными параметрами не делят один результат.
  • Считать, что можно кешировать процедуры или функции без параметров, зависящие от глобального состояния.
  • Не знать про ОбновитьПовторноИспользуемыеЗначения() и способ принудительно сбросить кеш.
  • Пихать в кеш быстро меняющиеся данные (например, текущие остатки) — гарантированные баги с «неправильными цифрами».

Итоги

  • Свойство общего модуля кеширует результат функции по её параметрам; режимы — на время вызова и на время сеанса.
  • Экономит повторные вычисления и обращения к базе для редко меняющихся данных.
  • Главный риск — устаревший кеш: изменили данные, а функция отдаёт старое.
  • Кешируйте стабильное; сбрасывайте кеш через ОбновитьПовторноИспользуемыеЗначения() после изменений.
Проверьте себя
1. Общий модуль имеет режим «Повторное использование = На время сеанса». Функция вернула значение, потом данные в базе изменили. Что вернёт следующий вызов с теми же параметрами в этом же сеансе?
AНовое значение из базы — платформа отслеживает изменения автоматически
BСтарое значение из кеша, пока кеш не сброшен или не закончится сеанс
CОшибку, потому что кеш стал невалидным
DПустое значение до перезапуска сервера
2. Что служит ключом кеша для повторного использования возвращаемых значений?
AИмя функции целиком, независимо от аргументов
BНабор значений параметров, с которыми вызвана функция
CИмя пользователя, запустившего сеанс
DВремя вызова функции