Как реализовать хранение истории уведомлений в 1С, не ломая существующий механизм очереди?

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

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

Анализ исходной проблемы

В типовом сценарии система использует непериодический регистр сведений для отправки Email и SMS. Логика проста: запись попадает в регистр, регламентное задание её считывает, отправляет уведомление и удаляет запись. Проблема возникает при попытке превратить этот же регистр в архив. Поскольку измерения в 1С должны быть уникальными, мы не можем записать два уведомления одному и тому же Получателю по одному и тому же Предмету, если состав измерений ограничен. Автор темы столкнулся с тем, что использование поля ДатаОповещения в качестве измерения невозможно, так как текущий код завязан на фиксированные значения этого поля (например, 31.12.3999).

Разберем подробно два основных способа решения этой задачи и дополним их профессиональными рекомендациями по оптимизации базы данных.

Решение 1: Разделение на два регистра (Очередь и История)

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

Преимущества этого подхода:

  1. Производительность: Основной регистр очереди остается компактным. Индексы работают быстро, блокировок при массовой рассылке будет на порядок меньше.
  2. Чистота кода: Нам не нужно переписывать существующие процедуры отправки, которые ищут или удаляют записи в первом регистре.
  3. Гибкость: В историческом регистре мы можем задать другую периодичность или добавить дополнительные ресурсы (например, текст ошибки при отправке), которые не нужны в оперативной очереди.

Рассмотрим пример кода, который переносит запись из очереди в историю после успешной отправки:


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

Решение 2: Использование Уникального Идентификатора (GUID)

Если создание второго регистра кажется избыточным, можно рассмотреть вариант добавления в текущий регистр измерения Идентификатор с типом УникальныйИдентификатор. Это гарантирует уникальность любой записи, даже если все остальные поля совпадают.

Однако, выясним технические нюансы этого метода. Использование GUID в качестве измерения (которое входит в индекс) может привести к сильной фрагментации индексов в SQL-базе при больших объемах данных. Это происходит потому, что GUID генерируются не последовательно. Если в вашей системе планируется отправка миллионов уведомлений, это может замедлить операции вставки (INSERT).

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

Архитектурные рекомендации по оптимизации истории

Проанализируем дополнительные аспекты, которые помогут сделать систему уведомлений профессиональной и масштабируемой.

1. Использование документов вместо регистров

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

2. Хранение текстов сообщений

Тексты писем могут занимать значительный объем. Ни в коем случае нельзя хранить их в измерениях регистра. Для этих целей используйте Ресурс с типом ХранилищеЗначения. Это позволит платформе 1С сжимать данные и хранить их в отдельных таблицах СУБД, не раздувая основной индекс регистра.

3. Механизм автоматической очистки (Data Retention)

История уведомлений — это классические «мусорные» данные, которые нужны редко. Чтобы база не росла бесконечно, сразу заложите регламентное задание по очистке. Посмотрим на пример простого запроса для удаления старых записей:


Процедура ОчиститьСтаруюИсторию() Экспорт
    ГраницаОчистки = ТекущаяДата() - (60 * 60 * 24 * 90); // Храним за последние 90 дней
    НаборЗаписей = РегистрыСведений.ИсторияУведомлений.СоздатьНаборЗаписей();
    НаборЗаписей.Отбор.Период.Установить(Новый Граница(ГраницаОчистки, ВидГраницы.Исключая));
    НаборЗаписей.Записать();
КонецПроцедуры

4. Борьба с блокировками в высоконагруженных системах

Если уведомления отправляются в несколько потоков несколькими фоновыми заданиями, стандартная выборка из регистра может привести к тому, что два задания попытаются отправить одно и то же сообщение. Чтобы этого избежать, используйте конструкцию ДЛЯ ИЗМЕНЕНИЯ в запросе и внедрите поле Состояние (Новое, В работе, Отправлено).

Разберем правильный метод захвата записи из очереди:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ ПЕРВЫЕ 100
    |   Очередь.Предмет,
    |   Очередь.Получатель
    |ИЗ
    |   РегистрСведений.ОчередьУведомлений КАК Очередь
    |ГДЕ
    |   Очередь.Состояние = ЗНАЧЕНИЕ(Перечисление.СостоянияУведомлений.Новое)
    |ДЛЯ ИЗМЕНЕНИЯ"; // Блокируем записи для других потоков

Подведение итогов

Наиболее сбалансированным решением для 1С является двухзвенная архитектура: непериодический регистр для оперативной очереди (с быстрой очисткой) и периодический регистр (или документ) для хранения истории. Это гарантирует стабильность текущих алгоритмов и предоставляет надежный механизм аудита действий системы без ущерба для производительности.

← На главную