Kamil Murawski

Inżynier Systemów JEE, Vice CTO

Jak przetrwać koniec miesiąca w branży direct selling. Kilka słów o optymalizacji wydajnościowej

O tym, że zagadnienia technologiczne przekładają się na realizację celów biznesowych nie trzeba dzisiaj nikogo przekonywać. Jednak jak wiemy z doświadczenia, każda branża cechuje się charakterystycznymi tylko dla siebie wyzwaniami, które nierzadko spędzają sen z powiek inżynierom. 

W przypadku modelu direct selling takim wyzwaniem jest obsługa wzmożonego ruchu internetowego (żargonowo peaku), który przypada na koniec okresu rozliczeniowego. Jednocześnie ze względu na zmasowaną skalę sprzedaży jest to efekt bardzo pożądany, dlatego kluczowego znaczenia nabiera umiejętnie przeprowadzona optymalizacja wydajnościowa. 

Wzmożony ruch

Podczas peaku liczba użytkowników równolegle korzystających z systemu drastycznie wzrasta. Odwiedzając system realizują oni rozmaite scenariusze biznesowe, krytyczne z punktu widzenia podmiotu prowadzącego biznes direct sellingowy. To przede wszystkim eksploracja katalogu, kompletowanie olbrzymich zamówień, intensywny monitoring własnego biznesu oraz motywacja partnerów partycypujących w budowanej strukturze.

Warto zwrócić uwagę, że taka kumulacja działań na koniec okresu rozliczeniowego ma swoje uzasadnienie.  Ogromne zakupy oznaczają też wymarzony przychód, ale nie może się to odbyć bez intuicyjnych i przydatnych narzędzi dla dystrybutorów, które podczas silnego obciążenia systemu muszą pracować bez zarzutu. Ewentualne obniżenie responsywności systemu w krótkiej perspektywie czasowej jest w stanie doprowadzić do utraty tak cennych użytkowników. 

Peak - nagroda i wyzwanie

Zwiększony ruch zakupowy w systemie e-commerce jest swojego rodzaju nagrodą za skutecznie przeprowadzone kampanie marketingowe przyciągające nowych użytkowników systemu. Można śmiało powiedzieć, że bez wsparcia dystrybucyjnych instrumentów on-line byłby niemalże niemożliwy do osiągnięcia. Takie zjawisko wzmożonego ruchu na koniec okresu rozliczeniowego to nieoceniony benefit jaki otrzymujemy w poprawnie zinformatyzowanym biznesie direct selling.

Co jeśli jednak nasz system nie będzie wystarczająco responsywny? Nie od dziś wiadomo, że długi czas oczekiwania na załadowanie się strony internetowej skutecznie zniechęca użytkowników do korzystania z aplikacji. Mało tego, mogą oni próbować zaspokoić swoje potrzeby u innego sprzedawcy. Jest to bardzo poważne zagrożenie dla naszego biznesu i nigdy nie należy bagatelizować roli optymalizacji. Identyfikacja i usunięcie potencjalnych błędów spowalniających interakcje klientów z systemem musi być elementem wykonywanym stale, w regularnych cyklach z zastosowaniem bogatego aparatu nowoczesnych narzędzi wspierających profilowanie i optymalizację oprogramowania.

Optymalizacja wydajnościowa

By lepiej zrozumieć istotę tego zagadnienia przypomnijmy, czym jest wydajność systemu informatycznego. Definiujemy ją jako ilość żądań, jakie oprogramowanie może obsłużyć w jednostce czasu. W przypadku aplikacji internetowych śmiało może być ona mierzona w jednostce RPS (requests per second), albo biorąc pod uwagę fizyczny rozmiar przesyłanych danych - Mbps (megabits per second).

Drugim ważnym zagadnieniem jest responsywność oprogramowania. W przypadku świata Internetu, jest to czas, jaki użytkownik musi odczekać od momentu wysłania żądania HTTP (np. kliknięcia w łącza, wypełnienia i wysyłania formularzy) do czasu wyrenderowania odpowiedzi przez przeglądarkę. 

Pomiędzy responsywnością i wydajnością istnieje ścisła korelacja. Poprawa czasu reakcji systemu jest jednoznaczna ze zwiększeniem jego przepustowości i możliwości równoległego przetwarzania żądań. Poniżej przedstawiam, w jaki sposób podchodzimy do tego procesu w e-point.

Optymalizacja jako proces ciągły

Pierwszym krokiem do zapewnienia naszym użytkownikom systemu o wysokiej responsywności jest zrozumienie, że optymalizacja wydajnościowa to ciągły, ustrukturyzowany proces. Przeprowadzamy go w cyklach złożonych z 5 etapów:

  1. Monitoring parametrów systemu. Na tym etapie otrzymujemy informacje o stanie zdrowia systemu i ewentualnej naturze problemów wydajnościowych.
  2. Analiza przyczyn problemów. Polega na precyzyjnym znalezieniu komponentów, które nie radzą sobie przy dużym obciążeniu i ustalenie, dlaczego taka sytuacja ma miejsce oraz jak ją naprawić.
  3. Optymalizacja aplikacji. To wprowadzanie poprawek adekwatnych do diagnozy postawionej w punkcie 2. W chwili obecnej inżynierowie mają do wyboru olbrzymi wachlarz możliwości, który stale się powiększa. 
  4. Testy wydajnościowe. Umiejętnie przeprowadzone pozwalają nam poznać nie tylko granice systemu, lecz przede wszystkim zweryfikować, czy wprowadzone poprawki przynoszą oczekiwany rezultat oraz czy ostatnio dokonywane zmiany w oprogramowaniu nie przyczyniają się do spadku responsywności systemu.
  5. Wdrożenie optymalizacji do systemu produkcyjnego.

Monitoring systemu

Stare powiedzenie mówi, że łańcuch jest tak mocny, jak jego najsłabsze ogniwo. W przypadku systemów informatycznych sytuacja wygląda analogicznie. Nie można przeprowadzić skutecznej optymalizacji nie wiedząc, który element naszej infrastruktury przetwarza żądania użytkowników najwolniej i co jest tego przyczyną. Skomplikowane i duże systemu składają się z tak wielu komponentów, że bez precyzyjnego mechanizmu monitorowania wszelkie próby przyspieszenia oprogramowania przypominają błądzenie po omacku.

Dlatego w e-point budujemy bardzo dokładne systemy monitorowania i wczesnego ostrzegania dyżurujących programistów i administratorów. Dzięki nim nie tylko precyzyjnie jesteśmy w stanie zlokalizować niedomagający element istniejącej infrastruktury, ale także uprzedzić jego awarię poprzez wprowadzenie poprawek wydajnościowych, skalowanie, czy zmianę konfiguracji.

By zbudować dobry stos narzędzi monitorujących nie są potrzebne kosztowne licencje ani drogie, płatne oprogramowanie. Najważniejsza jest nasza wiedza o technologii, którą wykorzystujemy w projekcie i doświadczenie inżynierów. Doskonałe wsparcie od strony oprogramowania oferuje grupa projektów typu open source napisanych w języku Go przez InfluxData. Obejmuje ona tzw. TICK stack, czyli cztery komponenty dające pełnowartościowy mechanizm monitoringu. Należą do nich: oparty na systemie wtyczek serwer Telegraf do zbierania i zapisu danych z różnych źródeł, baza InfluxDb zoptymalizowana pod kątem przechowywania informacji o charakterze szeregów czasowych, Chronograf - narzędzie do wizualizacji (może być zastąpiony bardziej funkcjonalną Grafaną) i Kapacitor pozwalający na wysyłanie powiadomień o sytuacjach wyjątkowych. To absolutnie podstawowy i minimalny zbiór, bez którego nie powinniśmy nawet zabierać się za publikację systemu e-commerce szerszej grupie klientów.

Co należy monitorować? Absolutnie wszystkie najważniejsze, techniczne aspekty życia naszego oprogramowania. Dla zobrazowania przykładowymi obiektami mierzonymi są m.in. pule wątków, połączeń bazodanowych, stopień i charakter zużycia pamięci i procesora,  działanie mechanizmów odśmiecania, ładowania klas, kontrola blokad i zakleszczeń przy dostępie do zasobów współdzielonych oraz wiele, wiele innych. Oprócz wymienionego tutaj TICK stack’a programiści i administratorzy dysponują dzisiaj ogromem dodatkowych narzędzi do profilowania, debugowania i ekstrakcji informacji z działających systemów. Taki warsztat powinien być nieustannie doskonalony i poszerzany o nowoczesne rozwiązania.

Mechanizmy optymalizacji

Kiedy już zlokalizujemy krytyczne z punktu widzenia przetwarzania żądań użytkowników komponenty naszej infrastruktury i ustalimy przyczyny ich nieoptymalnego działania, możemy przystąpić do wprowadzenia usprawnień. Dzisiejszego inżyniera ogranicza jedynie wyobraźnia - ma do dyspozycji bardzo szeroki wybór rozwiązań, które możemy zastosować, aby polepszyć responsywność systemu.  Optymalizacja wydajnościowa to nieustanne dążenie ku doskonałości i szybkości, a poprawa parametrów systemu daje programistom ogromną satysfakcję. 

Nie sposób wymienić tutaj wszystkich technik, które są powszechnie stosowane w optymalizacji. Warto wspomnieć, że mogą one być aplikowane na różnych poziomach architektury oprogramowania. Pewne modyfikacje możemy wprowadzać do samego kodu źródłowego np. integracja z mechanizm cache, programowanie reaktywne, systemy bezpieczników typu hystrix, asynchroniczne przetwarzanie z wykorzystaniem message brokerów. Inne mogą dotyczyć optymalizacji samej bazy danych, poprzez wprowadzanie widoków, indeksów, zaawansowaną konfigurację DBM-a, stosowanie w kodzie niestandardowych poziomów izolacji transakcji, zapytań zmaterializowanych, a nawet definiowania ich własnego rozbioru. Ostatnimi czasy przeprowadzane są także dekompozycje systemów monolitycznych na łatwo skalowalne mikrousługi, a nawet separacje odczytu i zapisu - tzw. CQRS. Sięgać można jeszcze dalej. Powszechnie wykorzystywane są systemy typu CDN (Content Delivery Network) zapewniające mechanizm cache dla podstron naszych systemów w rozproszonej lokalizacji geograficznej, a także ochronę przeciw atakom DDoS. 

Wymienione mechanizmy optymalizacyjne to oczywiście zaledwie kilkanaście możliwości z ogromu działań, jakie możemy przeprowadzić, by usprawnić responsywność. Warto mieć świadomość, że każda z nich musi być dobierana przez doświadczonego inżyniera i dostosowana do problemów o konkretnej naturze. Wówczas możemy liczyć na naprawdę imponujący efekt.

Testy wydajnościowe

Nie jest łatwo stworzyć dobry test wydajnościowy. By dostarczał nam rzetelnych informacji o odporności systemu na obciążenie musi spełniać kilka istotnych warunków. Pierwszym, zdecydowanie najważniejszym jest możliwie najwierniejsze odzwierciedlenie zachowań użytkowników podczas wzmożonego ruchu. W celu przygotowania takich scenariuszy musimy posiłkować się eksploracją logów serwerów HTTP i precyzyjnie dobrać proporcje w przykładaniu obciążenia do poszczególnych scenariuszy biznesowych. W e-point do automatyzacji tego procesu napisaliśmy szereg skryptów, które z powodzeniem możemy stosować w różnych projektach.

Bardzo ciekawą cechą dobrze przygotowanych testów wydajnościowych jest ich tendencja do maksymalnie powtarzalnego odtwarzania błędów synchronizacji dostępu do sekcji krytycznych w kodzie współbieżnym. Wszelkie zagłodzenia oraz zakleszczenia na etapie przedprodukcyjnym mogą być wykrywane tylko w ten sposób. Pozwala to na przygotowanie się do bezawaryjnej obsługi wzmożonego ruchu na dużo wcześniej niż system osiągnie upragnioną popularność.

Nigdy nie należy zapominać, że testy wydajnościowe są równie ważne jak wszystkie inne rodzaje: manualne, jednostkowe, integracyjne. Nie powinny być zaniedbywane. Rekomenduję by proces ich dostrajania i pielęgnacji  był przeprowadzany w sposób ciągły, a uruchomienie miało miejsce przed każdym wdrożeniem produkcyjnym. Koniecznością jest także, aby dostawca oprogramowania dysponował odpowiednimi maszynami, z których można takie testy przeprowadzać doprowadzając do padu obciążanego systemu, tym samym poznając jego granice i lokalizując najmniej wydajne elementy. Jest to nieoceniona wiedza z punktu widzenia planowania rozbudowy infrastruktury systemu.

Paradoks optymalizacji - im lepsza tym mniej widoczna

Na zakończenie chciałbym podzielić się pewną refleksją na temat optymalizacji wydajnościowej z punktu widzenia biznesu. Zagadnienie to jest technologicznie bardzo skomplikowane i mimo bogatego wachlarza narzędzi wspierających, inżynierowie muszą poświęcić sporo czasu by dobrze usprawniać budowane systemy. Czas ten nie jest zakończony dostarczeniem klientowi nowych funkcji systemu, więc dla mało doświadczonego managera może wydawać się stracony. Nic bardziej mylnego. Nieustannie optymalizując oprogramowanie, gwarantujemy naszym klientom spokój i pewność, że gdy szał zakupowy sięgnie zenitu, nic nie przeszkodzi klientom w korzystaniu z oprogramowania, a uzyskany przychód będzie stanowił ukoronowanie podjętych wysiłków.