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