При переходе с Directum 5 на RX в рамках одного из проектов компании СТАРКОВ Групп наша команда столкнулась с рядом сложностей, связанных с обработкой исторических данных.
Сама миграция прошла успешно, однако уже после внедрения начали проявляться проблемы, которые по разным причинам не были учтены заранее. Для их оперативного решения мы использовали привычные инструменты: фоновые процессы, асинхронные обработчики и загрузку данных из Excel-файлов, предоставляемых заказчиком.
Под «привычными инструментами» подразумевается создание отдельного фонового процесса либо асинхронного обработчика под каждую конкретную задачу. Это стандартный и быстрый способ разработки в Directum RX, который подходит для большинства задач, когда ожидаемый объем данных не нагружает сервер и занимает приемлемое время обработки.
При работе с сотнями тысяч записей появляются ключевые трудности:
Стандартные инструменты (фоновые процессы, асинхронные обработчики) предоставляют базовые возможности, но не предлагают готовой инфраструктуры для массовой обработки данных. Разработчик каждый раз вынужден вручную писать сложный код для:
Это не только замедляет разработку, но и делает процессы менее надежными.
При этом Directum RX «из коробки» уже имеет механизм, решающий все эти проблемы в частном порядке для правил назначения прав. Для минимизации нагрузки используется расписание фонового процесса, а количество документов в пакете и количество пакетов, для которых выдача прав должна запускаться одновременно настраиваются через конфигурационный файл. К сожалению, в этом замечательном механизме отсутствует переиспользуемая инфраструктура.
Все это привело нас к мысли о необходимости создания единого подхода — шаблонного решения для массовой обработки сущностей.
Прежде всего следует отменить, что решение основано на стандартных подходах к проектированию, используемых в Directum RX, но объединяет их в удобный шаблон, который:
Рис.1. Упрощенная схема процесса
Ситуация: после миграции в RX было обнаружено, что у более чем 28 000 приказов на командирование отсутствуют данные на вкладках «Командируемые» и «Организации». Требуется заполнить их из Excel-файлов, предоставленных заказчиком.
Заполняем реквизиты карточки соответствующего процесса:
Рис.2. Пример вида карточки настройки процесса
После запуска процесс автоматически создает очередь и начинает обработку данных. Если при обработке возникли ошибки, в карточке будет отображаться уведомление.
Рис.3. Уведомление о наличии ошибок.
Можно посмотреть связанные очереди по действиям "Показать очередь" или "Показать список ошибок".
Рис.4. Список элементов очереди, связанных с настройкой
На вкладке Ошибки в карточке очереди можно перейти к деталям с указанием текста ошибки и посмотреть Stack trace. А кликнув по ссылке с идентификатором, можно открыть карточку объекта, при обработке которого возникла ошибка:
Рис.5. Вкладка с ошибками карточки очереди
После устранения причины ошибки, элемент очереди можно вернуть в работу действием «Возобновить». Повторно будут обработаны только записи, в которых были ошибки.
Для создания нового обработчика, на рабочем слое своего решения требуется создать справочник-наследник от абстрактного предка из шаблона ProcessSettingBase («Настройка процесса»). При необходимости можно расширить форму дополнительными свойствами — например, свойства-ссылки на документы с файлами, как в рассмотренном выше случае. Далее потребуется переопределить несколько методов, необходимых для кастомизации логики:
/// <summary>
/// Получить все сущности.
/// </summary>
public override IQueryable<Sungero.Domain.Shared.IEntity> GetAllEntities()
{
// Вернуть результат метода GetAll() типа сущности, который будет в обработке.
}
/// <summary>
/// Получить сущности для обработки.
/// </summary>
public override IQueryable<Sungero.Domain.Shared.IEntity> GetEntitiesForProcessing()
{
// Ваша логика получения объектов (наследников IEntity), которые требуется обработать
}
Опционально, вместо метода GetEntitiesForProcessing можно перекрыть метод GetEntitiesIdsForProcessing, возвращающий IQueryable<long >, если получение идентификаторов сущностей предполагается не из репозитория, а ,например, из файла, как в примере выше.
/// <summary>
/// Обработать сущность.
/// </summary>
/// <param name="entity">Сущность для обработки.</param>
/// <param name="_logger">Преднастроенный экземпляр логгера с контекстом (settingId, processId).</param>
public override void ProcessEntity(Sungero.Domain.Shared.IEntity entity, Sungero.Core.ILogger _logger)
{
// Ваша логика обработки, которая должна применятся к каждому объекту
}
Чтобы ускорить работу, мы рекомендуем сразу добавлять инициализацию записи настройки процесса с предустановленными параметрами по умолчанию.
1. Абстрактный справочник-конфигуратор «Настройка процесса» — содержит настройки. Вот основные:
2. Параметр для хранения глобального значения максимального количества потоков в базе данных.
3. Фоновый процесс — отслеживает активные настройки, анализирует очереди, при необходимости формирует новые и запускает обработчики с учетом заданных лимитов.
4. Асинхронный обработчик — выполняет обработку очереди, фиксирует ошибки. Повторная обработка выполняется новым экземпляром, чтобы исключить неконтролируемый рост количества потоков.
5. Обработка ошибок и блокировок — реализованы в базовом методе. Обработка конкретной записи сущности вынесена в виртуальный метод, обязательный для перекрытия.
6. Детали выполнения процессов — очереди обработки, их статусы, время обработки и наличие ошибок можно увидеть по действию из карточки настройки процесса.
7. Гибкое обращение с ошибками — ошибки, возникающие в процессе обработки сущности, не прерывают основной процесс, а текст ошибок с привязкой к ИД сохраняется в таблицу внутри экземпляра очереди.
8. Контроль повторных ошибок — ошибки, не связанные с блокировками, которые превысили допустимое количество повторных попыток, исключаются из дальнейших обработок. После анализа и устранения причин ошибок обработку этих записей можно запустить вручную действием «Возобновить» прямо из карточки.
Важно отметить, что наше решение не ускоряет прямое выполнение кода обработки одной сущности. Абстрактная производительность одного потока остается прежней. Поэтому тут не будет прямого сравнения скорости выполнения, но зафиксированные значения по реальным кейсам приведем:
Бизнес-логику обработки сущности разработчик все равно пишет сам, и этого не избежать. Но наше решение берет на себя всю инфраструктурную нагрузку, которая отнимала львиную долю времени при создании каждого нового обработчика.
Что есть уже «из коробки» и не требует затрат:
Разработчику остается только унаследоваться от готового справочника и реализовать ключевые методы с бизнес-логикой. Как показала практика, это сокращает время разработки нового сценария массовой обработки с 1-2 дней (с нуля) до 2-3 часов (настройка по шаблону). Таким образом, если и считать выигрыш в «производительности», то он заключается не в миллисекундах на одну запись, а в часах и днях, сэкономленных на разработке, отладке и доведении процесса до полного завершения.
1. Контроль нагрузки: управление потоками позволяет эффективно использовать ресурсы и защищает сервер от перегрузок.
2. Прозрачность и самообслуживание: веб-интерфейс предоставляет полную информацию о ходе выполнения и детализацию ошибок с привязкой к конкретному объекту. Это исключает цепочку взаимодействия "пользователь → администратор → анализ логов". Поэтому ответственный сотрудник может самостоятельно увидеть суть проблемы.
3. Экономия времени: разработчику требуется около 2 часов, чтобы подключить новый процесс обработки.
4. Надёжность: встроенный механизм обработки блокировок и ошибок обеспечивает отказоустойчивость.
Идеально для задач, где требуется обработка десятков и сотен тысяч сущностей через объектную модель RX после миграций или для проведения массовой обработки.
Не актуально в процессах, где объектов для обработки немного, либо с задачей более эффективно справятся SQL-скрипты.
Опыт компании СТАРКОВ Групп показал: массовая обработка исторических данных после миграций — это задача, которая требует не точечных решений, а системного подхода. Созданный нами шаблон позволяет превратить трудоемкий и рискованный процесс в управляемый, прозрачный и предсказуемый инструмент.
С кодом самого решения можно ознакомиться по ссылке на github
Авторизуйтесь, чтобы написать комментарий