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

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

В процессе администрирования и разработки в среде 1С часто возникает необходимость контролировать актуальность данных. Например, нам нужно знать, когда в последний раз успешно запускался обмен данными, актуализировался ли партионный учет или обновлялись ли курсы валют. Все эти действия обычно выполняются с помощью регламентных заданий. Но как определить дату их последнего успешного выполнения программно?

На форумах часто советуют простое решение — использовать свойство ПоследнееЗадание. Однако на практике этого бывает недостаточно, так как последнее фоновое задание могло завершиться ошибкой, а нам нужна дата именно успешного запуска. В этой статье мы подробно разберем несколько способов решения этой задачи: от самых простых встроенных механизмов платформы до наиболее надежных архитектурных подходов класса «Best Practice».

Способ 1. Использование свойства "ПоследнееЗадание" (с проверкой статуса)

Давайте начнем с метода, предложенного на форуме. У каждого регламентного задания есть свойство ПоследнееЗадание, которое возвращает объект типа ФоновоеЗадание. Напрямую обращаться к дате окончания этого задания нельзя, так как оно могло аварийно завершиться или все еще находиться в процессе выполнения. Нам необходимо обязательно проверить его свойство Состояние.

Рассмотрим детальный пример кода, реализующий этот подход:


// 1. Устанавливаем отбор по метаданным нужного регламентного задания
Отбор = Новый Структура("Метаданные", Метаданные.РегламентныеЗадания.ИмяВашегоЗадания);
НайденныеЗадания = РегламентныеЗадания.ПолучитьРегламентныеЗадания(Отбор);

Если НайденныеЗадания.Количество() > 0 Тогда
    РегЗадание = НайденныеЗадания[0];
    ПоследнееФЗ = РегЗадание.ПоследнееЗадание;
    
    Если ПоследнееФЗ <> Неопределено Тогда
        // Проверяем, завершилось ли последнее фоновое задание успешно
        Если ПоследнееФЗ.Состояние = СостояниеФоновогоЗадания.Завершено Тогда
            ДатаУспешногоВыполнения = ПоследнееФЗ.Конец; // Свойство "Конец" содержит дату и время завершения
            Сообщить("Задание успешно выполнено: " + ДатаУспешногоВыполнения);
        Иначе
            Сообщить("Последний запуск завершился с ошибкой, был отменен или еще выполняется.");
        КонецЕсли;
    Иначе
        Сообщить("Информация о запусках регламентного задания отсутствует.");
    КонецЕсли;
КонецЕсли;

Проанализируем ограничения этого способа. Хотя он очень прост в реализации, у него есть два критических недостатка:

  1. Проблема последнего сбоя: если регламентное задание успешно отработало вчера, а сегодня при запуске упало с ошибкой, свойство ПоследнееЗадание вернет сегодняшнюю ошибку со статусом АварийноЗавершено. В результате мы не сможем узнать дату вчерашнего успешного запуска, хотя фактически данные актуальны на вчерашний день.
  2. Очистка истории кластера: история фоновых заданий хранится в оперативной памяти и логах кластера серверов 1С. При перезапуске службы ragent, очистке сеансов или ротации логов эта история стирается. Если регламентное задание выполняется редко (например, раз в месяц), свойство ПоследнееЗадание может вернуть Неопределено.

Способ 2. Получение списка фоновых заданий с фильтрацией

Чтобы обойти проблему последнего неудачного запуска, мы можем запросить у платформы всю доступную историю выполнения фоновых заданий для конкретного регламентного задания и отфильтровать их только по успешным событиям. Для этого используется метод ПолучитьФоновыеЗадания глобального менеджера ФоновыеЗадания.

Разберем по шагам, как это сделать программно:


// Шаг 1. Получаем регламентное задание
ОтборРег = Новый Структура("Метаданные", Метаданные.РегламентныеЗадания.ИмяВашегоЗадания);
НайденныеЗадания = РегламентныеЗадания.ПолучитьРегламентныеЗадания(ОтборРег);

Если НайденныеЗадания.Количество() > 0 Тогда
    РегЗадание = НайденныеЗадания[0];
    
    // Шаг 2. Настраиваем отбор для списка фоновых заданий
    ОтборФЗ = Новый Структура;
    ОтборФЗ.Вставить("РегламентноеЗадание", РегЗадание);
    ОтборФЗ.Вставить("Состояние", СостояниеФоновогоЗадания.Завершено); // Фильтруем только успешные
    
    СписокФЗ = ФоновыеЗадания.ПолучитьФоновыеЗадания(ОтборФЗ);
    
    // Полученный массив автоматически отсортирован по времени запуска.
    // Самый последний элемент массива будет являться самым свежим успешным запуском.
    Если СписокФЗ.Количество() > 0 Тогда
        ПоследнееУспешноеФЗ = СписокФЗ[СписокФЗ.Количество() - 1];
        ДатаУспешногоВыполнения = ПоследнееУспешноеФЗ.Конец;
        Сообщить("Дата последнего успешного выполнения: " + ДатаУспешногоВыполнения);
    Else
        Сообщить("В системной истории кластера нет успешных запусков этого задания.");
    КонецЕсли;
КонецЕсли;

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

Способ 3. Запись статуса в базу данных (Рекомендуемый подход / Best Practice)

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

Реализуем этот подход по шагам:

  1. Создание структуры хранения: Создадим в конфигураторе непериодический регистр сведений, например, ИсторияЗапускаРегламентныхЗаданий. В качестве измерения укажем ИмяРегламентногоЗадания (тип Строка), а в качестве ресурса — ДатаПоследнегоУспешногоЗапуска (тип Дата).
  2. Фиксация статуса в коде: В самом конце процедуры, которая привязана к регламентному заданию, пропишем вызов функции фиксации времени. Если в процессе выполнения основного алгоритма произойдет ошибка, выполнение прервется, и код записи в регистр просто не выполнится.

Пример кода внутри процедуры регламентного задания:


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

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


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ИсторияЗапуска.ДатаПоследнегоУспешногоЗапуска КАК ДатаУспеха
    |ИЗ
    |	РегистрСведений.ИсторияЗапускаРегламентныхЗаданий КАК ИсторияЗапуска
    |ГДЕ
    |	ИсторияЗапуска.ИмяРегламентногоЗадания = &ИмяЗадания";

Запрос.УстановитьПараметр("ИмяЗадания", "ВыполнитьРегламентныйОбменДанными");
Выборка = Запрос.Выполнить().Выбрать();

Если Выборка.Следующий() Тогда
    ДатаУспеха = Выборка.ДатаУспеха;
    Сообщить("Дата последнего успешного выполнения из БД: " + ДатаУспеха);
Иначе
    Сообщить("Задание еще ни разу не выполнялось успешно.");
КонецЕсли;

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

Способ 4. Анализ Журнала регистрации

Если вносить изменения в конфигурацию (создавать новые регистры) нельзя, а история фоновых заданий в кластере уже очистилась, у нас остается еще один системный инструмент — Журнал регистрации. Каждое регламентное задание при успешном завершении делает запись в журнал.

Для программного чтения воспользуемся методом ВыгрузитьЖурналРегистрации():


ФильтрЖР = Новый Структура;
ФильтрЖР.Вставить("Событие", "_$Job$.Success"); // Фильтр успешного завершения фоновых заданий
ФильтрЖР.Вставить("Начало", ТекущаяДата() - 86400 * 30); // Ищем в логах за последние 30 дней

ТаблицаЛога = Новый ТаблицаЗначений;
ВыгрузитьЖурналРегистрации(ТаблицаЛога, ФильтрЖР, "Дата, Данные, ПредставлениеДанных");

// Теперь нам нужно вручную отфильтровать таблицу по конкретному регламентному заданию,
// так как напрямую установить отбор по конкретному рег. заданию в ЖР сложнее.
// Мы можем анализировать колонку "ПредставлениеДанных" или связывать через метаданные.

Важно помнить, что этот метод является крайне ресурсоемким. Если журнал регистрации на сервере весит десятки гигабайт, выполнение метода ВыгрузитьЖурналРегистрации может вызвать серьезные зависания всей системы. Рекомендуется использовать его только в исключительных случаях или в качестве разовой административной процедуры.

← На главную