В процессе разработки на платформе «1С:Предприятие 8» часто возникает задача организации отложенной обработки данных. Если с документами или справочниками все предельно просто — мы можем легко сохранить ссылку на объект (ДокументСсылка или СправочникСсылка) в очередь задач, то с регистрами сведений (РС) и накопления (РН) ситуация обстоит сложнее. Объект типа «Набор записей» не имеет уникального идентификатора (UUID) в базе данных, так как является динамической коллекцией, определяемой параметрами отбора.
В этой статье мы подробно разберем, как обойти это ограничение платформы, проанализируем различные архитектурные подходы и выберем оптимальные решения для сохранения условной «ссылки» на набор записей регистра.
Для начала выясним причину проблемы. Набор записей регистрируется в базе данных по определенному фильтру. Физически это набор строк в таблице СУБД, удовлетворяющих условиям отбора.
Способ идентификации набора напрямую зависит от режима записи регистра:
ДокументСсылка), чтобы в дальнейшем прочитать по нему весь набор записей.Ниже мы детально рассмотрим три наиболее эффективных способа решения этой задачи.
Рассмотрим простейший вариант, предложенный автором темы: упаковка всех полей отбора в структуру с последующим сохранением в один реквизит нашей очереди задач (например, в регистр сведений «ОчередьЗадач» с типом ресурса ХранилищеЗначения или неограниченная строка).
Разберем по шагам, как это реализовать с помощью сериализации структуры в JSON. Использование JSON предпочтительнее стандартного ХранилищаЗначения, так как строку JSON легче читать при отладке, она весит меньше и быстрее обрабатывается СУБД.
Посмотрим на пример кода, выполняемый в обработчике события ПередЗаписью или ПриЗаписи набора записей регистра сведений:
// Определяем структуру отбора независимого регистра
СтруктураОтбора = Новый Структура;
Для Каждого ЭлементОтбора Из ЭтотОбъект.Отбор Цикл
Если ЭлементОтбора.Использование Тогда
СтруктураОтбора.Вставить(ЭлементОтбора.Имя, ЭлементОтбора.Значение);
КонецЕсли;
КонецЦикла;
// Добавляем метаданные, чтобы знать, какой регистр обрабатывать
ДанныеПакета = Новый Структура;
ДанныеПакета.Вставить("ИмяРегистра", "ЦеныНоменклатуры");
ДанныеПакета.Вставить("Отбор", СтруктураОтбора);
// Сериализуем структуру в строку JSON
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, ДанныеПакета);
СтрокаJSON = ЗаписьJSON.Закрыть();
// Теперь СтрокуJSON можно записать в текстовый реквизит нашей очереди задач
Для восстановления отбора и последующего чтения набора записей мы используем десериализацию:
// Чтение данных из очереди задач
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(СтрокаJSON);
ДанныеПакета = ПрочитатьJSON(ЧтениеJSON);
ЧтениеJSON.Закрыть();
ИмяРегистра = ДанныеПакета.ИмяРегистра;
СтруктураОтбора = ДанныеПакета.Отбор;
// Воссоздаем набор записей и применяем сохраненный отбор
НаборЗаписей = РегистрыСведений[ИмяРегистра].СоздатьНаборЗаписей();
Для Каждого КлючИЗначение Из СтруктураОтбора Цикл
НаборЗаписей.Отбор[КлючИЗначение.Ключ].Установить(КлючИЗначение.Значение, Истина);
КонецЦикла;
НаборЗаписей.Прочитать();
// Выполняем отложенную обработку набора данных...
Преимущества метода: Универсальность, простота внедрения, независимость от метаданных конфигурации.
Недостатки: Мы не можем выполнять поиск или группировку по элементам отбора внутри СУБД (например, быстро определить в запросе, есть ли уже в очереди обработка по конкретной номенклатуре), так как данные упакованы в строку или бинарный объект.
Если нам требуется гарантированная и производительная регистрация изменений для последующей отправки или обработки, платформа предоставляет мощный встроенный механизм — Планы обмена. Многие разработчики забывают, что планы обмена умеют работать не только со ссылочными типами, но и с наборами записей регистров.
При регистрации изменений набора записей в плане обмена платформа автоматически создает и сохраняет специальный системный объект — РегистрСведенийКлючЗаписи (или аналог для РН). Этот ключ содержит значения всех измерений, однозначно идентифицирующих измененную запись.
Рассмотрим подробнее схему работы:
ПланыОбмена.ЗарегистрироватьИзменения().КлючЗаписи.Преимущества метода: Высокая надежность, транзакционная целостность (регистрация происходит в той же транзакции, что и запись данных), автоматическое отслеживание изменений платформой.
Недостатки: В высоконагруженных системах (HighLoad) частая запись в таблицы регистрации изменений планов обмена может приводить к избыточным блокировкам в СУБД. Именно поэтому автор темы искал альтернативные пути решения.
Проанализируем продвинутый подход, который используется в современных типовых конфигурациях фирмы «1С» (например, в «1С:ERP Управление предприятием» или «1С:Управление торговлей 11»). Этот подход идеален для высоконагруженных систем.
Суть паттерна заключается в уходе от хранения множества отдельных измерений в самом регистре сведений и переходе к хранению единого ссылочного измерения — Ключа аналитики.
Рассмотрим реализацию этого паттерна:
КлючиАналитикиУчетаНоменклатуры.СправочникСсылка.КлючиАналитикиУчетаНоменклатуры.Теперь, если нам требуется сохранить «ссылку» на набор записей для очереди отложенной обработки, нам не нужно упаковывать сложные структуры в JSON или XML. Нам достаточно сохранить в очередь обычную ссылку на элемент справочника ключа аналитики!
Посмотрим на пример запроса к очереди задач при использовании ключей аналитики:
ВЫБРАТЬ
ОчередьЗадач.КлючАналитики,
ОчередьЗадач.ПараметрОбработки
ИЗ
РегистрСведений.ОчередьЗадач КАК ОчередьЗадач
ГДЕ
ОчередьЗадач.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗадач.Новая)
Преимущества метода:
Недостатки: Требует серьезного изменения архитектуры метаданных конфигурации и написания дополнительного кода для поиска/создания ключей аналитики при записи движений.
Мы детально разобрали три подхода к решению проблемы отсутствия ссылок у наборов записей. Выбор конкретного решения зависит от требований вашего проекта: