Aplikacja odpytuje puste tabele


10

Moja firma korzysta z aplikacji, która ma dość poważne problemy z wydajnością. Istnieje wiele problemów z samą bazą danych, nad którymi pracuję, ale wiele z nich dotyczy wyłącznie aplikacji.

Podczas mojego dochodzenia odkryłem, że do bazy danych SQL Server trafiają miliony zapytań, które odpytują puste tabele. Mamy około 300 pustych tabel, a niektóre z nich są sprawdzane do 100-200 razy na minutę. Tabele nie mają nic wspólnego z naszym obszarem działalności i są zasadniczo częścią oryginalnej aplikacji, której sprzedawca nie usunął, gdy firma zleciła mi opracowanie rozwiązania programowego dla nas.

Oprócz tego, że podejrzewamy, że nasz dziennik błędów aplikacji jest zalewany błędami związanymi z tym problemem, sprzedawca zapewnia nas, że nie ma wpływu na wydajność ani stabilność ani aplikacji, ani serwera bazy danych. Dziennik błędów jest zalany do tego stopnia, że ​​nie możemy zobaczyć błędów dłuższych niż 2 minuty na wykonanie diagnozy.

Rzeczywisty koszt tych zapytań będzie oczywiście niski pod względem cykli procesora itp. Ale czy ktoś może zasugerować, jaki wpływ miałby na SQL Server i aplikację? Podejrzewam, że sama mechanika wysyłania zapytania, potwierdzania go, przetwarzania, zwracania i potwierdzania odbioru przez aplikację sama w sobie miałaby wpływ na wydajność.

Do aplikacji używamy SQL Server 2008 R2, Oracle Weblogic 11g.

@ Frisbee - Krótko mówiąc, stworzyłem tabelę zawierającą kwerendę, która uderzyła w puste tabele w bazie danych aplikacji, a następnie zapytałem o wszystkie nazwy tablic, które znam puste i mam bardzo długą listę. Największym hitem było wykonanie 2,7 miliona egzekucji w ciągu 30 dni bezawaryjności, mając na uwadze, że aplikacja jest zwykle używana w godzinach 8–18, więc liczby te są bardziej skoncentrowane na godzinach pracy. Wiele tabel, wiele zapytań, pewnie niektóre ponownie powiązane poprzez połączenia, niektóre nie. Największym hitem (w tym czasie 2,7 miliona) był prosty wybór z pojedynczego pustego stołu z klauzulą ​​where, bez łączenia. Spodziewałbym się, że większe zapytania z łączeniami do pustych tabel mogą obejmować aktualizacje połączonych tabel, ale sprawdzę to i zaktualizuję to pytanie jak najszybciej.

Aktualizacja: Istnieje 1000 zapytań o liczbie wykonań od 1043 do 4622614 (ponad 2,5 miesiąca). Będę musiał kopać więcej, aby dowiedzieć się, kiedy pochodzi buforowany plan. Ma to na celu zobrazowanie zakresu zapytań. Większość jest dość skomplikowana z ponad 20 złączeniami.

@ srutzky- tak Wydaje mi się, że istnieje kolumna dat związana z czasem, kiedy plan został opracowany, więc byłoby to interesujące, więc to sprawdzę. Zastanawiam się, czy ograniczenia wątków byłyby w ogóle czynnikiem, gdy SQL Server znajduje się w klastrze VMware? Na szczęście wkrótce będzie dedykowany Dell PE 730xD na szczęście.

@Frisbee - Przepraszamy za spóźnioną odpowiedź. Jak zasugerowałeś, uruchomiłem select * z pustej tabeli 10 000 razy w ciągu 24 wątków za pomocą SQLQueryStress (czyli w rzeczywistości 240 000 iteracji) i natychmiast otrzymałem 10 000 żądań wsadowych / s. Następnie zredukowałem do 1000 razy w ciągu 24 wątków i trafiłem nieco poniżej 4000 żądań partii / s. Próbowałem także 10 000 iteracji tylko na 12 wątkach (czyli 120000 wszystkich iteracji), co dało ciągły wynik 6505 partii / s. Wpływ na procesor był zauważalny, około 5-10% całkowitego zużycia procesora podczas każdego uruchomienia testowego. Oczekiwania sieciowe były znikome (jak 3ms z klientem na mojej stacji roboczej), ale wpływ procesora był na pewno, co jest dość rozstrzygające, jeśli o mnie chodzi. Wydaje się, że sprowadza się to do użycia procesora i trochę niepotrzebnego IO pliku bazy danych. Łączna liczba egzekucji na sekundę wynosi niespełna 3000, co jest więcej niż w produkcji, jednak testuję tylko jedno z kilkudziesięciu takich zapytań. Efekt netto setek zapytań trafiających w puste tabele z szybkością 300–4000 razy na minutę nie byłby zatem nieistotny, jeśli chodzi o czas pracy procesora. Wszystkie testy przeprowadzono na bezczynnym PE 730xD z podwójnym układem pamięci flash i 256 GB pamięci RAM, 12 nowoczesnych rdzeni. To jest wynik SQLSentry

@ srutzky- dobre myślenie. Wygląda na to, że SQLQueryStress domyślnie korzysta z puli połączeń, ale mimo to rzuciłem okiem i stwierdziłem, że tak, pole puli połączeń jest zaznaczone. Zaktualizuj, aby śledzić

@ srutzky- Pule połączeń najwyraźniej nie są włączone w aplikacji - a jeśli tak, to nie działa. Zrobiłem śledzenie profilera i stwierdziłem, że połączenia mają EventSubClass „1 - Nonpooled” dla zdarzeń logowania inspekcji.

RE: Pula połączeń - sprawdzono logikę i znalazłem włączone połączenie połączeń. Sprawdziłem więcej śladów na żywo i znalazłem, że pule nie występują prawidłowo / wcale: wprowadź opis zdjęcia tutaj

Oto, jak to wygląda, gdy uruchamiam jedno zapytanie bez sprzężeń z zapełnioną tabelą; wyjątki brzmią: „Wystąpił błąd związany z siecią lub specyficzny dla instancji podczas nawiązywania połączenia z programem SQL Server. Serwer nie został znaleziony lub był niedostępny. Sprawdź, czy nazwa instancji jest poprawna i czy program SQL Server jest skonfigurowany tak, aby zezwalał na połączenia zdalne. (dostawca: Dostawca nazwanych potoków, błąd: 40 - Nie można otworzyć połączenia z programem SQL Server) „Zanotuj licznik żądań wsadowych. Pingowanie serwera podczas generowania wyjątków powoduje pomyślną odpowiedź ping.

wprowadź opis zdjęcia tutaj

Aktualizacja - dwa kolejne testy, to samo obciążenie (wybierz * z Pustej tabeli), włączanie / wyłączanie buforowania. Nieco więcej użycia procesora i dużo awarii i nigdy nie przekracza 500 żądań wsadowych na sekundę. Testy wykazują 10 000 partii / s i brak awarii przy włączonym buforowaniu oraz około 400 partii / s, a następnie wiele awarii spowodowanych wyłączeniem puli. Zastanawiam się, czy te awarie są związane z brakiem dostępności połączenia?

wprowadź opis zdjęcia tutaj

@ srutzky- Wybierz Count (*) z sys.dm_exec_connections;

  • Pula danych włączona: 37 konsekwentnie, nawet po zakończeniu testu obciążenia

  • Pula wyłączona: 11–37 w zależności od tego, czy wyjątki
    występują w SQLQueryStress, tj .: gdy te koryta pojawiają się na
    wykresie partii / s, wyjątki występują w SQLQueryStress, a
    liczba połączeń spada do 11, a następnie stopniowo wraca do 37 kiedy partie zaczynają osiągać szczyt i wyjątki nie występują. Bardzo, bardzo interesujące.

Maksymalna liczba połączeń w obu instancjach testowych / na żywo ustawiona na wartość domyślną 0.

Sprawdziłem dzienniki aplikacji i nie mogę znaleźć problemów z łącznością, jednak rejestrowanie jest możliwe tylko przez kilka minut ze względu na dużą liczbę i rozmiar błędów, tj .: wiele błędów śledzenia stosu. Kolega z działu obsługi aplikacji informuje, że występuje znaczna liczba błędów HTTP związanych z łącznością. Wydaje się na tej podstawie, że z jakiegoś powodu aplikacja nie tworzy poprawnej puli połączeń, w wyniku czego serwerowi ciągle brakuje połączeń. Przyjrzę się więcej dziennikom aplikacji. Zastanawiam się, czy istnieje sposób na udowodnienie, że dzieje się to w produkcji od strony SQL Servera?

@ srutzky- Dziękuję. Sprawdzę jutro konfigurację weblogiczną i zaktualizuję. Myślałem jednak o zaledwie 37 połączeniach - jeśli SQLQueryStress wykonuje 12 wątków przy 10 000 iteracjach = 120 000 instrukcji select bez puli, czy nie powinno to oznaczać, że każdy wybór tworzy odrębne połączenie z instancją SQL?

@ srutzky- Weblogics są skonfigurowane do pula połączeń, więc powinno działać dobrze. Pula połączeń jest skonfigurowana w następujący sposób na każdej z 4 logik internetowych z równoważeniem obciążenia:

  • Początkowa pojemność: 10
  • Maksymalna pojemność: 50
  • Minimalna pojemność: 5

Gdy zwiększam liczbę wątków wykonujących zapytanie „wybierz z pustej tabeli”, liczba połączeń osiąga wartość szczytową około 47. Przy wyłączonej puli połączeń konsekwentnie widzę niższe maksymalne żądania wsadowe / s (z 10 000 do około 400). Co się stanie za każdym razem, gdy „wyjątki” w SQLQueryStress pojawią się wkrótce po przejściu serii / sek. Ma to związek z łącznością, ale nie mogę zrozumieć, dlaczego tak się dzieje. Gdy nie są uruchomione żadne testy, liczba połączeń spada do około 12.

Przy wyłączonej puli połączeń mam problem ze zrozumieniem, dlaczego występują wyjątki, ale może to zupełnie inny stos. Wymienić pytanie / pytanie dla Adama Machanica?

@srutzky Zastanawiam się więc, dlaczego występują wyjątki bez włączonej puli, nawet jeśli w SQL Server nie brakuje połączeń?


1
Piotr, z najnowszych aktualizacji w umyśle dotyczących zestawiania połączeń, wygląda teraz trzeba ponownie uruchomić swoje testy z SQLQueryStress ale z zestawianiem połączeń odwrócił się . Byłoby to dokładniejsze odzwierciedlenie efektów działania aplikacji i wierzę, że pokaże to wzrost wykorzystania procesora, a nawet pamięci RAM.
Solomon Rutzky

1
Peter, czy masz maksymalną liczbę połączeń ustawionych dla serwera? Zgaduję, że bez puli napotykasz problem zbyt wielu połączeń. Zastanawiam się, czy Twoja aplikacja kiedykolwiek ma ten błąd. Ponadto, jeśli to możliwe, ponownie uruchomić ostatni test jeszcze raz (zarówno z włączoną pulą, jak i bez niej), gdy test jest uruchomiony dla każdej z tych dwóch konfiguracji, uruchom a, SELECT COUNT(*) FROM sys.dm_exec_connections;aby sprawdzić, czy wartość różni się znacznie między włączeniem puli lub nie. W oparciu o te błędy, myślę, że byłoby więcej połączeń, gdy pula była wyłączona.
Solomon Rutzky

1
Peter, 37 połączeń wydaje się strasznie niskim maksimum. Biorąc pod uwagę, że limit połączeń jest ustawiony na 0 (tj. Nieograniczony), czy pamięć systemowa jest ograniczona? Również pule połączeń powinny być domyślnie włączone, ale są kontrolowane przez klienta. Czy aplikacja jest aplikacją .NET? Nie musi to być w celu użycia puli połączeń, ale pomogłoby to wiedzieć, aby znaleźć przyczynę tego. I czy widzisz, jaki ciąg połączenia jest używany? Czy to określa Pooling=falseczy Max Pool Size?
Solomon Rutzky

1
Peter, każdy z 12 wątków tworzy własne połączenie dla zapytania, kolejno dla 10k iteracji. Tak więc bez pulowania połączenie może zostać zniszczone, gdy tylko kod zostanie zamknięty. Pooling zachowa połączenie do ponownego użycia. Ma więc sens, że liczba połączeń była spójna podczas korzystania z puli. Nie jestem pewien, dlaczego 37 bez dodatkowych informacji. Ile jest połączeń, gdy nie jest uruchomiony test? Wycofanie tej liczby da lepsze wskazanie, ile z nich powstało podczas testowania.
Solomon Rutzky

1
Pule połączeń są utrzymywane dla klienta, a nie dla serwera. Dlatego WebLogics i SQLQueryStress powinny mieć własne pule połączeń (pod względem wielkości min_pool i max_pool itp.). Odnośnie „Przy wyłączonej puli połączeń widzę niższe maksymalne żądania wsadowe / s”: ma to sens, ponieważ uwierzytelnianie i inicjowanie sesji zajmuje więcej czasu każdemu połączeniu z aplikacji itp. Właśnie dlatego istnieje pula połączeń: ).
Solomon Rutzky

Odpowiedzi:


7

Podejrzewam, że sama mechanika wysyłania zapytania, potwierdzania go, przetwarzania, zwracania i potwierdzania odbioru przez aplikację sama w sobie miałaby wpływ na wydajność.

Tak, istnieją nawet dodatkowe czynniki, ale nie można powiedzieć, w jakim stopniu którekolwiek z nich faktycznie wpływają na twój system bez analizy systemu.

To powiedziawszy, pytasz o to, co może być problemem, i jest kilka rzeczy do wspomnienia, nawet jeśli niektóre z nich nie są obecnie czynnikiem w twojej konkretnej sytuacji. Mówisz tak:

Mamy około 300 pustych tabel, a niektóre z nich są sprawdzane do 100-200 razy na minutę.

  • Puste tabele, które nie są sprawdzane, nie stanowią problemu. Ale myślę, że możesz mieć również na myśli to, że wszyscy są przesłuchiwani, tylko że niektórzy zostają trafieni o wiele bardziej niż inni.
  • Analiza składni zapytania i generowanie planu wykonania nie powinno stanowić większego problemu, jeśli przesłany tekst zapytania pozostaje taki sam w przypadku wszystkich wywołań. SQL Server haszuje tekst zapytania i sprawdza go w pamięci podręcznej planu. Jeśli zostanie znaleziony, nie wykona ponownie ani analizy, ani kompilacji (dopóki plan nie zostanie usunięty z pamięci podręcznej).
  • Każda tabela, pusta lub niepusta, będzie wymagała co najmniej „współdzielonej” blokady, aby wskazać, że zasób jest używany. Zapobiega to wprowadzaniu zmian przez operacje, które wymagają wyłącznych blokad (dodawanie / zmienianie / usuwanie kolumn itp.) Podczas korzystania z zasobu. Blokowanie i odblokowywanie, nawet jeśli jest wykonywane w czasie krótszym niż 1 milisekunda, ponieważ nie ma danych, nadal wymaga zasobów systemowych (pamięci i procesora) do zarządzania tymi operacjami blokowania.
  • Nawet bez zestawów wyników wracających do aplikacji z SQL Server, nadal jest taka sama ilość ruchu sieciowego docierającego do SQL Server, niezależnie od tego, czy zapytanie daje wyniki, czy nie. Należy wysłać tekst zapytania lub nazwę procedury składowanej. I nawet jeśli żadne wyniki nie wrócą, SQL Server nadal musi wysyłać niektóre pakiety sieciowe zawierające strukturę zestawu wyników oprócz pakietów informujących klienta, że ​​zestaw wyników się uruchamia (nawet jeśli nie znaleziono żadnych wierszy), a następnie że zestaw wyników jest zakończenie i powinno zostać zamknięte. I mogą pojawić się dodatkowe komunikaty z instrukcji print i / lub liczby wierszy.
  • Połączenie z programem SQL Server wymaga pewnej ilości zasobów systemowych. Potrzeba procesora i pamięci do obsługi uwierzytelnienia (a także pakietów sieciowych tam iz powrotem), a to także zajmuje dużo czasu. Właśnie dlatego istnieje Pula połączeń: aby zmniejszyć ten koszt.
  • Nawet z pulą połączeń zmniejszającą zużycie zasobów systemowych SQL Server nadal musi utrzymywać te połączenia, a to wymaga pamięci i minimalnego procesora.
  • Nawet bez wierszy, a tym samym z bardzo krótkim czasem wykonania, zapytanie było nadal wykonywane. Nawet jeśli było 10 lub 10 000 wierszy i zostały one pobrane z puli buforów (tj. Pamięci), ponieważ były często używane, wątek nadal musi wykonać tę pracę. Wątek, który działa na tym bezużytecznym zapytaniu, nie działa na rzeczywistym przydatnym zapytaniu.

Może być nawet więcej, ale to powinno pomóc w zrozumieniu rzeczy. I pamiętaj, że podobnie jak większość problemów z wydajnością, wszystko zależy od skali. Wszystkie wyżej wymienione przedmioty nie są problemami, jeśli zostaną trafione raz na minutę. To jak testowanie zmiany na stacji roboczej lub w bazie danych programowania: zawsze działa tylko z 10 - 100 wierszami w tabelach. Przenieś ten kod do produkcji, a uruchomienie zajmie 10 minut, a ktoś musi powiedzieć: „dobrze, działa na moim pudełku” ;-). Oznacza to, że widzisz problem tylko z powodu samej liczby wykonywanych połączeń, ale taka jest sytuacja.

Zatem nawet przy 1 milionie bezużytecznych zapytań bez wiersza, co stanowi:

  • dodatkowe 2 miliony operacji blokady (każda blokada musi zostać odblokowana, prawda?). jest to głównie koszt czasu spędzonego na bezużytecznej operacji zamiast na użytecznej operacji.
  • większy ruch sieciowy, który może przybliżać Cię do nasycenia (nie wiem, czy to prawdopodobne, ale nadal)
  • utrzymywanych jest więcej połączeń, które zajmują więcej pamięci. Ile masz nieużywanej fizycznej pamięci RAM? pamięć ta byłaby lepiej wykorzystana do uruchamiania zapytań i / lub pamięci podręcznej planu zapytań. Najgorszym przypadkiem jest brak pamięci fizycznej i SQL Server musi zacząć korzystać z pamięci wirtualnej (swap), ponieważ to spowalnia proces (sprawdź dziennik błędów SQL Server, aby zobaczyć, czy otrzymujesz komunikaty o stronicowaniu pamięci).

    I na wypadek, gdyby ktoś wspomniał: „cóż, istnieje pula połączeń”. Tak, to zdecydowanie pomaga zmniejszyć liczbę potrzebnych połączeń. Ale przy zapytaniach przychodzących z prędkością do 200 razy na minutę jest to dużo równoczesnych działań i połączenia muszą nadal istnieć w celu uzyskania uzasadnionych żądań. Zrób, SELECT * FROM sys.dm_exec_connections;aby zobaczyć, ile utrzymujesz aktywnych połączeń.

  • niezależnie od czegokolwiek innego, wciąż jest to co najmniej milion razy w ciągu dnia, że ​​wątek, który mógł robić coś pożytecznego, był zamiast tego niedostępny.

Jeśli nie mylę się co do tego, co tu mówię, wydaje mi się, że nawet na niewielką skalę jest to rodzaj ataku DDoS na twój system, ponieważ zalewa on sieć i twój SQL Server fałszywymi żądaniami , zapobiegając przedostawaniu się rzeczywistych żądań do SQL Server lub przetwarzaniu przez SQL Server.


1

Jeśli stoły trafiają 100-200 razy na minutę, to (miejmy nadzieję) są w pamięci. Obciążenie serwera jest bardzo, bardzo niskie. Chyba że masz wysoki procesor lub pamięć na serwerze bazy danych, prawdopodobnie nie stanowi to problemu.

Tak, zapytania biorą wspólne blokady, ale mam nadzieję, że nie blokują żadnych blokad aktualizacji ani nie są blokowane przez żadną blokadę aktualizacji. Czy masz jakieś aktualizacje, wstawianie lub usuwanie w tych tabelach. Jeśli nie, po prostu odpuszczę - jeśli masz problemy z wydajnością, z perspektywy serwera bazy danych muszą być większe ryby do smażenia.

Przeprowadziłem test na 100 000 wybranych liczników (*) na pustej tabeli i wykonano go w 32 sekundy, a zapytania były przesyłane przez sieć. 1/3 milisekundy. O ile sieć nie zostanie przeciążona, nie ma to nawet wpływu na klienta. Jeśli masz poważne problemy z wydajnością, te 1/3 milisekund puste zapytania nie są przyczyną śmierci aplikacji.

Mogą to być tylko fragmenty lewego łącznika przechwytujące dane statyczne, które nie są częścią bieżącej aplikacji. Można go połączyć z innymi zapytaniami, więc nie jest to dodatkowa podróż w obie strony. Jeśli tak, to jest niechlujny, ale nawet nie powoduje większego ruchu.

Wróćmy więc do faktycznych stwierdzeń. Czy widzisz jakieś aktualizacje, dodania lub usunięcia w tych tabelach?

Tak, wiele pustych tabel i zapytania do pustych tabel wskazują na niechlujne kodowanie. Ale jeśli masz poważne problemy z wydajnością, nie jest to przyczyną, o ile nie wykonujesz naprawdę niechlujnych operacji zapisu przy tych tabelach.


Ilu innych użytkowników korzystało z zapytań SQL Server podczas testowania 100 000 zapytań? Nie twierdzę, że mam rację, a ty się mylisz, ale jeśli byłbyś jedynym w systemie lub jednym z niewielu, to naturalnie nie zobaczyłbyś większego wpływu. Kwestia blokowania nie była kwestią blokowania, była to po prostu kwestia zasobów, których wymaga SQL Server do blokowania i odblokowywania tych stron danych, nawet jeśli zawsze znajdują się w puli buforów. To wciąż praca, którą się wykonuje. A harmonogramy nie są nieograniczone.
Solomon Rutzky,

I nie mówię, że się mylisz. Inni użytkownicy, czy nie, jest to nadal miara czasu i zasobów. Podane obciążenie wynosi 100-200 na minutę. 100 000 od jednego klienta w ciągu 30 sekund przekracza to obciążenie o współczynnik 200 do 400. Jeśli nie ma żadnych blokad aktualizacji, to jeśli pochodzi od jednego klienta lub 100, nie ma znaczenia. Twoja odpowiedź zakłada, że ​​istnieje przeciążona sieć lub serwer SQL i na podstawie pytania, którego nie znasz. Gdyby to był atak DDoS, byłoby więcej jak 100 / s (nie minuta) i nie byłoby to przeciwko pustemu stołowi.
paparazzo,

Prawidłowo, w oparciu o pytanie, którego nie wiemy na tyle, aby je zawęzić, dlatego mówiłem, że te rzeczy mogą stanowić problem, w zależności od okoliczności. A sprawa DDoS była tylko analogią, opartą głównie na brzmieniu pierwotnego pytania, co sugerowało, że kilka trafiło w tym tempie, a wiele innych zostało trafionych, tylko rzadziej.
Solomon Rutzky,

Uważam, że jest to cenna odpowiedź w tym sensie, że akapit pierwszy bardzo dobrze podsumowuje: „O ile nie masz dużego procesora lub pamięci na serwerze bazy danych, prawdopodobnie nie jest to problem”. W naszym przypadku mamy wysokie zużycie procesora w określonych porach dnia, dlatego też dodatkowe ciśnienie procesora wydaje się być czynnikiem opartym na moich testach.
Peter

W szczególności cytowałem tylko zapytania wykonujące 100-200 razy / minutę, podczas gdy w rzeczywistości jest około 50 zapytań do tych pustych tabel z liczbą wykonań między 200-4000 / minutę. Łącznie efekt zapytania pustych tabel o tej częstotliwości ma duży wpływ na procesor, nawet w najlepszym przypadku wielokrotnego wykonywania nieparametryzowanych zapytań, więc plan, dane itp. Są w pamięci.
Peter

0

Ogólnie przy każdym zapytaniu wykonuje się następujące kroki:

  1. Wniosek z aplikacji.
  2. Baza danych Parsuj zapytanie.
  3. Aparat bazy danych sprawdź, czy to zapytanie jest już zapisane w pamięci RAM. użyj planu wykonania, jeśli istnieje w pamięci.
  4. jeśli nie istnieje w pamięci RAM, aparat bazy danych sprawdza istniejące statystyki dotyczące obiektów w zapytaniu i określa plan wykonania.
  5. Uruchom plan wykonania, użyj operacji we / wy, aby uzyskać dane z dysku.
  6. odpowiedź na wniosek.

wiele zapytań, o których wspomniałeś, może spowodować dodatkowe obciążenie już obciążonego systemu - dodatkowe obciążenie połączeń, procesora, pamięci RAM i operacji we / wy.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.