Skalowanie systemów informatycznych w sektorze publicznym

Skalowanie systemu to zjawisko zwiększania (lub zmniejszania) jego możliwości obliczeniowych na miarę aktualnych potrzeb. Nietrudno sobie wyobrazić, że serwery obsługujące ruch na portalu zakupowym będą znacznie bardziej obciążone w okresie przedświątecznym, niż krótko po np. Świętach Bożego Narodzenia. Gdyby zespół IT takiego sklepu przeoczył tę prawidłowość, firma mogłaby doświadczyć przeciążenia serwerów, stracić pieniądze, renomę oraz klientów. W nieodległej przeszłości dało się słyszeć o tego typu problemach (np. w momencie lawinowego wzrostu popularności portalu nasza-klasa.pl). Moje odczucie jest takie, że świadomość tego zagrożenia znacznie wzrosła w sektorze prywatnym, jednak ciągle daje się ono we znaki w sektorze publicznym.

Śledząc nagłówki polskich tytułów można napotkać informacje o problemach z wydajnością systemów informatycznych. Słyszeliśmy już o problemach z systemami: EWUŚ, Źródło, Emp@tia oraz z systemem do obsługi wyborów parlamentarnych (przykładowe artykuły podlinkowane zostały na końcu niniejszego artykułu). Jestem przekonany, że ta lista mogłaby być bardziej pokaźna po przeprowadzeniu nieco bardziej wnikliwego „śledztwa”.

Wspólnym mianownikiem wymienionych systemów wydaje się być owa mityczna „centralizacja”. Cóż ona oznacza? Dokładnie to, że zamiast rozwiązywać pewien problem w środowisku rozproszonym (w osobnych komisjach wyborczych, w urzędach gminy. miasta itp.) przenosi się ten sam problem do jednego, centralnego systemu informatycznego, a następnie liczy na to, że – przy zastosowaniu odpowiednio szybkich i pojemnych serwerów – system będzie wystarczająco wydajny. Praktyka pokazuje często, że jest inaczej. Co może być tego przyczyną?

Obawiam się, że jest to często brak świadomości w zespołach odpowiedzialnych za wykonanie i wdrożenie oprogramowania systemów, choć nie możemy, w niektórych wypadkach, wykluczyć istnienia jakichś czynników uniemożliwiających im podjęcie odpowiednich decyzji projektowych (terminy, błędnie wykonane SIWZ itp.). Niestety, dość często pojawiają się wątpliwości co do kompetencji firm wygrywających przetargi rządowe, jednak nie jest to temat, którym chciałbym się zajmować w niniejszym artykule – chciałbym się skupić na tym, skąd problemy z wydajnością się biorą i jak im zaradzić.

Pracując, kilka lat temu, w jednej z firm dostarczających rozwiązania informatyczne dla sektora publicznego zauważyłem, że niejako instynktownie przyjmuje się w wielu systemach architekturę warstwową jako podstawową architekturę rozwiązania.

Charakteryzuje się ona tym, że poszczególne elementy systemu rezydują w swoich określonych warstwach (warstwa interfejsu użytkownika, logiki, bazy danych), a komunikacja pomiędzy komponentami systemu odbywa się z warstw wyższych (UI) do niższych (baza danych), najczęściej w sposób synchroniczny, czyli blokujący zasoby systemu na czas wykonania określonego żądania (przy większych obciążeniach wywołując efekt „zawieszenia się”).

arch-warstwowa

Baza danych najczęściej jest bazą relacyjną typu SQL zawierającą wysoko znormalizowany model danych (tabel), powiązanych ze sobą siecią połączeń. Do wykonywania bardziej skomplikowanych operacji często wykorzystuje się naturalne dla baz SQL procedury składowane.

Jaki jest problem z takim modelem? Jak zwykle – to zależy. Jeżeli nie spodziewamy się ruchu przekraczającego możliwości konkretnej liczby serwerów, ani nie przewidujemy okresowych znacznych wzrostów aktywności – model ten będzie wystarczający. W przeciwnym wypadku, napotkamy na dwa główne ograniczenia możliwości systemu.

Baza danych

Poczynając od bazy danych, takie podejście praktycznie uniemożliwia skalowanie horyzontalne (tj. dodawanie nowych serwerów bazodanowych w miarę potrzeb), Nie możemy po prostu dodać nowego serwera i składować na nim tylko część danych, ponieważ przy wielu połączeniach pomiędzy tabelami nie daje się wydzielić takiego zbioru (w teorii grafów – grafu niespójnego), który można by w całości wydzielić i przenieść do osobnej bazy danych, zwielokrotniając w ten sposób możliwości systemu.

Dodatkowym obciążeniem dla bazy są procedury składowane, które zwiększają obciążenie bazy i nieuchronnie przybliżają moment konieczności skalowania w wertykalnego (o którym za chwilę), co jest w przypadku baz danych bardzo kosztowne (polecam wpis na blogu Stephena Smith’a podlinkowany na końcu tego wpisu).

Wspomniałem wyżej o problematycznym skalowaniu pionowym. Polega ono na zwiększaniu możliwości obliczeniowych konkretnego serwera lub serwerów poprzez zwiększenie ilości dostępnej pamięci RAM, liczby procesorów, przestrzeni dyskowej itp. Brzmi logicznie, jednak problem polega na tym, że koszty tego skalowania rosną wykładniczo, co oznacza, że dwukrotne zwiększenie mocy serwera jest bardziej kosztowne niż zakup drugiego serwera o identycznych parametrach. Serwer przez to staje się bardziej wydolny, ale i coraz bardziej kosztowny, a nadal pozostaje punktem krytycznym systemu – w przypadku jego awarii cały system przestaje działać (można oczywiście zainwestować w serwery zapasowe).

skal-pionowe

Przy okazji realizacji systemu na potrzeby jednego z dużych banków w Polsce dane mi było doświadczyć problemu z tym modelem skalowania. System wdrażany był na potężnym serwerze bazodanowym, na którym konkurował o zasoby z pozostałymi aplikacjami. Nie dość, że baza danych stanowiła główny punkt awarii, to w dodatku problemy wydajności jednej z aplikacji mogły propagować się na pozostałe systemy. W przypadku konieczności skalowania poziomego tego serwera (np. zwiększone sezonowo obciążenie jednego z systemów) powodowałoby to masę problemów i stanowiło samo w sobie pokaźne przedsięwzięcie.

Warstwa logiki biznesowej / interfejs użytkownika

W tym obszarze najwięcej rozterek użytkownikom i architektom dostarcza synchroniczny model komunikacji, który powoduje owe „zawieszanie się” systemów. Wynika to z tego, że system blokuje zasoby oraz interfejs użytkownika na czas wykonania jednego żądania. Model ten, w mojej opinii, nadużywany jest przez dostawców oprogramowania ze względu na prostotę jego implementacji, stanowiącej jednocześnie jego podstawową zaletę.

Wyobraźmy sobie system, który umożliwia mieszkańcom miasta składanie w urzędzie pewnego wniosku oraz generowania potwierdzenia jego przyjęcia opatrzonego unikalnym numerem.

W modelu synchronicznym petent podchodzi do okienka, przekazuje urzędnikowi pewne dane wejściowe np. w postaci formularza wypełnionego ręcznie, urzędnik wprowadza dane do systemu, zatwierdza je i czeka na przetworzenie żądania. Architektura systemu sprawia, że czas przetworzenia wniosku może być bardzo różny. Od kilku sekund w momencie obniżonej aktywności do, przykładowo, kilku minut w okresie wzmożonego obciążenia (koniec roku, wejście w życie nowych przepisów itp.). Petent w tym czasie czeka, a kolejka rośnie ponieważ interfejs aplikacji jest zablokowany w trybie „proszę czekać”. Po zakończeniu przetwarzania petent otrzymuje wydruk swojego potwierdzenia i obsłużona może zostać kolejna osoba.

system-1

Widzimy, że architektura systemu zaprojektowanego w ten sposób jest relatywnie prosta, jednak jest on przy tym podatny na wydłużanie czasu przetwarzania oraz wydłużanie kolejek w urzędzie.

Alternatywa – architektura umożliwiająca skalowanie poziome

Istnieją techniki, które możemy zastosować w celu zwiększenia wydajności oraz umożliwieniu skalowania poziomego.

Skalowanie poziome, w odróżnieniu od pionowego, polega na tworzeniu klastrów serwerów składających się z wielu maszyn o niewygórowanych parametrach. Zamiast jednego, bardzo drogiego, serwera stosujemy większą liczbę maszyn o znacznie mniejszej mocy obliczeniowej i dużo niższej cenie. Takie podejście umożliwia nam osiągnięcie wyższej sumarycznej mocy obliczeniowej przy niższych kosztach, które w tym modelu rosną – w przybliżeniu – liniowo.

skal-poziome

Wiemy już, że do osiągnięcia naprawdę dużej skali konieczne jest zaprojektowanie systemu w sposób umożliwiający jego skalowanie poziome. Jak to osiągnąć? Jedną z części tej układanki jest partycjonowanie. Polega ono na podzieleniu zbioru danych na pewne niezależne od siebie części, które można następnie umieścić na różnych dyskach lub nawet serwerach. To umożliwi nam składowanie danych na wielu serwerach jednocześnie oraz dołączanie nowych serwerów w miarę potrzeby, co jest – jak już ustaliliśmy – definicją skalowania poziomego.

Możemy również zauważyć, że taki model skalowania jest, niejako, naturalnym modelem wzrostu dla instytucji, w których wdrażane systemy mają działać: urzędy miasta – partycjonowanie obywateli według ich miast zamieszkania, urzędy wojewódzkie – analogicznie, według województw.

Nawet skalowalny system może doświadczyć chwilowego przeciążenia. Powinniśmy zatem pomyśleć o tym, jak zaprojektować nasz system aby takie chwilowe wzrosty aktywności nie powodowały jego paraliżu, który mógłby objawiać się jako znane nam już z artykułów prasowych „zawieszanie się”.

Asynchroniczna komunikacja pomiędzy komponentami

Zastosowanie modelu asynchronicznego w pierwszych dwóch warstwach systemu bardzo często pozwala na znaczne zwiększenie przepustowości.

Rozważmy to na przykładzie użytym wcześniej. W wariancie asynchronicznym systemu przyjmowania wniosków petent podchodzi do okienka, przekazuje formularz urzędnikowi, urzędnik wprowadza go do systemu, niezwłocznie otrzymuje potwierdzenie przyjęcia wniosku, prosi kolejną osobę do okienka, a aktualnego petenta prosi o poczekanie kilku minut obok okienka. Podczas obsługi kolejnego petenta (np. po 5 minutach) urzędnik otrzymuje wydruk potwierdzenia i może go przekazać osobie czekającej przy okienku po obsłużeniu aktualnego petenta.

system-2

Taki model zwiększa – kosztem komplikacji procesu (może powstać dodatkowa kolejka, petent może opuścić budynek przed odbiorem wydruku itp.) – przepustowość systemu oraz okienka w urzędzie. Zastosowanie asynchronicznej infrastruktury do komunikacji pomiędzy komponentami systemu (np. kolejek FIFO lub magistrali ESB) spowoduje, że w momencie chwilowego przeciążenia systemu, w najgorszym wypadku, urośnie kolejka osób czekających przy okienku na potwierdzenie wniosku, jednak cały system będzie nadal funkcjonował i „kolejkował” kolejne żądania.

Dodatkowym czynnikiem zwiększającym niezawodność systemu jest fakt, że zgłoszenia będą kolejkowane nawet w czasie, gdy któryś z serwerów przetwarzających uległ awarii. Zgłoszenia te zostaną kolejno obsłużone w momencie usunięcia usterki. Mogłoby to powodować czasowe przestoje w konkretnych miastach lub województwach (w przypadku partycjonowania według miejsca zamieszkania petenta), jednak nie groziłoby paraliżem systemu w skali całego kraju.

Wnioski

Widzimy jak stosując dwa paradygmaty – partycjonowanie danych oraz asynchroniczną komunikację – możemy wpływać na wydajność i niezawodność systemu. Odbywa się to, jak zawsze, pewnym kosztem. Jest nim wzrost skomplikowania systemu. Systemy asynchroniczne są z natury bardziej złożone i trudniejsze w implementacji.

Celem niniejszego artykułu było uwidocznienie problemu ignorowania zjawiska skalowalności systemów przez dostawców oprogramowania – mam nadzieję, że udało mi się przyczynić do zwiększenia świadomości tego zjawiska oraz pokazać, że klasyczne „książkowe” metody wytwarzania oprogramowania często zawodzą w zderzeniu z prawdziwym światem, co stawia dodatkowe wyzwania dla dostawców oprogramowania.

Odnośniki

Artykuły informujące o problemach z wydajnością rządowych systemów:

Wpis na blogu Stephena Smith’a – byłego głównego architekta w firmie Sage Software – w którym autor wyjaśnia m.in. jak użycie procedur skalowalnych ogranicza możliwości skalowania systemu.

 

Dodaj komentarz