Jedna transakcja shared a wielowątkowość

Jedna transakcja shared a wielowątkowość
AS
  • Rejestracja:ponad 4 lata
  • Ostatnio:7 dni
  • Postów:19
0

Cześć mam pytanko, nie wiem jak do tego podejść ale.

screenshot-20240204115107.png

Zdjęcie jak ma to wyglądać 😄 Mamy 2 tabelki z danymi (A i B)
Potrzebuje zrobić "snapshot" danych, dane A pozwolą mi stworzyć dane B (prawa strona) oraz muszą być zapisane do innej tabelki A (przekopiowane z A lewo do A prawo).
C tabelka również ma być przekopiowana.

Chciałbym to zrobić równolegle tylko problem pojawia się podczas współdzielenia transakcji.
Bo nie może istnieć sytuacja taka, że proces A się wykona (czyli zapisze do A i B) a C nie wykona się bo będzie jakiś error.
Jak wyczytałem z użyciem metody Transactional on tworzy sobie za każdym razem nową transkacje, jak próbowałem dawać parametr Mandatory (w pod metodzie) to dostawałem błąd że transakcja nie istnieje co by się zgadzało z opisem że nie jest dzielona.

Czy ktoś mógłby jakoś doradzić jak/czym albo jak sobie z tym poradzić ?

(Próbowałem to z użyciem CompletableFuture)

Dzięki za każdą poradę 😉

AK
  • Rejestracja:prawie 7 lat
  • Ostatnio:około miesiąc
  • Postów:3561
0

Brzmi to jak javowsko-springowy YX problem

@Transacinal to pole minowe
A pachnąca tu między wierszami rozproszona transakcja to nie mniejszy problem.


Bo C to najlepszy język, każdy uczeń ci to powie
RequiredNickname
tylko w przeciwieństwie do transakcji rozproszonych tego drugiego problemu z wszechobecnym jpa nie unikniemy w korpo projektach :(
YA
  • Rejestracja:prawie 10 lat
  • Ostatnio:3 dni
  • Postów:2370
0

Chciałbym to zrobić równolegle tylko problem pojawia się podczas współdzielenia transakcji.

Dlaczego równolegle? Co zyskujesz kosztem złożoności rozwiązania?

Poprawnie byłoby zbudować logikę, które obsługuje wykonanie Twojego workflow i mieć mechanizm, który jest w stanie wyszukać instancje workflow, które padły i wznowić ich wykonanie.
Zamiast

Kopiuj
activity1.execute();
activity2.execute();

coś w stylu

Kopiuj
MyBusinessProcess process = MyBusinessProcess(activity1,activity2);
process.execute();

Gdzie ten MyBusinessProcess zamyka maszyną stanową/workflowem, który jest w stanie "kontynuować wykonanie procesu zgodnie z logiką, której potrzebujesz" (trzeba to sobie oprogramować, można wykorzystać istniejące frameworki).

Kolejna sprawa, to piszesz, że równolegle... ale co z przeplotami wykonania? Piszesz o abstrakcyjnych tabelach A,B,C, ale gdyby C nie było w jakiś sposób związane z przetwarzaniem A,B to chyba problemu by nie było? Czy robi Ci różnicę kolejność w jakiej zadania rozpoczną pracę i w jakiej zakończą pracę i jaki będzie stan (zawartość) tabel A,B,C w określonym punkcie czasu?

np. możesz mieć takie przeploty (| - momenty rozpoczęcia/zakończenie zadania, - - wykonanie zadania)

Kopiuj
Task #1: |------------------------|
Task #2: |------------------------|

Task #1: |------------------------|
Task #2:    |---------------------|

Task #1: |------------------------|
Task #2:    |-------------------|
...

Przeplot#1 - Task#1 i #2 zaczynają i kończą się dokładnie w tym samym czasie (mało prawdopodobne ale mocno zależne od tego "jak i gdzie mierzymy czas")
Przeplot#2 - Task#1 zaczyna się wcześniej niż 2, ale kończą w tym samym momencie
Przeplot#3 - Task#2 zaczyna się później niż task#1, ale kończy wcześniej niż Task#1
...

edytowany 1x, ostatnio: yarel
AS
  • Rejestracja:ponad 4 lata
  • Ostatnio:7 dni
  • Postów:19
0
yarel napisał(a):

Chciałbym to zrobić równolegle tylko problem pojawia się podczas współdzielenia transakcji.

Dlaczego równolegle? Co zyskujesz kosztem złożoności rozwiązania?

Poprawnie byłoby zbudować logikę, które obsługuje wykonanie Twojego workflow i mieć mechanizm, który jest w stanie wyszukać instancje workflow, które padły i wznowić ich wykonanie.
Zamiast

Kopiuj
activity1.execute();
activity2.execute();

coś w stylu

Kopiuj
MyBusinessProcess process = MyBusinessProcess(activity1,activity2);
process.execute();

Gdzie ten MyBusinessProcess zamyka maszyną stanową/workflowem, który jest w stanie "kontynuować wykonanie procesu zgodnie z logiką, której potrzebujesz" (trzeba to sobie oprogramować, można wykorzystać istniejące frameworki).

Kolejna sprawa, to piszesz, że równolegle... ale co z przeplotami wykonania? Piszesz o abstrakcyjnych tabelach A,B,C, ale gdyby C nie było w jakiś sposób związane z przetwarzaniem A,B to chyba problemu by nie było? Czy robi Ci różnicę kolejność w jakiej zadania rozpoczną pracę i w jakiej zakończą pracę i jaki będzie stan (zawartość) tabel A,B,C w określonym punkcie czasu?

np. możesz mieć takie przeploty (| - momenty rozpoczęcia/zakończenie zadania, - - wykonanie zadania)

Kopiuj
Task #1: |------------------------|
Task #2: |------------------------|

Task #1: |------------------------|
Task #2:    |---------------------|

Task #1: |------------------------|
Task #2:    |-------------------|
...

Przeplot#1 - Task#1 i #2 zaczynają i kończą się dokładnie w tym samym czasie (mało prawdopodobne ale mocno zależne od tego "jak i gdzie mierzymy czas")
Przeplot#2 - Task#1 zaczyna się wcześniej niż 2, ale kończą w tym samym momencie
Przeplot#3 - Task#2 zaczyna się później niż task#1, ale kończy wcześniej niż Task#1
...

"Dlaczego równolegle? Co zyskujesz kosztem złożoności rozwiązania? "

Tylko po to żeby przyspieszyć proces.

"(trzeba to sobie oprogramować, można wykorzystać istniejące frameworki). "

Masz jakieś fajne na myśli ? Mi z tego co kojarzę jest Spring Batch tylko nie wiem czy to nie jest armata do muchy.

"> Kolejna sprawa, to piszesz, że równolegle... ale co z przeplotami wykonania? Piszesz o abstrakcyjnych tabelach A,B,C, ale gdyby C nie było w jakiś sposób związane z przetwarzaniem A,B to chyba problemu by nie było? Czy robi Ci różnicę kolejność w jakiej zadania rozpoczną pracę i w jakiej zakończą pracę i jaki będzie stan (zawartość) tabel A,B,C w określonym punkcie czasu?"

C jako tako nie jest powiązane z A i B w sensie jeśli chodzi o kalkulacje czy coś natomiast nie może być takiej sytuacji że A i B się przekalkuluje a C wyskoczy gdzieś error i nie przekopiuje.
C również może się zacząć później i skończyć wcześniej nie ma to wpływu.

Przez to myślałem nad jakąś transakcją ale może dobrym pomysłem by było "ala proces" to ogarnąć.

edytowany 1x, ostatnio: ArturoS159
PI
  • Rejestracja:ponad 9 lat
  • Ostatnio:4 miesiące
  • Postów:2787
2

Dla mnie też brzmi jak XY problem.

W świecie Springowym zazwyczaj:

  • @Transactional na metodzie
  • @Async na metodzie
  • @Version nad polem encji

i zazwyczaj działa xD

YA
  • Rejestracja:prawie 10 lat
  • Ostatnio:3 dni
  • Postów:2370
1
ArturoS159 napisał(a):

...

"Dlaczego równolegle? Co zyskujesz kosztem złożoności rozwiązania? "

Tylko po to żeby przyspieszyć proces.

Czy sekwencyjnie jest zbyt wolno? O ile będzie szybciej ?

"(trzeba to sobie oprogramować, można wykorzystać istniejące frameworki). "

Masz jakieś fajne na myśli ? Mi z tego co kojarzę jest Spring Batch tylko nie wiem czy to nie jest armata do muchy.

Szukaj pod hasłami: Java FSM, Spring FSM, z bardziej złożonych rozwiązań: Flowable, Activiti, Camunda. Ale to musisz mieć dobry powód do takiej złożoności.

W0
  • Rejestracja:ponad 12 lat
  • Ostatnio:około 10 godzin
  • Postów:3584
0

Podstawowe pytanie - czy to jest w obrębie jednej bazy danych czy więcej niż jednej.

  1. Jeśli to w obrębie jednej bazy danych, a baza ma taką opcję to zcedowałbym kopiowanie do bazy danych - i transakcje trzymał właśnie tam. W ostatecznym rozrachunku takie zabawy na zbiorach w bazie danych są najprostsze właśnie na bazie danych.
  2. W przypadku gdy kopiujesz z bazy do bazy i nie masz aż tak wielu danych to wystarczy użyć executora:
Kopiuj
@Service
public class SynchronizationService {
  private final SourceRepository source;
  private final TargetRepository target;
  private final ConcurrentTaskExecutor executor;

  // konstruktor

  @Transactional
  public void synchronize() {
    // zadziała, jeśli storeTableA i storeTableC będą też oznaczone @Transactional
    CompletableFuture.allOf(
      executor.submit(() -> source.getTableA()).andThenAccept(list -> target.storeTableA(list)),
      executor.submit(() -> source.getTableC()).andThenAccept(list -> target.storeTableC(list))
    ).join();
  }
}

  1. W końcu - jeśli masz dużo danych to dobrze użyć batch processingu.
jarekr000000
Za moich czasów taki kod powodował spektakularne wyjebki na produkcji (od NPE aż po śmieci wrzucone przez autocommit (brak transakcji)
99xmarcin
Teraz wszystko async, sam nie korzytam z S, bez popatrzenia w kod ConcurrentTaskExecutor czy przekazuje transakcje wątkom nic nie można wnioskować. Może działać może nie działać, na zwykłym executorze na pewno nie zadziała bo wątek dziecko nie dowie się o transakcji (zwykle przekazywanej przez InheritableThreadLocal - przynajmniej w driverach z jakimi pracuje).
jarekr000000
Faktycznie dopiero teraz zobaczyłem, że to ConcurrentTaskExecutor - czyli springowy. Można domniemywać, że przekazuje kontekst transakcji. Ale kompletnie nie mam pojęcia jak w tej sytuacji wyglądają rollbacki itp. (co jak jeden z wątków po godzinie rzuci exceptionem itp). Oczywiście jak zwykle wspaniale jest to udokumentowane.
99xmarcin
O Ile rollback to kwestia wysłania do bazy rollback tx to należy zakładać że każdy może wysłać co zwinie transakcje na DB, oraz że wszystkie pozostałe operacje zaczną rzucać wyjątki...
YA
Moim zdaniem, gdyby Spring magicznie propagował kontekst transakcyjny do ConcurrentTaskExecutora, to można by zapomnieć o ACID ;-) Nawet o eventual consistency. Może trzeba by ukuć nowy termin: phantom consistency
99xmarcin
ACID jest na bazie i cały czas by obowiązywał (zakładam że sync musi być na najniższym poziomie czyli faktycznego połączenia do bazy). Jedyne co byśmy stracili to kolejność wysyłania instrukcji na poziomie programu. To że można zwracać Future z repo sugeruje że się da, ale jak już wspomniałem nie piszę w springu od wielu lat: https://wesome.org/spring-data-jpa-async-query-results
W0
Spokojnie, to nie zadziała :D Chciałem technikalia zachować na weekend. Wrzuciłem to tutaj plbo kiedyś pracowałem w projekcie, gdzie była customowa ThreadFactory przekazywana do TaskExecutora i ona była "mądra". Nie było to stuprocentowo bullet-proof (tzn. nie można tego skopiować do innego projektu i oczekiwać, że będzie działało), ale działało bardzo dobrze w limitowanym scopie.
YA
Wydaje mi się, że taki transaction context musiałby być skojarzony z konkretnym taskiem, a nie pulą/wątkami. Możliwe że spring ma jakieś task decoratory. W każdym razie kod sobie zapisałem, bo to dobry materiał na zadawanie pytań na interview :)
AK
  • Rejestracja:prawie 7 lat
  • Ostatnio:około miesiąc
  • Postów:3561
0
wartek01 napisał(a):

... są najprostsze właśnie na bazie danych.

Dlaczego sposób podania tematu mi się kojarzy z Excellem ?

ArturoS159 napisał(a):

Potrzebuje zrobić "snapshot" danych,

ArturoS159 napisał(a):

"Dlaczego równolegle? Co zyskujesz kosztem złożoności rozwiązania? "

Tylko po to żeby przyspieszyć proces.

No tak, snapshoty z bazy są znanym sposobem polepszenie wydajności
Integralności zresztą też ;)


Bo C to najlepszy język, każdy uczeń ci to powie
YA
  • Rejestracja:prawie 10 lat
  • Ostatnio:3 dni
  • Postów:2370
1

@wartek01 nie żebym się czepiał, ale może czegoś nie rozumiem z magii springowej (tzn. na pewno nie rozumiem :-)), ale jeśli chodzi Twój przykład, to tam masz 3xTransactional:

  • Transactional-1 na wątku głównym (z którego wołasz synchronize())
  • Transactional2-3 na wątkach executora

Każdy taki transactional działa w ramach kontekstu transakcyjnego, który w springu jest utrzymywany per wątek (i domyślnie, w ramach tego samego wątku jest propagowany na kolejne metody oznaczone Transactional). Masz więc 3 transakcje (jedna na wątku głównym) i te 2 z executora. Jak storetTableA przejdzie, a storeTableC się wywali, to chyba nie będzie stanu o jaki autorowi posta chodzi.

Koziołek
Moderator
  • Rejestracja:prawie 18 lat
  • Ostatnio:14 dni
  • Lokalizacja:Stacktrace
  • Postów:6821
0

Masz dwa niezależne procesy do ogarnięcia.

  • wyliczanie B na podstawie A oraz zapisanie kopii A z których wyliczono B.
  • kopiowanie C.

To wszystko chcesz uruchomić w ramach jednej transakcji, a raczej chcesz to uruchomić na raz.

Użyj Spring Batch, to jest bardzo proste i lekkie rozwiązanie. w którym musisz ogarnąć jedynie konfigurację zadań. Cała reszta „zrobi się sama”.

Job 1 – kopiowanie danych w C
Job 2 – trzystopniowy, czyli pobranie A, i dwa równoległe kroki wyliczanie B, zapisywanie A`.


Sięgam tam, gdzie wzrok nie sięga… a tam NullPointerException

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.