Привет, Хабр! Меня зовут Егор, я бэкенд-разработчик в T-Банке, участвую в разработке продуктов комплаенса. Недавно в одном из наших проектов мы столкнулись с проблемой низкой производительности Camunda — и хотим поделиться опытом, который мы получили в процессе ее решения.

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

Какая была проблема

Сервис, который мы разрабатываем, занимается актуализацией данных клиентов банка. В основном это создание задач во внутренней CRM для сотрудников операционной линии и информирование клиентов — нам нужно передать актуальные документы. Если клиент вовремя этого не делает, мы накладываем на него ограничения, потому что из-за такого клиента мы рискуем получить санкции от регулятора.

В качестве основы приложения используется bpmn-движок Camunda, который встроен в контекст Spring. Мы поддерживаем 13 bpmn-процессов, и их количество растет. Сервис слушает топики в Kafka с данными компаний, для которых необходимо провести актуализацию, и запускает по ним экземпляры процессов в Camunda.

Обычно в день мы обрабатываем около 50 тысяч процессов — это небольшая нагрузка для Camunda, с которой она справляется без особых усилий. Были случаи, когда в топик отправляли по миллиону сообщений в день и мы успевали их обрабатывать по мере поступления. Даже сложилась уверенность, что наш сервис может справиться с любым количеством процессов.

Но уверенность быстро пропала, когда произошла очередная массовая выгрузка данных в один из топиков. Нам отправили всего 700 тысяч событий, но если раньше их присылали со скоростью 10 сообщений в секунду, то в этот раз топик наполнялся значительно быстрее, чем мы успевали читать.

Все экземпляры нашего сервиса упорно разгребали свои партиции и за пару часов перевели все события из топика в процессы внутри Camunda. В итоге на момент вычитки последнего события в базе данных находилось более 500 тысяч активных процессов.

И тогда мы столкнулись с рядом проблем:

  • В пять раз упала скорость обработки. Сильно деградировал запрос на поиск Job’ов из-за большого числа записей в таблице act_ru_job.

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

  • Затормозили критичные процессы на других схемах. На тот момент в конфигурации не были выставлены приоритеты выполнения процессов, и Camunda обрабатывала их в порядке общей очереди. Из-за этого критичные процессы стали ждать, когда до них дойдет очередь из 500 тысяч менее важных процессов.

  • Пропала возможность массово остановить, удалить или перезапустить процессы, потому что их скопилось такое количество, что любое действие над ними в админке приводило к ошибке переполнения памяти в самом сервисе или в БД. В Camunda API есть возможность запустить асинхронный процесс удаления процессов, но мы не могли его использовать, потому что это работало очень медленно. Быстрее было дождаться, пока все процессы доработают сами.

  • Высокая нагрузка на БД. Даже когда мы смогли приостановить все процессы, нагрузка на CPU базы данных держалась в районе 60% в простое. Поиск Job’ов в БД запускался постоянно и грузил базу тяжелыми запросами.

Чем больше активных процессов было в БД, тем сильнее Camunda тормозила. Приблизительный график падения производительности:

Уже при 100 тысячах активных процессов производительность упала в два раза.
Уже при 100 тысячах активных процессов производительность упала в два раза.

Какие решения рассмотрели

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

Разработчики Camunda в новой версии решили проблему бутылочного горлышка переходом на Zeebe — их новый движок bpmn-процессов, который более устойчив к большим нагрузкам, в первую очередь за счет горизонтального масштабирования. Мы не рассматривали переход на Camunda 8, поэтому искали более простые варианты решений.

Большинство советов от Сamunda-комьюнити делятся на две категории:

  1. Стараться не допускать накопления активных процессов.

  2. Пытаться оптимизировать запрос на получение Job’ов из базы данных.

С советами из первой категории все просто: нужно стараться не допускать большого количества активных Job’ов в БД. Например, отказаться от таймеров на схемах и реализовать их вручную в отдельной таблице.

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

Заполнение даты выполнения для всех Job’ов. В документации Camunda авторы предлагают включить настройку ensureJobDueDateNotNull для оптимизации запроса на получение Job’ов из БД.

В таблице act_ru_job есть столбец duedate, который содержит дату, при наступлении которой этот Job можно будет выполнить. Но если указать в нем значение null, то Camunda выполнит его с максимальным приоритетом. Настройка ensureJobDueDateNotNull позволяет отказаться от включенной по умолчанию функции, но взамен будет выполнять более оптимальный запрос на чтение Job’ов из БД.

@Configuration
class ExtendedProcessEngineConfiguration() : DefaultProcessEngineConfiguration() {
    override fun preInit(configuration: SpringProcessEngineConfiguration) {
        configuration.isEnsureJobDueDateNotNull = true
    }
}

Результаты:

Чуда не произошло, процессы продолжали накапливаться и завершаться за несколько часов. Но видно, что включение этой настройки ускорило запрос на получение Job’ов и увеличило скорость их обработки.

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

Rate Limiter для запуска новых процессов. Когда выяснилось, что сервис запускает новые процессы быстрее, чем завершает старые, стали искать способы, как ограничить скорость создания процессов. Для этого было решено прикрутить Rate limiter в виде готовой реализации от Resilience4j. В настройках поставили ограничение на семь процессов в секунду, то есть два экземпляра приложения в сумме за секунду могли запустить не больше 14 процессов.

Пример конфигурации лимитера от Resilience4j:

resilience4j.ratelimiter:
  limiters:
    process-start:
      limitForPeriod: 7
      limitRefreshPeriod: 1000ms

Вешаем аннотацию @RateLimiter на метод, в котором описана логика запуска процесса. Пример использования:

@RateLimiter(name = "process-start")
fun startProcess(): ProcessInstance {
    // TODO этот код будет запускаться не чаще 7 раз в секунду
}

Результаты:

Хотя в этом решении нет никаких оптимизаций, результат довольно ощутим.

Оказалось, что, если запускать новые процессы дозированно, время обработки всех процессов будет меньше на 30%. Это происходит из-за того, что у Camunda появляется время на обработку прошлых процессов — и они не успевают накапливаться.

В итоге получили красивое число 14 в метрике скорости полной обработки процессов, что равно границе в Rate limiter, которую мы выставили в конфигурации.

Но есть и минусы:

  1. Сложно с ходу подобрать границу для лимитера, поэтому процессы могут продолжать накапливаться, если выставить слишком большой порог. И наоборот, при выставлении низкого порога обработка может быть сильно медленнее, чем позволяет Camunda. Это проблема, если для топика установлена retention-политика с коротким сроком жизни сообщений.

  2. Граница лимитера привязана к количеству слушателей топика. Нужно будет менять настройки лимитера каждый раз, когда изменится число партиций или экземпляров приложения.

  3. Границу для лимитера нужно устанавливать индивидуально для каждого слушателя. Так как в зависимости от размера bpmn-схемы пропускная способность для каждого процесса может сильно отличаться.

Изменение конфигурации Camunda. В Camunda есть несколько настроек, которые позволяют менять скорость обработки Job’ов. В нашем случае нужно было сократить количество запросов на чтение, поэтому решили увеличить размер очереди у JobExecutor.

Конфигурация JobExecutor:

camunda:
  bpm:
    job-execution:
      max-jobs-per-acquisition: 20
      core-pool-size: 10
      max-pool-size: 20
spring.datasource.hikari.maximum-pool-size: 10

max-pool-size — максимальный размер пула потоков, который обрабатывает очередь Job’ов. По умолчанию стоит значение 10, увеличили его в два раза, чтобы очередь обрабатывалась быстрее.

max-jobs-per-acquisition — настройка, которая указывает, сколько Job’ов будет получено за один запрос в базу данных. Также она устанавливает максимальный размер очереди для JobExecutor. По умолчанию стоит значение 3, мы увеличили его до 20, что позволило сократить число запросов на чтение из БД.

spring.datasource.hikari.maximum-pool-size — оставили значение 10, чтобы пул соединений БД был в два раза меньше пула JobExecutor’а.

Увеличение пула ускорило обработку и дало неожиданный сайд-эффект: Camunda перестала запускать новые процессы, пока не завершались старые.
Дело в том, что, когда мы увеличили пул потоков у JobExecutor, он стал занимать весь пул соединений БД. Это привело к тому, что слушателю kafka-топика приходилось постоянно ожидать свободного соединения из-за голодания пула.

Процессы перестали накапливаться, и 99% из них успевали завершиться за 50 секунд. Это изменение решило исходную проблему, но привело к другим, которые заставили нас отказаться от этого решения:

  1. Из-за голодания пула соединений в случайном месте приложения стали возникать ошибки с нехваткой свободного соединения. Это сделало бы сервис слишком непредсказуемым при большой нагрузке.

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

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

Движок Camunda работает так: схема процесса делится на исполняемые отрезки (JobDefinition), которые в документации движка называются транзакциями. Границы этих транзакций можно определять, указывая на элементах схемы признаки asyncBefore/asyncAfter.

Например, выставленный признак asyncAfter=”true” у ServiceTask означает, что после обработки этого элемента сохранится состояние процесса в БД. И выполнение этого процесса продолжится только после того, как очередь у JobExecutor снова дойдет до этого процесса.

Если на схеме не будет ни одной такой точки сохранения, процесс будет выполняться полностью in-memory и в случае ошибки или рестарта сервиса полностью исчезнет. И наоборот, если везде расставить такие точки сохранения, каждое действие будет записано в БД и каждый раз будет ждать очереди в JobExecutor.

Если не указать приоритеты схем или отдельных Job’ов, по умолчанию Camunda будет обрабатывать их по алгоритму round-robin, то есть будет проходить все процессы по кругу и выполнять по одному Job у каждого процесса.

Например, если в БД находится 100 тысяч активных процессов, то, чтобы продвинуть один из них дальше по схеме, нужно ожидать, пока Camunda обработает 100 тысяч Job’ов в других процессах. Получается, при скорости обработки 100 Job’ов в секунду процесс будет ждать своей очереди примерно 16 минут на каждый Job. Если схема процесса состоит из десяти Job’ов, суммарное время жизни процесса будет примерно 160 минут.

Ради эксперимента решили проверить, насколько сильно вырастет производительность, если сократить количество Job’ов на схеме в два раза. Обычно мы ставим asyncAfter=”true” после каждого ServiceTask, чтобы избежать лишних перезапусков делегатов при ошибках, поэтому изначально в процессе было восемь таких сохранений. Убрали четыре из них, и вот что получилось:

По результатам видно, что количество Job’ов в схеме почти линейно влияет на скорость обработки процесса. Поэтому при разработке схемы стоит осознанно подходить к расстановке границ транзакций. Привычка везде расставлять asyncBefore/asyncAfter может привести к увеличению времени обработки процесса в разы.

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

Замеры производительности

Для проверки гипотез мы провели нагрузочное тестирование: в топик с двумя партициями в очень короткий срок отправлялось 200 тысяч сообщений со средней скоростью 80 rps. Само приложение было развернуто в Kubernetes в двух экземплярах. После каждого прогона полностью очищалась БД.

Все подходы тестировались независимо друг от друга, и их итоговые результаты сравнивались только с исходными показателями.

Пройдемся по всем метрикам, которые мы собирали.

Время обработки всех процессов — за какое время сервис обработает все события из топика. Эти показатели могут быть критичны для бизнеса.

Время вычитывания всех сообщений из топика — если в описании топика указан retention на время хранения сообщений, они могут быть удалены до того, как будут прочитаны.

Максимальный consumer-lag топика — ситуация аналогична метрике выше: у топика может быть установлен retention на размер в байтах, поэтому нужно следить, чтобы сервис успевал вычитывать события до их удаления.

Максимальное количество активных процессов — как мы выяснили, большое число активных процессов в БД сильно замедляет производительность Camunda, так что чем их меньше, тем лучше.

Скорость обработки Job’ов в секунду — основная метрика производительности движка Camunda. Показывает, сколько Job’ов было обработано за 1 секунду. Были взяты 5-й и 15-й перцентили, чтобы были видны максимальные просадки в производительности.

Скорость обработки процессов в секунду — показывает, сколько процессов завершается за 1 секунду. Логично предположить, что если за секунду создается больше процессов, чем завершается, то активные процессы начинают накапливаться.

Время выполнения запроса на получение Job’ов из БД в секундах — техническая метрика, измеряющая время, за которое JobExecutor получает новые джобы на исполнение. Для сбора метрики был создан отдельный scheduled-метод, который вызывал компонент движка, ответственный за поиск Job’ов, и замерял время работы всей функции.

Время жизни процесса — показывает, сколько времени процесс находился в активном состоянии, то есть исполнялся движком Camunda. Зависание процесса может быть неприемлемо с точки зрения бизнеса, также могут сработать лишние таймеры на схеме, поэтому важно, чтобы процесс выполнялся максимально быстро.

Чтобы собрать метрику, при старте процесса в него записывалась переменная с временем создания, а в конец процесса был добавлен слушатель (ExecutionListener), который считал, сколько времени прошло до завершения процесса.

Скрытый текст

Что получилось

Подведем итоги:

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

  2. RateLimiter решил исходную проблему, но нам пришлось отказаться от него — мы посчитали его сложным в сопровождении, потому что требуется постоянное внимание к его конфигурации.

  3. Повышение пула и очереди у JobExecutor тоже решило исходную проблему, но привело к нестабильности приложения при нагрузке и куче ошибок в логах. От этого решения также пришлось отказаться.

  4. Последний способ нам тоже не подошел, потому что он не решил полностью проблему и мы не готовы сокращать количество сохранений процессов в БД.

К сожалению, ни одно из быстрых решений не дало нам желаемого результата, поэтому мы решили выделить больше времени на решение этой проблемы. Приняли решение, что будем внедрять паттерн Message Inbox для входящих сообщений из Kafka. О том, что из этого получилось расскажу в следующей части.

Надеюсь, что эта статья была вам полезна. Буду рад обратной связи: возможно, вы тоже сталкивались с описанной проблемой и у вас есть опыт, которым можно поделиться.

Комментарии (13)


  1. deniskiyanitsa
    30.08.2024 13:52
    +1

    Очень жду следующей части!


  1. sergey-kz
    30.08.2024 13:52

    Интрига


  1. vagon333
    30.08.2024 13:52

    А какую базу использовали?
    И пробовали-ли оптимизировать базу?
    С ваших слов, при большом количестве задач поиск в базе занимал значительное время.
    Может быть оптимизация поможет?

    не откусывай больше, чем можешь проглотить

    Глотаете не жуя? :)
    Корректная фраза: bite more than you can chew - укусил больше, чем можешь пережевать.


    1. Camundaslav Автор
      30.08.2024 13:52

      Спасибо за комментарий! Да, мы пробовали настроить более агрессивный автовакуум, чтобы почаще чистить мусор(у нас Postgres), но результат нас не порадовал: прирост был всего на пару процентов, то есть в рамках погрешности

      Думали над добавлением новых индексов, но с этим возникли сложности:

      1. В Camunda из коробки уже есть все необходимые индексы на таблицу act_ru_job, здесь мы не нашли возможностей для улучшения. Но возможно они есть, и мы плохо искали)

      2. Сложно комплексно мониторить перфоманс при добавлении индексов. Например, если у нас получится ускорить чтение Job'ов, мы случайно можем резко замедлить вставку при батчевых операциях, таких как миграции на новую версию схемы

      3. Мы скоро планируем добавить приоритезацию Job'ов, и она потребует замены индексов в act_ru_job. Есть риск вернуться к проблеме, так как запрос на поллинг Job'ов изменится

      А вот по фразе из заголовка мной было проведено отдельное расследование на эту тему еще перед публикацией)

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


  1. Debrainer
    30.08.2024 13:52

    Моё мнение, вы просто ошиблись с архитектурой решения и не учли потенциальную нагрузку с учетом роста. По факту просто уперлись в производительность постгреса. Даже если вы сейчас потюните что-то, то это спасёт только на время. При подаче нагрузки ещё x2 у вас опять всё сляжет. Тут или использовать in-memory оркестраторы бп с такими же бд или всю оркестрацию бизнес процессов строить самим на базе Кафки.

    Слышал, что сбер сделал свой bpmn движок platform v flow как раз на базе Кафки избавившись от бд, где все состояния бп в кафке и хранятся. Видимо тоже уперлись в бд...


    1. Camundaslav Автор
      30.08.2024 13:52

      Спасибо за комментарий! Здесь наверное не соглашусь с тем, что выбор Camunda был ошибкой. Дам немного контекста по приложению:

      • 15 независимых между собой схем-процессов

      • Процессы часто переиспользуют код из соседних процессов. Все общие части мы выносим в отдельные схемы и вызываем через CallActivity.

      • Схемы постоянно меняются из-за изменения требований бизнеса.

      • Часто на схемах можно встретить возврат состояния на предыдущие шаги процесса

      • Стандартная нагрузка примерно 50 тысяч процессов в день. Массовые выгрузки на миллионы это скорее редкость или ошибка

      Если отказаться от Camunda как оркестратора, нам бы пришлось в коде фиксировать все переходы и состояния. Это бы заняло в разы больше времени на мой взгляд

      А заменить камунду особо не на что, могу ошибаться, но по-моему достойных аналогов у нее сейчас нет. Остается только выбирать между 7 и 8 версиями)

      Про железки кстати у нас недавно был интересный кейс:

      При ресурсах Postgres в 1 CPU и 2 RAM у нас использование CPU держалась стабильно в диапазоне 20-30% в простое. Но когда увеличили ресурсы в 2 раза, нагрузка опустилась до 1%. Глубоко в эту тему не копали, но есть предположение, что Postgres смог выгрузить все данные в память и не ходить на диск при чтении


  1. vdshat
    30.08.2024 13:52

    Спасибо за статью со столь нлубоким описанием.

    Вы правы, данная ситуация не уникальна и многие с ней сталкиваются.

    1. Вы правы, настройка должна быть включена. Можно еще принудительно проверять dueDate в диаграммах перез заливкой в репозиторий.

    2. В настройках Kafka слушателя уже есть поддержка мультипоточности и максимального количества потоков. Настройка слушателя необходима, т.к. всегда есть аппаратные ограничения.

    3. Нужно блюсти баланс и, вы правы, просто увеличение пула не решает проблемы

    4. А вот здесь, как раз, и кроется основная проблема и основнаое решение - нужно уменьшить количество висящих процессов. Супер база и железо быстрей обработает ваши сотни тысяч процессов, но и их возможности ограничены. Технические решения могут только на время улучшить картину, но не решат логические ошибки.

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

      1. При увеличении количества сотрудником количество процессов будет расти

      2. При завершении и старте нового периода количество процессов может удваиваться

      3. Если нужно передеплоить новую версию процесса, например, нашли ошибку в логике, то получаем тот же массовый эффект.

      4. Сама по себе поддержка сотен тысяч процессов затратное дело.

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

        Основной совет - не смешивать реалтайм и обработку с таймерами в одном процессе.


    1. Camundaslav Автор
      30.08.2024 13:52

      Спасибо за подробный комментарий! Рад что статья собирает людей, кто уже имел дело с этой проблемой, надеюсь это будет тот случай, когда комментарии будут полезнее самой статьи)

      Процесс по времени шлет уникальные сообщения на каждого сотрудника

      Можете здесь чуть подробнее объяснить, как вы шлете сообщения в нужное время? Мы просто такие вещи обычно делаем на таймерах , и я пока не представляю как без них обойтись в такой ситуации

      Или может есть какие-нибудь универсальные подходы по избавлению от таймеров, или здесь все индивидуально от сервиса к сервису?

      У нас вот много циклических ежедневных таймеров, и у меня была мысль заменить их ожидающий MessageEvent. И в приложении добавить простой советский Schedulled Job от Spring, который будет находить все активные процессы и коррелировать в них сообщения. Таблица act_ru_job сильно похудеет, так как процесс висящий на ожидании сообщения не создает джобу


  1. dunyashin
    30.08.2024 13:52

    Добрый день! Большое спасибо за статью, было интересно прочитать. Если я правильно понял, то вы используете делегаты, в таком случае действительно не помогает увеличение потоков в пуле без увеличения числа соединений БД, тк поток, если мне не изменяет память, забирает соединение целиком и полностью, пока не завершит джобу, так что прирост сложно ощутить.

    Рассматриваете ли вы переход с делегатов на external task? С ними и на 8 версию можно будет переходить) У нас была похожая проблема, мы ее решили в том числе переходом на external task, тк коннекты к БД были задействованы только для поиска задач и сохранения результата, а во время исполнения задачи освобождались. Замеряли потом нагрузочные тестированием и выявили, что таким образом можно кусать куда больше. В целом подход с внешним исполнением позволяет больше обрабатывать, но требует и большей зрелости, тк начинается пляска с блокировками задачами и идемпотентностью. Но бесконечно не замасштабироваться, тк в конце упремся в БД все равно. Где-то были записаны замеры, получали в районе х4-х10 при разных проблемах, но это уже тема для другой статьи)


    1. Camundaslav Автор
      30.08.2024 13:52

      Привет, спасибо за комментарий! Да, external таски нам советовали, слышал что некоторые команды внутри банка перешли полностью на них, но нам к сожалению пока не удалось с ними поработать

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

      Звучит действительно как крутая тема для отдельной статьи, по типу Delegate vs External Task с замерами по производительности. Было бы круто, если бы вы пошарили свой опыт по переходу на них, мы наверное не скоро до них доберемся


  1. Geoteg
    30.08.2024 13:52

    Не знаю насколько будет актуален этот совет, т.к. я только изучаю процессы, но видел видео с похожей проблемой у Avito. Это было связано с PgBouncer, но оно 3-х летней давности, возможно это уже не актуальное решение, тем не менее стоит посмотреть и там.
    Суть проблемы, соединение вытесняется из ConnectionPool в БД, на его место приходит новое и устанавливает connect, однако, старому объекту для отмены запроса нужно снова подключиться к БД в асинхронном режиме, чем больше запросов - тем больше таких соединений копится и лимит коннектов забивается этими "неактивными" объектами.
    Вот ссылка на видео в YouTube: Есть смысл посмотреть с 10 минуты или 13 минуты.
    https://www.youtube.com/watch?v=imfSiWFJrb0
    Заранее извиняюсь, если это не то, но, возможно, будет полезно.


  1. RobertRT
    30.08.2024 13:52

    Большое спасибо за статью.

    Пока читал в голове была мысль, что самым дешёвым и эффективным решением будет внедрить transactional inbox, читать сообщения из кафки, полезную нагрузку сообщения сохранять в БД без запуска процессов камунды. Далее отдельным обработчиком (на ShedLock'е) определять количество запущенных процессов, определять, какое количество новых процессов можно запустить, и запускать, основываясь на том, что было сохранено в БД ранее из кафки.

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

    Как я понял, вы пробуете именно этот вариант? Интересно будет почитать что у вас получится. Удачи!

    P.S. Что-то похожее было недавно у нас, но там проблема была не на старте, а на середине процесса где стоял таймер. При большом числе процессов весь пулл экзекуторов обрабатывал таймеры и новые процессы не создавались)


    1. Camundaslav Автор
      30.08.2024 13:52

      Спасибо за ваш комментарий! Да, вы угадали, именно так и планируем сделать, вплоть до деталей с шедлоком)

      Еще дополнительно добавим фильтрацию сообщений, т.к у нас бывает такое, что процессы запускаются только по 5% сообщений из топика. Сейчас эта фильтрация происходит в уже запущенном процессе, и хотим перенести её в обработчик входящих сообщений, чтобы запускать только действительно нужные процессы