Глобальные значения в серверном коде 1С 8.3: сеанс, кэш и база

· 9 мин

Содержание

Объявить глобальную переменную на сервере 1С через Перем ... Экспорт, как в модуле приложения, нельзя: в общих модулях переменные не объявляются, а серверный контекст между вызовами не сохраняется. Роль серверной глобальной переменной в пределах сеанса играет параметр сеанса (ПараметрыСеанса.ИмяПараметра). Для значения, общего для всех пользователей, используют константу или регистр сведений .

Выбор зависит от двух вопросов: кто должен видеть значение (один сеанс или все) и как часто оно меняется. Ниже разобраны все рабочие варианты с кодом для платформы 8.3 и управляемого приложения.

Почему на сервере 1С нет переменных «на всё время работы»

Серверный код в 1С 8.3 выполняется короткими вызовами. Клиент вызывает серверную процедуру, рабочий процесс её выполняет, возвращает результат и забывает контекст. Следующий вызов того же пользователя может попасть в другой рабочий процесс.

Отсюда поведение переменных в разных модулях:

Где объявлена переменнаяСколько живёт значениеВидно на сервере глобально?
Модуль управляемого приложения (Перем Х Экспорт)Весь сеанс, но только на клиенте❌ Нет
Модуль внешнего соединенияСеанс COM-соединенияТолько в этом соединении
Общий модульПеременные объявлять нельзя❌ Нет
Модуль формы, серверная частьОдин серверный вызов❌ Нет
Модуль объекта, модуль менеджераПока жив экземпляр объекта или вызов❌ Нет

Переменные модуля формы с директивой &НаСервере обнуляются после каждого серверного вызова. Если значение нужно между вызовами формы, сделайте его реквизитом формы — реквизиты передаются между клиентом и сервером вместе с формой.

Глобальная переменная клиента из модуля приложения на сервер не попадёт. Нужно значение на сервере — передайте его параметром серверной процедуры или храните в параметре сеанса.

Параметры сеанса как глобальная переменная на сервере

Параметр сеанса — объект метаданных из ветки Общие → Параметры сеанса. Значение хранится на сервере, доступно из любого серверного кода текущего сеанса и не теряется между вызовами. Другим пользователям оно не видно: у каждого сеанса свой набор значений.

Как создать параметр сеанса

  1. В конфигураторе откройте ветку Общие → Параметры сеанса и добавьте элемент, например ТекущийСклад.
  2. Задайте тип. Подходят примитивные типы, ссылки и фиксированные коллекции (ФиксированныйМассив, ФиксированнаяСтруктура). Изменяемые Массив и ТаблицаЗначений в параметр сеанса не положить.
  3. В ролях выдайте права на параметр: «Получение» — тем, кто читает значение, «Установка» — только если код пользователя должен его менять.
  4. Откройте модуль сеанса (контекстное меню корня конфигурации → «Открыть модуль сеанса») и напишите обработчик УстановкаПараметровСеанса.

Код инициализации

Платформа вызывает УстановкаПараметровСеанса при старте сеанса с ТребуемыеПараметры = Неопределено, а позже — при первом обращении к ещё не заданному параметру, передавая массив имён. Это позволяет инициализировать тяжёлые значения лениво.

Процедура УстановкаПараметровСеанса(ТребуемыеПараметры)

    Если ТребуемыеПараметры = Неопределено Тогда
        // Старт сеанса: задаём только лёгкие параметры
        Возврат;
    КонецЕсли;

    Для Каждого ИмяПараметра Из ТребуемыеПараметры Цикл
        Если ИмяПараметра = "ТекущийСклад" Тогда
            ПараметрыСеанса.ТекущийСклад = СкладПоУмолчанию();
        КонецЕсли;
    КонецЦикла;

КонецПроцедуры

Функция СкладПоУмолчанию()

    Значение = ХранилищеОбщихНастроек.Загрузить("НастройкиСклада", "ОсновнойСклад");
    Если ТипЗнч(Значение) = Тип("СправочникСсылка.Склады") Тогда
        Возврат Значение;
    КонецЕсли;
    Возврат Справочники.Склады.ПустаяСсылка();

КонецФункции

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

Склад = ПараметрыСеанса.ТекущийСклад;

Поменять значение по ходу работы тоже можно — присваиванием на сервере. Если у роли нет права «Установка», оберните присваивание в УстановитьПривилегированныйРежим(Истина) внутри серверного общего модуля.

Ограничения параметров сеанса

  • 📦 Только сериализуемые и неизменяемые значения. Нужна коллекция — собирайте Структуру, затем Новый ФиксированнаяСтруктура(Структура).
  • 🧮 Держите значения компактными. Данные сеанса хранятся на сервере и сопровождают каждый вызов. Таблица на тысячи строк в параметре сеанса замедлит работу всех серверных вызовов этого пользователя.
  • 👤 Значение живёт ровно один сеанс. После перезапуска клиента оно инициализируется заново.
  • ⚙️ Фоновое задание — отдельный сеанс. Не рассчитывайте, что оно увидит значение, которое вы присвоили в сеансе пользователя: передавайте нужное параметрами задания.

Кэш общих модулей: «глобальная переменная» только для чтения

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

Настройка модуля:

  1. Создайте общий модуль, например НастройкиСервераПовтИсп.
  2. Включите флаг «Сервер», флаг «Вызов сервера» — только если функцию будут вызывать с клиента.
  3. В свойстве «Повторное использование возвращаемых значений» выберите «На время сеанса».
Функция ПараметрыУчета() Экспорт

    Результат = Новый Структура;
    Результат.Вставить("ЛимитОстатка", Константы.ЛимитОстатка.Получить());
    Результат.Вставить("ВалютаУчета", Константы.ВалютаРегламентированногоУчета.Получить());
    Возврат Новый ФиксированнаяСтруктура(Результат);

КонецФункции

Вызов из любого серверного кода: НастройкиСервераПовтИсп.ПараметрыУчета().ЛимитОстатка.

Ключевые свойства кэша:

  • 🔁 Кэш хранит результат для каждого набора значений параметров отдельно.
  • ⏳ Значение может быть удалено из кэша: через 20 минут после вычисления или через 6 минут после последнего обращения, а также при нехватке памяти. Поэтому в кэше держат то, что можно пересчитать, а не «состояние».
  • 🔒 Возвращайте фиксированные коллекции. Если вернуть обычную Структуру, вызывающий код сможет её изменить, и другие вызовы получат испорченное значение.
  • 🧹 Сбросить кэш принудительно можно методом ОбновитьПовторноИспользуемыеЗначения().

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

Общее значение для всех сеансов: хранилища и временная память

Оперативной памяти, общей для всех сеансов, в 1С нет. Сеансы пользователей изолированы, и обменяться значением они могут только через базу данных. Платформа даёт три механизма, которые часто путают.

МеханизмЧьё значениеСколько живётДля чего подходит
ХранилищеОбщихНастроекКонкретного пользователяПостоянно, в базеЛичные настройки: склад по умолчанию, последний фильтр
ПоместитьВоВременноеХранилищеСеанса или формыДо закрытия формы или конца сеансаПередача больших данных между клиентом, сервером и фоновым заданием
Константа, регистр сведенийОбщие для всехПостоянно, в базеЗначения, которые читают и меняют разные пользователи

Хранилище общих настроек по умолчанию сохраняет данные под текущим пользователем. Для общих значений его не используют: каждый пользователь увидит свою копию.

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

Константы и регистр сведений как общая память базы

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

Константа: одно значение на базу

Создайте константу в ветке Константы и задайте тип. Чтение и запись:

Лимит = Константы.ЛимитОстатка.Получить();
Константы.ЛимитОстатка.Установить(500000);

Константа подходит для настроек, которые меняют редко: дата запрета, флаг режима, адрес сервиса. Каждая запись — отдельная транзакция в базе.

Регистр сведений: много значений с ключом

Если нужен набор глобальных значений (счётчики по видам, статус обмена по узлам), создайте независимый непериодический регистр сведений. Например, ГлобальныеСчетчики с измерением Ключ (строка 100) и ресурсом Значение (число 15, 0).

Безопасное увеличение счётчика из многих сеансов сразу — с управляемой блокировкой по конкретному ключу:

Функция СледующееЗначение(Ключ) Экспорт

    НачатьТранзакцию();
    Попытка
        Блокировка = Новый БлокировкаДанных;
        ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ГлобальныеСчетчики");
        ЭлементБлокировки.УстановитьЗначение("Ключ", Ключ);
        ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
        Блокировка.Заблокировать();

        МенеджерЗаписи = РегистрыСведений.ГлобальныеСчетчики.СоздатьМенеджерЗаписи();
        МенеджерЗаписи.Ключ = Ключ;
        МенеджерЗаписи.Прочитать();

        Если МенеджерЗаписи.Выбран() Тогда
            НовоеЗначение = МенеджерЗаписи.Значение + 1;
        Иначе
            МенеджерЗаписи.Ключ = Ключ;
            НовоеЗначение = 1;
        КонецЕсли;

        МенеджерЗаписи.Значение = НовоеЗначение;
        МенеджерЗаписи.Записать();
        ЗафиксироватьТранзакцию();
    Исключение
        ОтменитьТранзакцию();
        ВызватьИсключение;
    КонецПопытки;

    Возврат НовоеЗначение;

КонецФункции

Блокировка ставится до чтения. Без неё два сеанса прочитают одно и то же значение, оба прибавят единицу, и один номер выдадут дважды. Код рассчитан на управляемый режим блокировок: свойство конфигурации «Режим управления блокировкой данных» — «Управляемый».

Где хранить серверное значение?

Что меняется в файловом и клиент-серверном варианте

Программный код для обоих вариантов одинаковый. Различается то, где физически живут данные.

Файловый вариант. Серверная часть работает в процессе клиента или веб-сервера. Параметры сеанса всё равно отдельные для каждого пользователя: два человека в одной файловой базе не видят значения друг друга. Константы и регистры лежат в файле 1Cv8.1CD, а запись в базу в файловом режиме блокирует таблицы целиком. Частая запись общего счётчика здесь быстрее упирается в ожидание.

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

Типичная ловушка при переносе разработки: в файловой базе на одном компьютере всё работало, потому что пользователь был один. В клиент-серверной базе с 50 пользователями тот же код с константой-счётчиком начинает выдавать ошибки блокировок.

Блокировки и производительность общих значений

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

Что помогает:

  • ✅ Дробите ключи. Вместо одного счётчика «на всё» держите счётчик на организацию или на вид документа. Регистр сведений блокируется по значению измерения, а не целиком.
  • ✅ Сокращайте транзакцию. Между Блокировка.Заблокировать() и ЗафиксироватьТранзакцию() — только чтение и запись счётчика, никаких запросов к другим таблицам и обращений к внешним сервисам.
  • ✅ Не пишите константу в цикле. Сначала посчитайте итог, потом запишите один раз.
  • ✅ Читайте редко меняющиеся значения через кэш общего модуля, а не запросом в каждом вызове.
  • ❌ Не храните в константе журнал или очередь. Каждое добавление переписывает всё значение целиком и блокирует его для остальных.

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

Если счётчик нужен только для уникальности, а не для сплошной нумерации, используйте Новый УникальныйИдентификатор. Он не требует записи в базу и блокировок вообще.

Права доступа к глобальным значениям

Глобальные значения часто влияют на безопасность, поэтому права на них настраивают отдельно.

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

Константы. Права «Чтение» и «Изменение» задаются в роли для каждой константы. Если пользователю нужно только косвенно влиять на значение, оставьте ему «Чтение», а изменение делайте в серверном модуле с привилегированным режимом.

Регистр сведений. Аналогично: права на чтение и запись в роли. Для служебного регистра-счётчика прямое право «Изменение» пользователям не выдают. Вызывают серверную функцию, которая включает УстановитьПривилегированныйРежим(Истина) на время записи.

Привилегированный режим. Включайте его точечно и только в серверных общих модулях без флага «Вызов сервера». Иначе клиентский код сможет вызвать функцию с произвольными параметрами в обход ролей.

Перед выпуском доработки с глобальным значением
Выполнено: 0 / 5

Частые вопросы

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