Как перехватить и записать дату и время динамического обновления в 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

Для минимизации рисков разработчику важно четко фиксировать точный момент применения динамического обновления. В этой статье мы подробно разберем, как перехватить это событие в реальном времени, зафиксировать дату и время его проведения, а также рассмотрим технические решения как средствами встроенного языка 1С, так и на уровне систем управления базами данных (СУБД).

Вариант 1. Использование обработчика ожидания и встроенного языка 1С

Самый очевидный способ перехватить обновление на стороне платформы — периодически опрашивать систему на предмет изменения конфигурации базы данных. Для этого во встроенном языке 1С предусмотрен метод КонфигурацияБазыДанныхИзмененаДинамически(). Этот метод возвращает значение Истина, если в базу были внесены динамические изменения, которые еще не были приняты текущим активным сеансом.

Проанализируем базовый алгоритм реализации данного подхода:

  1. В модуле управляемого приложения при старте системы подключается обработчик ожидания, который будет с заданной периодичностью выполнять проверку.
  2. В процедуре обработчика ожидания вызывается метод проверки изменений.
  3. Если факт обновления зафиксирован, система выполняет запись даты и времени в базу данных (например, в специально созданный непериодический регистр сведений) и отключает обработчик ожидания для текущего сеанса, чтобы избежать дублирования проверок.

Рассмотрим пример программного кода для реализации этого механизма:


// В модуле управляемого приложения
Процедура ПриНачалеРаботыСистемы()
    #Если Клиент Тогда
        // Подключаем проверку раз в 5 минут (300 секунд)
        ПодключитьОбработчикОжидания("ПроверитьДинамическоеОбновлениеИБ", 300);
    #КонецЕсли
КонецПроцедуры

// В глобальном или общем клиентском модуле
Процедура ПроверитьДинамическоеОбновлениеИБ() Экспорт
    Если КонфигурацияБазыДанныхИзмененаДинамически() Тогда
        // Передаем управление на сервер для фиксации события
        ЗафиксироватьДатуОбновленияНаСервере();
        
        // Отключаем обработчик текущего сеанса, чтобы не спамить базу данных
        ОтключитьОбработчикОжидания("ПроверитьДинамическоеОбновлениеИБ");
    КонецЕсли;
КонецПроцедуры

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


// В общем серверном модуле
Процедура ЗафиксироватьДатуОбновленияНаСервере() Экспорт
    УстановитьПривилегированныйРежим(Истина);
    
    ТекущийМомент = ТекущаяДатаСеанса();
    
    // Блокируем чтение/запись регистра для предотвращения дублей от разных сеансов
    Блокировка = Новый БлокировкаДанных;
    ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ИсторияДинамическихОбновлений");
    
    НачатьТранзакцию();
    Попытка
        Блокировка.Заблокировать();
        
        // Проверяем, была ли уже сделана запись за последние 10 минут
        Запрос = Новый Запрос;
        Запрос.Текст = 
            "ВЫБРАТЬ ПЕРВЫЕ 1
            |	История.Период КАК Период
            |ИЗ
            |	РегистрСведений.ИсторияДинамическихОбновлений КАК История
            |УПОРЯДОЧИТЬ ПО
            |	Период УБЫВ";
            
        РезультатЗапроса = Запрос.Выполнить();
        НужноЗаписать = Истина;
        
        Если Не РезультатЗапроса.Пустой() Тогда
            Выборка = РезультатЗапроса.Выбрать();
            Выборка.Следующий();
            // Если последняя запись сделана менее 10 минут назад, значит, это то же самое обновление
            Если ТекущийМомент - Выборка.Период < 600 Тогда
                НужноЗаписать = Ложь;
            КонецЕсли;
        КонецЕсли;
        
        Если НужноЗаписать Тогда
            МенеджерЗаписи = РегистрыСведений.ИсторияДинамическихОбновлений.СоздатьМенеджерЗаписи();
            МенеджерЗаписи.Период = ТекущийМомент;
            МенеджерЗаписи.Инициатор = ПараметрыСеанса.ТекущийПользователь;
            МенеджерЗаписи.Записать();
        КонецЕсли;
        
        ЗафиксироватьТранзакцию();
    Исключение
        ОтменитьТранзакцию();
        ЗаписьЖурналаРегистрации("ДинамическоеОбновление", 
            УровеньЖурналаРегистрации.Ошибка, , , ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
    КонецПопытки;
КонецПроцедуры

Вариант 2. Интеграция с Библиотекой стандартных подсистем (БСП)

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

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

  1. Найдем в БСП общий модуль, отвечающий за периодические проверки (например, СтандартныеПодсистемыСервер или процедуры модуля управляемого приложения, вызывающие проверку версии программы).
  2. Создадим расширение и перехватим метод проверки динамического обновления (например, процедуру ПриВыполненииСтандартныхПериодическихПроверокНаСервере) с помощью аннотации &После.
  3. Внедренный код расширения проверяет факт обновления через системный вызов и, в случае положительного результата, фиксирует точное время в нашей структуре данных.

Этот подход гарантирует минимальную нагрузку на систему, так как периодичность опроса централизованно управляется настройками БСП.

Вариант 3. Использование триггеров на уровне СУБД (MS SQL Server / PostgreSQL)

Если ваша информационная база развернута в клиент-серверном варианте, наиболее точным и производительным решением будет перехват события непосредственно в СУБД. При проведении динамического обновления платформа 1С производит запись новых версий объектов метаданных в системную таблицу dbo.Config (или public.config в случае PostgreSQL).

Выясним подробнее, что происходит внутри таблицы Config при запуске динамического обновления:

Мы можем создать триггер СУБД класса AFTER INSERT, UPDATE на таблицу Config, который будет автоматически записывать текущую системную дату сервера баз данных в специально созданную таблицу логирования.

Рассмотрим пример реализации SQL-триггера для MS SQL Server:


-- 1. Создаем отдельную таблицу для хранения истории в нашей БД 1С
CREATE TABLE dbo.My_DynamicUpdateLog (
    ID INT IDENTITY(1,1) PRIMARY KEY,
    UpdateDateTime DATETIME NOT NULL,
    HostName NVARCHAR(128),
    AppUser NVARCHAR(128),
    Spid INT
);
GO

-- 2. Создаем триггер на системную таблицу Config
CREATE TRIGGER trg_Config_DynamicUpdate
ON dbo.Config
AFTER INSERT, UPDATE
AS
BEGIN
    SET NOCOUNT ON;
    
    -- Проверяем, были ли изменены или добавлены записи с маркерами динамического обновления
    IF EXISTS (
        SELECT 1 
        FROM inserted 
        WHERE FileName IN ('dynamicCommit', 'DynamicallyUpdated')
    )
    BEGIN
        -- Записываем системное время СУБД, имя ПК и учетную запись, из-под которой запущено обновление
        INSERT INTO dbo.My_DynamicUpdateLog (UpdateDateTime, HostName, AppUser, Spid)
        VALUES (
            GETDATE(), 
            HOST_NAME(), 
            ORIGINAL_LOGIN(), 
            @@SPID
        );
    END
END;
GO

Преимущества использования СУБД-триггера:

Вариант 4. Использование Технологического журнала 1С

Для администраторов крупных систем хорошей альтернативой без изменения кода конфигурации и СУБД является настройка Технологического журнала (ТЖ) платформы. При динамическом обновлении сервер приложений 1С (процессы rphosts) начинает генерировать системные события, связанные с пересогласованием конфигураций и несоответствием версий метаданных у пользователей.

Для детального логирования достаточно настроить файл конфигурации ТЖ logcfg.xml на сбор событий, связанных с конфигурацией базы данных, или отслеживать появление предупреждений вида: «Попытка подключения к контексту сервера с неподходящей версией метаданных». Анализируя лог-файлы ТЖ внешними парсерами (например, скриптами на Python или с помощью систем сбора логов ELK), можно оперативно фиксировать и визуализировать моменты обновления ИБ.

Резюме и рекомендации

Мы рассмотрели несколько подходов для решения поставленной задачи. Выбор конкретного метода зависит от структуры вашей инфраструктуры:

← На главную