|
Professor Seleznov
|
Сокращения
| Книга |
Вся данная работа делается ради проверок идей изложенных в книге «Искусство неизменяемой архитектуры: теория и практика управления данными в распределенных системах». Далее по тексту если указано слово «книга» и не названо ее имя, подразумевается книга «Искусство неизменяемой архитектуры: теория и практика управления данными в распределенных системах». |
Ссылки на предыдущие части В прошлых частях цикла
Были описаны технологии, используемые в этом примере (Spring Boot, Kotlin, H2, Python, FastApi). В первой статье был проверен самый простой пример обработки запросов на статус документа, описаны плюсы и минусы технологии неизменяемой архитектуры. Во второй части было описано решение для аутентификации с помощью закрытых/открытых ключей. Введение Получив рабочую инфраструктуру для решения задач и проверив работоспособность технологий, теперь создадим менеджер обработки бизнес-процесса на основе выбранных технологий и архитектуры неизменяемых данных. Для реализации выбран абстрактный бизнес-процесс запроса абстрактных ресурсов. Пример придуман максимально общим. Необходимо реализовать обработку бизнес-процесса с несколькими стадиями. Цель Убедиться, что выбранная архитектура позволяет решать задачи управления бизнес-процессами проще, чем обычные архитектуры на основе обновления записей в таблице. Описание процесса Процесс описывает этапы получения абстрактного ресурса через подачу заявок и прохождения заявки по этапам. Диаграмма процесса показана на рисунке ниже.
 В процессе обработки бизнес-процесса будут создаваться внутренние документы в БД, описанные на рисунке ниже. Направление стрелок идет от подчиненного документа к инициирующему документу или, другими словами, документу предку. Так рекомендует книга.
 В процессе обработки процесса он меняет свой статус. На рисунке ниже описаны статусы процесса.
 И вот на этом месте мы получаем важный момент. В рамках технологии неизменяемых данных мы не можем обновлять какое-то место в базе данных, где будет показан текущий статус процесса. В рамках книги предлагается вариант расчета статуса процесса при каждом запросе. В книге есть отдельная глава, как корректно и быстро обновлять кеш, если будет решено выгружать текущие статусы в какие-либо «витрины» данных. На данном этапе кеш строиться не будет. В текущем решении статус процесса записывается в последний документ процесса. Последним документом процесса является документ, у которого нет других подчинённых документов. Т. е. последний документ указывает на статус всего процесса. Конечно, это сильное ограничение технологии: процесс с несколькими параллельными ветками обработки не сможет иметь чёткого статуса. Этот нюанс и то как его обходить описан в книге; в рамках данного цикла статей сделано допущение, что такое решение корректно. Техническая реализация Статусы/документы процессов хранятся в ResourceRequestDocument. Перечисленные выше статусы описаны в перечислении ResourceRequestStatus. Связь статусов делается через поле parentDocumentHash. Для обеспечения производительности для полей поиска созданы индексы. Обработка процесса сделана в сервисе ResourceRequestServiceAsync. Эмуляция сделана на основе механизма сообщений, встроенного в Spring Boot. При получении первого сообщения делается отправка сообщения о необходимости обработки процессов. При обработке сообщения, если был выставлен не FINISHED статус, делается повторный вызов обработчика статусов. Поиск не законченных процессов сделан через поиск документов без наследников, кроме документов в статусе FINISHED. Чтобы приблизить тестовое решение к реальной жизни, для каждого этапа, где делается выбор предусмотрена возможность с 10%-ной вероятностью получить статус ошибки и закончить процесс, не пройдя полный цикл. Пример начального запроса приложен в проекте. Поле "request-id" не обязательно должно быть, как в примере, датой - это может быть любая уникальная строка. Для того чтобы получить информацию о всех этапах определённого бизнес-процесса, можно использовать SQL запрос с CTE. Пример можно найти в проекте. Возврат результатов SQL CTE в отформатированном виде выходит за рамки текущего цикла статей. При разработке обнаружено ограничение H2 базы данных, которое вынудило приводить к varchar поля сравнения, для построения иерархии. Технологии тестирования Для тестирования создан Python FastAPI-скрипт. Скрипт в 20 потоков через asyncio отправляет заданное количество запросов к серверу работы с бизнес-процессом. Итоговое состояние базы данных после обработки всех процессов проверяется через скрипт поиска незакрытых процессов. Для проверки результатов с помощью табличного редактора создан скрипт выгрузки времени начала процесса, времени окончания процесса и длительности работы процесса в миллисекундах. Результаты тестирования решения Тестирование решения проводилось с нагрузкой по созданию 200 тысяч процессов при запуске и сервера, и тестирующего скрипта на одном компьютере Intel Core i5-10310U. 200 тысяч процессов были обработаны примерно за 4.5 минуты. Результат удивил. Ожидалось, что при увеличении количества процессов в БД будет падать и время их обработки. Собственно, было интересно узнать, с какой скоростью падает производительность при росте количества процессов, которое в данной архитектуре - суть количество этапов или документов одного процесса. Однако результат получился обратный. Замедление есть на первых секундах работы. После чего приложение выходит на одну скорость обработки процесса. Т. е. скорость выборки данных из БД не изменяется при увеличении количества записей до примерно 1.2 миллиона записей о статусах процесса. Я решил запустить тестирование на 400 тысячах записей. 400 тысяч процессов дали 2 465 830 записей в таблице статусов/документов. Обработка заняла примерно 9 минут Результат оказался таким же: замедление на старте и примерно одинаковая скорость работы дальше. Замедления в середине связаны с тем, что я делал запросы к БД в момент работы скрипта тестирования. Результат для 400 тысяч процессов, в виде графика, показан на рисунке ниже. Как видно на графике, в начале работы приложения скорость обработки одного процесса может составлять до 8 секунд, однако уже через несколько секунд скорость обработки одного процесса падает до менее чем 10 миллисекунд, после чего остаётся на этом уровне. Замедления происходят если обратиться к БД с каким-то «другим» запросом. Анализ причин такого странного поведения выходит за рамки данного цикла. Предполагаю, что работает оптимизация в JVM и использование индекса для поиска данных в БД.
 Выводы. Выводы сделаны при сравнении данного решения, которое похоже «сферического коня в вакууме» с не существующим решением, которое делали бы «как обычно». Решение «как обычно», скорей всего, имело бы 2 таблицы. Одну таблицу с описанием процесса и его статусом. И еще одну таблицу с историей. В обычном решении история, наверное, была бы похожа на таблицу RESOURCE_REQUEST_DOCUMENT из текущей реализации. Однако таблица со статусами постоянно обновлялась бы через update для отслеживания своего состояния. Сравнивая эти два подхода, сразу видно, что при использовании неизменяемой архитектуры, приложение получилось проще. Есть одна таблица которая содержит всю нужную для работы информацию. Опасения что при росте объема данных в таблице истории будет наблюдаться падение производительности не подтвердились. Даже при наличии более чем 2 млн записей в таблице истории, скорость работы с данными остается одинаковой и падения производительности не наблюдается. Опасения, что из одной таблицы будет сложно получить полный, цикл также не подтвердились. Скрипт построения истории обработки бизнес-процесса получился не сложным, и понятным. Данный скрипт в H2 работает медленно, для таблицы RESOURCE_REQUEST_DOCUMENT с 2.4 млн. записей построение истории занимает примерно 14 секунд. Данное поведение связанно с фильтром cast(c.parent_document_hash as varchar) = p.json_hash, который не позволяет использовать индекс. Это ограничение БД H2. Конечно, данное решение сделано на основе in-memory БД. При использовании более «классических» БД результат мог бы быть другим, но даже этот результат позволяет сказать, что использование технологий неизменяемой архитектуры, описанных в книге «Искусство неизменяемой архитектуры: теория и практика управления данными в распределенных системах» дает выигрыш в простоте и производительности.-Источник
|