В практике разработки на платформе 1С:Предприятие часто встречается ситуация, когда внешние системы, обращаясь к HTTP-сервисам, получают неактуальные данные. Представим сценарий: пользователь меняет значение реквизита в справочнике, записывает его, и тут же инициируется HTTP-запрос к этой же базе. К удивлению разработчика, HTTP-сервис возвращает старое значение, которое было до записи. При этом, если подождать 15-20 секунд, данные "чудесным образом" обновляются. Рассмотрим подробнее, почему возникает этот эффект «запоздания» и как заставить систему отдавать актуальную информацию мгновенно.
Первое, что нам необходимо понять — это механизм работы сеансов HTTP-сервисов. Когда мы публикуем базу на веб-сервере (Apache или IIS), платформа использует так называемый пул соединений. Это означает, что после выполнения запроса сеанс не завершается немедленно, а "засыпает" на определенное время, ожидая следующего вызова. Это сделано для экономии ресурсов на авторизацию и инициализацию параметров сеанса.
Проанализируем ситуацию с точки зрения кэширования. В 1С существует несколько уровней кэша. Самый коварный в данном случае — объектный кэш сеанса. Когда в рамках одного сеанса (даже если он "спящий") происходит обращение к объекту через ПолучитьОбъект() или через обращение к реквизиту по ссылке (Ссылка.Реквизит), платформа считывает данные из СУБД и помещает их в память сеанса. При повторном обращении в этом же сеансе платформа, в целях оптимизации, берет данные из памяти, а не идет в SQL Server.
Если в это время данные были изменены другим пользователем (или в другом сеансе), "спящий" сеанс HTTP-сервиса об этом не узнает, пока срок жизни его внутреннего кэша не истечет или пока сеанс не будет перезапущен. Именно поэтому в отладке мы можем наблюдать, как версия данных "сама" меняется через некоторое время — просто истекает время жизни кэша.
Самый быстрый способ гарантировать получение свежих данных — запретить повторное использование сеансов или ограничить их время жизни. Разберем по шагам, как это сделать:
default.vrd в каталоге на веб-сервере.sessionMaxAge. Этот параметр определяет, сколько секунд сеанс будет храниться в пуле после завершения запроса.sessionMaxAge="0", платформа будет создавать новый сеанс для каждого запроса. Это гарантирует отсутствие старого кэша, но может увеличить нагрузку на сервер из-за постоянных инициализаций.Пример настройки в файле default.vrd:
<point xmlns="http://v8.1c.ru/8.2/virtual-resource-system"
base="/MyBase"
ib="Srvr="ServerName";Ref="BaseName";">
<httpService name="MyService"
alias="service.1c"
sessionMaxAge="0"
poolSize="10"/>
</point>
Важно: После внесения изменений в default.vrd обязательно выполните перезапуск веб-сервера, чтобы настройки вступили в силу.
Если мы не хотим полностью отключать пул сеансов (например, ради производительности), мы можем программно управлять кэшем внутри кода самого HTTP-сервиса. Рассмотрим метод ОчиститьКэшОбъектов().
Вызов этого метода в начале обработки HTTP-запроса заставляет платформу сбросить все накопленные объектные данные текущего сеанса. Выясним, как это выглядит в коде:
Функция ДанныеСправочникаГет(Запрос)
// Принудительно очищаем кэш, чтобы не получить старые данные из пула
ОчиститьКэшОбъектов();
СсылкаНаЭлемент = Справочники.Номенклатура.НайтиПоКоду("001");
// Теперь обращение к реквизитам гарантированно вызовет запрос к БД
ЗначениеРеквизита = СсылкаНаЭлемент.НужныйРеквизит;
// ... дальнейшая логика
КонецФункции
Проанализируем разницу между обращением "через точку" и использованием объекта Запрос. В 1С запросы (Query) гораздо меньше подвержены влиянию объектного кэширования сеанса. Если вы используете конструкцию Справочники.Имя.ПолучитьОбъект(), вы почти наверняка попадете в кэш. Если же вы выбираете данные через Запрос, платформа чаще обращается напрямую к таблицам СУБД.
Для максимальной уверенности в актуальности данных в тексте запроса можно использовать конструкцию ДЛЯ ИЗМЕНЕНИЯ. Рассмотрим пример:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.НужныйРеквизит КАК Значение
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка = &Ссылка
|ДЛЯ ИЗМЕНЕНИЯ"; // Блокировка и обход кэша
Запрос.УстановитьПараметр("Ссылка", НужнаяСсылка);
Результат = Запрос.Выполнить();
Использование ДЛЯ ИЗМЕНЕНИЯ заставляет систему игнорировать любые промежуточные кэши и извлекать самую последнюю версию данных непосредственно из транзакционного лога СУБД.
Посмотрим на ситуацию с системной стороны. Если ваша информационная база развернута в кластере с несколькими рабочими процессами (rphost), может возникнуть задержка синхронизации между процессами. Пользователь в одном процессе изменил данные, а HTTP-сеанс запущен в другом процессе. Платформе требуется 1-2 секунды, чтобы оповестить все процессы кластера об изменении данных (невалидности кэша).
В этом случае, даже программная очистка кэша в коде может не сработать мгновенно. Единственным надежным способом здесь остается либо использование запроса с явным указанием ДЛЯ ИЗМЕНЕНИЯ, либо небольшая технологическая пауза перед вызовом HTTP-сервиса на стороне клиента, если бизнес-логика это позволяет.
Подводя итог, если вы столкнулись с "запозданием" данных в HTTP-сервисе, рекомендуем придерживаться следующего алгоритма:
sessionMaxAge="0" в default.vrd. Это решит 99% проблем с кэшированием.ПолучитьОбъект() в коде HTTP-сервисов. Запросы к базе данных работают чище.ОчиститьКэшОбъектов() в начало экспортных функций модулей HTTP-сервисов.ВерсияДанных объекта через запрос. Если версия в кэше не совпадает с базой, перечитывайте объект.Соблюдение этих правил обеспечит консистентность данных при интеграции 1С с внешними системами и избавит вас от трудноуловимых ошибок, связанных с "призрачными" данными в отладке.