Testy równoległe z bazą danych

0

Próbuję przyśpieszyć testy poprzez odpalanie ich równolegle, w których wykorzystuje bazę SQL. Baza działa na dockerze. Do tego celu tworzę pulę baz danych przy starcie testów o ilości równej liczbie ustawionych wątków np. 8 (uprzedzając pytania mój procesor pozwala na więcej). Instancje tych baz trzymam w LinkedBlockingQueue i później rozdzielam do nich dostęp przy pomocy queue.take(), a po zakończeniu testu dodaję wolną bazę do kolejki. Problem w tym że nie dostrzegam znaczącej różnicy pomiędzy odpaleniem testów na 1 wątku, a kilku. Jest tylko kilka sekund różnicy na korzyść testów wielowątkowych (ale może to tylko kwestia innych zmian).

Czy jest coś jest nie tak z moim podejściem?

1

Tu jest tyle rzeczy, które można zrobić niechcący nierównolegle, że nawet nie wiadomo od czego zacząć.

Może masz to za małej ilości speców - w ramach jednego Speca testy kotest chyba i tak lecą równolegle.
Może te bazy na dockerze mają ograniczenia na liczbę połączeń (to jeden docker z wieloma schematami, czy wiele dokerów? ) itd?

Poza tym niepotrzebne sobie komplikujesz.
Normalnie baze odpalasz przy pomocy testcontainers - dostajesz losowy port i w zasadzie już tylko na poziomie gradle wybierasz poziom zrównoleglenia,
żadnych zabaw w kolejki. Może jest narzut na startowanie wielokrotne dockera, ale za to mniej rzeczy się może wywalić/przytkać.

0
jarekr000000 napisał(a):

Normalnie baze odpalasz przy pomocy testcontainers - dostajesz losowy port i w zasadzie już tylko na poziomie gradle wybierasz poziom zrównoleglenia,
żadnych zabaw w kolejki. Może jest narzut na startowanie wielokrotne dockera, ale za to mniej rzeczy się może wywalić/przytkać.

Przy próbie zrównoleglenia testów na poziomie gradle przy pomocy maxParallelForks testy działają wielokrotnie wolniej niż na 1 wątku. Pomimo że widać, że są odpalane równolegle.

0

Ale co te testy konkretnie robią? Czytają, zapisują, przetwarzają jakoś dane w tej bazie? Mało, średnio, dużo?
Może to bardziej kwestia rywalizacji o I/O a nie CPU (nie napisałeś co tam pod spodem siedzi).
Może być to też kwestia jakiegoś zarządzania tymi bazami przez SQL Server (tj. czy konfiguracja jest poprawna pod kątem obsługi wielu baz i wielu CPU) albo zasobów jakie one mają. Tylko gdybam, bo to by trzeba zobaczyć detale tej konfiguracji.

0

A połączenia do bazy są trzymane w jakieś puli i używane przez kolejne testy? Czy tworzone przez każdy test?

Trochę nie rozumiem idei wielu baz w tych testach. Jak to jest w SQL Serverze, baza jest pewną abstrakcją jak w MySQL czy odrębnym bytem (plikami + cała reszta) jak w Oracle?

0
robertos7778 napisał(a):

A połączenia do bazy są trzymane w jakieś puli i używane przez kolejne testy? Czy tworzone przez każdy test?

Połączenia są trzymane w puli. Zarządza nimi HikariCP. Jest limit połączeń i są one reużywane przez kolejne testy. Ilość połączeń odpowiada u mnie liczbie wątków przeznaczonych na testy.

Trochę nie rozumiem idei wielu baz w tych testach.

Jak każdy test korzysta z oddzielnej bazy to nie ma ryzyka, że dane z jednego testu będą wpływać na inny.

Jak to jest w SQL Serverze, baza jest pewną abstrakcją jak w MySQL czy odrębnym bytem (plikami + cała reszta) jak w Oracle?

Tego niestety nie wiem, ale chętnie się dowiem.

0

Cała baza dla jednego testu wydaje mi się rozrzutnym podejściem. Może po prostu osobne schematy?
Z tego co wyczytałem, jedna instancja może mieć wiele baz, z których każda to osobny zestaw plików (w tym log transakcji).

https://learn.microsoft.com/en-us/sql/relational-databases/databases/databases?view=sql-server-ver16
https://learn.microsoft.com/en-us/sql/relational-databases/databases/database-files-and-filegroups?view=sql-server-ver16

0
robertos7778 napisał(a):

Cała baza dla jednego testu wydaje mi się rozrzutnym podejściem. Może po prostu osobne schematy?

Próbowałem początkowo ze schematami, ale przy połączeniu nie można ustawić docelowego schematu i to rodzi dodatkowe problemy. Nie wiem czy używanie osobnych schematów zamiast osobnych baz będzie miało znaczący wpływ na wydajność.

0

Jak każdy test korzysta z oddzielnej bazy to nie ma ryzyka, że dane z jednego testu będą wpływać na inny.

Potwornie komplikujesz sobie życie. Testy nigdy nie potwierdzą na 100%, że aplikacja działa poprawnie, to zawsze jest pewne (graniczące z pewnością?) przybliżenie. Odpal te testy rownolegle na jednej bazie i idź dalej, szkoda czasu na walkę z wiatrakami.

2
Charles_Ray napisał(a):

Jak każdy test korzysta z oddzielnej bazy to nie ma ryzyka, że dane z jednego testu będą wpływać na inny.

Potwornie komplikujesz sobie życie. Testy nigdy nie potwierdzą na 100%, że aplikacja działa poprawnie, to zawsze jest pewne (graniczące z pewnością?) przybliżenie. Odpal te testy rownolegle na jednej bazie i idź dalej, szkoda czasu na walkę z wiatrakami.

Tyle, że to oznacza, że testy muszą być odpowiednio do tego napisane, co jest czasem dość trudne/niepraktyczne.
Banalny przykład: dodajemy ileś rekordów do bazy i sprawdzamy ile tych rekordów teraz jest... i już problem jak takie testy idą równolegle.

0
jarekr000000 napisał(a):
Charles_Ray napisał(a):

Jak każdy test korzysta z oddzielnej bazy to nie ma ryzyka, że dane z jednego testu będą wpływać na inny.

Potwornie komplikujesz sobie życie. Testy nigdy nie potwierdzą na 100%, że aplikacja działa poprawnie, to zawsze jest pewne (graniczące z pewnością?) przybliżenie. Odpal te testy rownolegle na jednej bazie i idź dalej, szkoda czasu na walkę z wiatrakami.

Tyle, że to oznacza, że testy muszą być odpowiednio do tego napisane, co jest czasem dość trudne/niepraktyczne.
Banalny przykład: dodajemy ileś rekordów do bazy i sprawdzamy ile tych rekordów teraz jest... i już problem jak takie testy idą równolegle.

Oczywiście, pisanie testów z pomyślunkiem nie jest trywialne :) zwykle wystarczy w teście wygenerować jakiś identyfikator i odnosić się do niego w sekcji z asercjami. To tez nie jest tak, że wszystkie testy mażą po tej samej tabeli.

1

zakładam że w testach masz dużo blokującego IO więc faktycznie odpalenie ich równolegle powinno przyspieszyć

baza rozumiem cały czas działa w tle na tym dockerze czy docker jest uruchamiany per test?

jeżeli do drugie to możliwe że te restarty bazy Ci na tyle wysycają CPU że ich zrównoleglenie mało daje

ostatnio tez zmagałem się z tym i ja jednak skończyłem z rozwiązaniem żeby mieć dobre czasy egzekucji testów:

  • globalna baza testowa do wszystkich testów uruchamianych w różnych jobach CI
  • każdy też uruchamia się na swojej schemie o nazwie jakas_apka_id_joba_ci
  • baza ma konfiguracje zoptymalizowaną pod testy (RAM disk, nie czekania na zapis WAL'a, itp.)

niemniej musiał byś więcej opisać swój setup żeby można Ci pomóc i może tak pokazać jak zachowuje się CPU na 1-wątkowym odpaleniu a jak na wielo

0

zakładam że w testach masz dużo blokującego IO więc faktycznie odpalenie ich równolegle powinno przyspieszyć

Zależy co masz na myśli przez "dużo". Zakładając że każdy test coś tam zapisuje do bazy to również blokuje IO. Na pewno nie mam tam jakiś skomplikowanych obliczeń, które potrzebują dużo CPU.

walec51 napisał(a):

baza rozumiem cały czas działa w tle na tym dockerze czy docker jest uruchamiany per test?

Docker z bazą działa cały czas. Po każdym teście czyszczę bazę z danych, z której korzystał dany test.

ostatnio tez zmagałem się z tym i ja jednak skończyłem z rozwiązaniem żeby mieć dobre czasy egzekucji testów:

Co to znaczy dobry czas testu? Średnio wychodzi mi 0.125 s na test. W tej średniej zawiera się również zmarnowany czas na build itp.

  • każdy test uruchamia się na swojej schemie o nazwie jakas_apka_id_joba_ci

Czy oddzielne schemy w Twoim przypadku dały lepsze czasy niż oddzielne bazy (na jednym serwerze)?

  • baza ma konfiguracje zoptymalizowaną pod testy (RAM disk, nie czekania na zapis WAL'a, itp.)

Myślałem nad sprawdzeniem RAM disk. Dużo to daje? Ogólnie po zoptymalizowaniu bazy ile zyskałeś na czasie?

niemniej musiał byś więcej opisać swój setup żeby można Ci pomóc i może tak pokazać jak zachowuje się CPU na 1-wątkowym odpaleniu a jak na wielo

Jeszcze będę dokładnie analizował zużycie zasobów i wtedy wrócę ze szczegółami.

1

Zależy co masz na myśli przez "dużo". Zakładając że każdy test coś tam zapisuje do bazy to również blokuje IO. Na pewno nie mam tam jakiś skomplikowanych obliczeń, które potrzebują dużo CPU.

no mam namyśli właśnie to :) w standardowych JPA / JDBC każde zapytanie to blokujące IO

Docker z bazą działa cały czas. Po każdym teście czyszczę bazę z danych, z której korzystał dany test.

A powiedz mi jaką masz bazę i jak ją czyścisz?

Bo w moim przypadku jak właśnie przerzucałem testy z H2 na Postgres to winowajcą pierwszego mocnego spowolnienia był sposób czyszczenia, truncate w H2 był natychmiastowy, a w postgres powodował vacuum, który spowalniał każdy test o 1-2 sec

Co to znaczy dobry czas testu? Średnio wychodzi mi 0.125 s na test. W tej średniej zawiera się również zmarnowany czas na build itp.

Ciężko porównać z projektu na projekt. U mnie miałem porównanie bo przechodziliśmy z H2 do testów na Postgres (naszą silnik stosowany też na produkcyji).

Pierwsze podejście spowodowało że kilkaset testów co trwało na H2 ~10 min zaczęło trwać ponad 1h.

  • każdy test uruchamia się na swojej schemie o nazwie jakas_apka_id_joba_ci

Czy oddzielne schemy w Twoim przypadku dały lepsze czasy niż oddzielne bazy (na jednym serwerze)?

Na pewno schemy są nieco lżejsze bo przynajmniej w postgres oddzielna baza per test wymagała by stawiania pod każdy test nowej puli połączeń po stronie apki i nowego folderu z plikami bazy po strony postgres.

Ale to jest mocno zależne od tego jaką bazę stosujesz i jak Twoja apka zarządza połączeniami.

  • baza ma konfiguracje zoptymalizowaną pod testy (RAM disk, nie czekania na zapis WAL'a, itp.)

Myślałem nad sprawdzeniem RAM disk. Dużo to daje? Ogólnie po zoptymalizowaniu bazy ile zyskałeś na czasie?

Pod względem dostępu do dysku najwięcej dało w Postgres wyłączenie czekania na wpadnięcie WAL'a na dysk przed commit <- tylko pamiętaj żeby tego nie włączyć czasem na produkcji ;)

Po tym RAM disk już dał bardzo małe % uzysku. Ale nie wiem jak na Twojej bazie to będzie.

1 użytkowników online, w tym zalogowanych: 0, gości: 1