Mam do ogarnięcia długi proces, który wykonuje się kilka godzin codziennie i startuje o konkretnej godzinie.
Mam kilkanaście paczek faktur i innych dokumentów finansowych, od 0,2mln. do 2mln. w zależności od rozmiaru paczki.
Proces taki to wykonanie jedno po drugim wywołań procedur, api, stworzenie gdzieś na zasobie pliku itd..
np. taki proces dla jednej paczki (a trzeba odpalić ten proces dla kilku paczek):
- Pobierz paczkę ->
- przepuść przez zewnętrzne API, które odfiltruje i posortuje dokumenty ->
- odpal procedurę ->
4 Jeśli typ paczki==A to odpal kolejną inną procedurę w bazie, w innym przypadku idź do kolejnego kroku ->
5 Przepuść dokumenty przez OCR w chmurze =>
6 Przepuść dokumenty przez api, by uzyskać wyliczenie i notę dla dokumentów =>
7 Dołącz z poprzedniego kroku dane do dokumentów =>
9 Wywołaj api, które stworzy wrzuci dokumenty z raportem w odpowiednie miejsca w systemie
To tylko przykładowo jak proces dla jednej paczki może wyglądać by przedstawić problem.
Mamy już system, który realizuje coś podobnego i mamy po prostu pętlę z paczkami, a w środku pętli wszystkie kroki dla danej paczki wywołują się synchronicznie.
Kolejne kroki procesu dla jednej paczki muszą się wykonywać jeden po drugim, bo każdy krok bazuje na poprzednim, wyjątki są małe i tutaj zwykłe Task.WhenAll wystarcza, zwłaszcza że wszystkie kroki procesu wykonują się długo ale w zewnętrznych systemach i trzeba tylko poczekać.
Aplikacja więc, którą muszę stworzyć jest takim jakby orkiestratorem całości i puszcza jeden krok po drugim wykorzystując zewnętrzne systemy na które nie mam wpływu poza parametrami ustawionymi w naszym panel adminie dla każdej paczki. Chciałbym żeby biznes też mógł widzieć na którym kroku procesu która paczka jest obecnie i nie wiem czy załatwi mi to jakiś framework przy okazji, czy ręcznie w bazie tą informację po zakończeniu każdego kroku zrobić.
Kolejnym problemem jest fakt, że najlepiej byłoby puścić proces dla wszystkich paczek równolegle, ale wiele z tych kroków może w danym czasie być odpalony tylko dla jednej paczki! A niektóre już można równolegle puścić i zyskać masę czasu. Co więcej kolejność przetwarzania paczek jest bardzo ważna! np. Paczka Id2 nie może wejść w krok 6 przed Paczką Id1, nawet jeśli Paczka Id2 wszystkie równoległe kroki przeszła szybciej.
Np. zamiast:
Paczka Id1: Krok1->Krok2->...->Krok9
Paczka Id2: Krok1->Krok2->...->Krok9
Paczka Id3: Krok1->Krok2->...->Krok9
Paczka Id4: Krok1->Krok2->...->Krok9
puścić to by wykonało się mniej więcej np. tak:
Paczka Id1: Krok1 -> Krok2 -> Krok3 -> Krok4 -> Bardzo długi Krok5 -> Krok6
Paczka Id2: Krok1 -> wait -> Krok2 -> Krok3 -> Krok4 -> Bardzo długi Krok5 -> Krok6
Paczka Id3: Krok1 -> wait -> Krok2 -> Krok3 -> Krok4 -> Bardzo długi Krok5 -> Krok6
Aż samo się ciśnie wykorzystanie kolejek, po prostu niektóre kroki mogłyby przyjąć tylko jedno zadanie na raz, inne wiele zadań równolegle, ale jak ogarnąć kolejkę by nie przyjęła obsługi paczki Id2 jeśli wcześniej nie obsłużyła paczki Id1? Pewnie sprawdzać przy przyjęciu Paczkę Id2, sprawdzić wtedy status paczki Id1, czy już jest na następnym kroku czy przed, a jeśli przez do Paczkę Id2 wrzucić na kolejkę obok by zrobiła retry za minutę i znowu puściła do pierwszej kolejki. Oczywiście oprócz tego ogarnięcie co się dzieje jak na procesie którejś paczki
Sprawa skomplikowana, ale nie wiem czego użyć za bardzo. Może to ogarnąć w jakiś silnik BPMN, skorzystać z kolejek do tego, może dobry by tu był stack particular z Saga i NServiceBus (ale nie temat wymaga spędzenia do rozpoznania masę godzin, za nim się zorientuję czy byłoby to dobre do użycia narzędzie), czy może dużo więcej samemu napisać kodu do obsługi i się pobawić MediatR i Hangfirem (Hangfire wykorzystywałem tylko do odpalenia joba wg skonfigurowanego crona i nie wiem czy to narzędzie, które ogarnie taki workflow jobów ze stepami)
Miał ktoś styczność z podobnymi problemami?