Архитектор осознанных систем
Контекст и ситуация: почему «осознанность» — не модный термин
Когда мы проектируем систему, интуитивно хочется «ускорить» через кэширование данных на стороне клиента или сервера. Но в 1С, где глобальный контекст (модули приложения, сеанса, общие модули) живёт дольше, чем один запрос, неосторожное кэширование превращается в мину замедленного действия. Особенно это проявляется в механизме повторного использования возвращаемых значений (ПовторноИспользуемыеЗначения) и в работе с кэшем запросов.
Технический разбор: как ломается консистентность
Рассмотрим типичный сценарий: в общем модуле определена функция получения курса валюты, которая кэширует результат в глобальную переменную сеанса.
Функция ПолучитьКурсВалюты(ВидВалюты, Дата) Экспорт
КлючКэша = "Курс_" + ВидВалюты + "_" + Формат(Дата, "ДФ=yyyyMMdd");
Если ЗначениеЗаполнено(ПараметрыСеанса.КэшКурсов[КлючКэша]) Тогда
Возврат ПараметрыСеанса.КэшКурсов[КлючКэша];
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ РАЗРЕШЕННЫЕ Курс.Значение ИЗ РегистрСведений.КурсыВалют КАК Курс
|ГДЕ Курс.ВидВалюты = &ВидВалюты И Курс.Дата = &Дата";
Запрос.УстановитьПараметр("ВидВалюты", ВидВалюты);
Запрос.УстановитьПараметр("Дата", Дата);
Результат = Запрос.Выполнить().Выгрузить();
Если Результат.Количество() = 0 Тогда
Возврат Неопределено;
КонецЕсли;
ПараметрыСеанса.КэшКурсов[КлючКэша] = Результат[0].Значение;
Возврат Результат[0].Значение;
КонецФункции
Внешне всё корректно. Но если в рамках одной транзакции мы сначала читаем курс, а потом другой документ (в том же сеансе или в параллельном) его меняет — кэш остаётся старым. И если дата курса совпадает с датой записи, а транзакция ещё не завершена — мы получаем «чтение грязных данных» неявно, через кэш. Проблема усугубляется, если функция вызывается в условных операциях с разными блоками транзакций — тогда одно и то же значение может быть прочитано из кэша в одном транзакционном контексте, а из базы — в другом.
Ключевой тезис: В 1С нет автоматического сброса кэша при изменении данных в текущем сеансе. Программист сам отвечает за инвалидацию. Игнорирование этого правила — путь к неконсистентным отчётам и «магическим» цифрам, которые не сходятся.
Практическое руководство: что делать прямо сейчас
1. Инвентаризация кэша
Пройдитесь по всем общим модулям, где используются ПараметрыСеанса, ГлобальныеПеременные или ПовторноИспользуемыеЗначения для хранения данных, полученных из базы. Для каждого кэша определите:
- Время жизни — может ли он устареть до завершения сеанса?
- Транзакционную зависимость — читается ли он внутри транзакции, а записывается ли вне её?
- Источник инвалидации — есть ли событие, которое сбросит кэш при изменении данных?
Если хотя бы один пункт неясен — кэш опасен.
2. Внедрите «осознанную» стратегию сброса
Лучший способ — не кэшировать на уровне сеанса данные, которые могут меняться в параллельных транзакциях. Или использовать счётчик версий:
// В модуле менеджера регистра
Процедура ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты) Экспорт
// Увеличиваем глобальный счётчик при любом изменении
ПараметрыСеанса.ВерсияКурсов = ПараметрыСеанса.ВерсияКурсов + 1;
КонецПроцедуры
// В функции получения курса
Функция ПолучитьКурсВалюты(ВидВалюты, Дата) Экспорт
Если ПараметрыСеанса.ВерсияКурсов <> ПараметрыСеанса.КэшВерсияКурсов Тогда
ПараметрыСеанса.КэшКурсов = Новый Соответствие;
ПараметрыСеанса.КэшВерсияКурсов = ПараметрыСеанса.ВерсияКурсов;
КонецЕсли;
// ... остальная логика
КонецФункции
Важно: Счётчик версий не решает проблему полностью, если обновление происходит в одном сеансе, а запрос — в другом. Но он защищает от неконсистентности внутри одного сеанса при многократных вызовах.
Типичные ошибки архитекторов
Ошибка 1: «Это же просто кэш, он никому не мешает»
На практике — мешает. В одном из проектов кэш курсов валют, завязанный на ПараметрыСеанса, привёл к тому, что расчёт себестоимости в отчёте «Анализ остатков» использовал курс, записанный три часа назад, пока другой пользователь перепроводил документы. Разница в цифрах достигала 5% — и это при НДС 22% (с 2026 года).
Ошибка 2: «Сброшу кэш при открытии формы»
Сброс кэша в обработчике ПриОткрытии формы не гарантирует, что внутри неё не будет параллельного вызова из другого модуля. Лучше инвалидировать кэш в точке изменения данных, а не в точке чтения.
Чек-лист осознанного архитектора
- Определите, какие данные в вашей системе могут быть прочитаны из кэша, а затем изменены в той же транзакции.
- Для каждого такого кэша реализуйте механизм версионирования или полного отказа от кэширования сложных объектов.
- Проверьте, не используется ли
ПовторноИспользуемыеЗначениядля запросов, которые зависят от текущего состояния транзакции. - Убедитесь, что кэш не хранит ссылки на объекты, которые могут быть удалены в других сеансах (объекты с признаком «ПометкаУдаления»).
- Добавьте в регламент код-ревью: обязательно проверять кэширующие конструкции на транзакционную согласованность.
Вывод: архитектура — это про управление неопределённостью
Осознанное проектирование в 1С — это не про выбор между «толстым» и «тонким» клиентом. Это про понимание времени жизни каждого объекта, каждой переменной, каждого кэша. Мы привыкли думать, что кэш ускоряет — но он ускоряет только до того момента, пока не начнёт искажать данные. И тогда вся экономия времени превращается в часы отладки и «почему у бухгалтера не сходится баланс».
Архитектор осознанных систем — это тот, кто не просто пишет «работает», а знает, как и почему это будет работать после 1000 одновременных сеансов, после обновления конфигурации, после перепроведения документов задним числом. И вовремя ставит блокировку там, где кэш — зло.
Попробовать НОПик →