Dlaczego testy oprogramowania nie chronią przed błędami?

W dniu, w którym Facebook debiutował na giełdzie, ukryty w kodzie Nasdaq błąd programowania nagle dał o sobie znać, uniemożliwiając wpłynięcie zamówień i powodując jednocześnie ich powtórne przesłanie. Giełda straciła 10 mln USD w wyniku grzywny nałożonej przez amerykańską Komisję Papierów Wartościowych i Giełd (SEC) i 40 mln w ramach roszczeń odszkodowawczych. Ucierpiała też jej reputacja. Jak to możliwe, że tak poważny błąd programowania nie został wykryty podczas testów?

Niektóre błędy, zwłaszcza powodowane przez tzw. wyścig (race condition), który może wystąpić zwłaszcza w przypadku programowania współbieżnego są niemal niewykrywalne podczas testów. By je wykryć, często nie wystarczy sto, ani nawet tysiąc prób testowania. A w opisanym wyżej przypadku błąd nie został wykryty, ani nie ujawnił swojej obecności przed sądnym dniem debiutu Facebooka na giełdzie.

Tykające bomby współbieżności

Oprogramowanie współbieżne zawierające kod o nieprzewidywalnych warunkach wykonania operacji to ukryta bomba zegarowa, czekająca na właściwy moment, aby eksplodować. Przez wiele lat może „leżeć” i tykać, dopóki nie pojawi się zbieg okoliczności, który wywołuje spektakularny wybuch.

Zobacz również:

  • Wszystkie problemy macOS 14.4 - lepiej nie aktualizować Maców

Ogólnie, aby zapewnić wysoki poziom wydajności i krótki czas oczekiwania, kod aplikacji działa na dwóch lub więcej rdzeniach procesora z wieloma strumieniami instrukcji działającymi w tym samym czasie. Na przykład jeden strumień zapisuje dane w pamięci, a drugi je odczytuje. Zazwyczaj zapis następuje przed odczytem. Ale zdarza się, że strumień odpowiedzialny za zapis danych nie wykona tego zadania na czas. A wtedy drugi strumień odczyta dane zanim ich odpowiedni zestaw zostanie zapisany. To właśnie sytuacja określana jako tzw. wyścig (race condition). Większa lub mniejsza prędkość, z jaką każdy strumień wykonuje zadania może wpłynąć na wynik końcowy.

Programy współbieżne same decydują co, kiedy i gdzie ma się dziać

W przypadku procesorów z jednym rdzeniem takiego problemu nie ma. Natomiast, gdy mamy do czynienia z procesorami wielordzeniowymi, w których strumienie danych wykonują zadania jednocześnie, wynik nie zawsze jest taki sam. Skąd pewność, że wyścig zawsze wygra ten sam strumień? Nie zawsze tak się dzieje. Aplikacje współbieżne nie zachowują się w sposób deterministyczny, czyli nie zawsze generują te same rezultaty.

Aby zrozumieć, dlaczego tak jest, należy pamiętać, że programista nie ma pełnej kontroli nad wszystkimi elementami środowiska, w którym działa aplikacja. Wykonanie zadania przez aplikację określa harmonogram na niskim poziomie, który sam decyduje o tym, który element programu ma zadziałać i kiedy. Twórca kodu nie ma do tego dostępu. Harmonogram odpowiada też za prezentowanie danych i instrukcji oraz przesyłanie informacji z pamięci podręcznych i do nich.

W praktyce, za każdym razem, gdy aplikacja wykonuje zadanie, warunki i otoczenie jest inne. W efekcie problem może ujawnić się raz na dziesięć tysięcy operacji wykonujących to samo zadania.

Wykrycie tego podczas programowania jest bardzo trudne. Nawet jeśli test wykryje błąd powodujący awarię aplikacji, w środowisku nie-deterministycznym, może on nigdy nie zostać powtórzony. Podczas ponownego przeprowadzania testu w podobnych warunkach, często okazuje się, ze aplikacja działa bezbłędnie.

W celu komercyjnej reprodukcji treści Computerworld należy zakupić licencję. Skontaktuj się z naszym partnerem, YGS Group, pod adresem [email protected]

TOP 200