Klient naciska, by program nigdy, pod żadnym pozorem się nie crashował, nawet, jeśli są wyjątki wynikłe z bugów

2

Sytuacja: łączenie strony ecommerce (woo, presta, itp) z systemem ERP. W uproszczeniu: na stronie ecommerce klient sklepu składa zamówienie, obsługa sklepu gapi się w ERP, kiedy tam pojawi się nowe zamówienie to zaczynają je realizować. Język (to narzucone z góry) C#. Nasz klient: małe sklepy.

Ciągle słyszę, że najgorsze, co się może zdarzyć, to nieprzeniesienie jakiegoś zamówienia. Szef żąda, by KAŻDY(!) wyjątek był bezwarunkowo łapany, zapisywany do logów i ignorowany. Nawet systemowy.

Jest to radykalnie sprzeczne ze zwykłą mądrością programistyczną, wedle której wyjątki (a) systemowe oraz (b) wynikłe z bugów w moim programie winny spowodować early fail. Jest to też radykalnie sprzeczne z domyślnym zachowaniem C#, gdzie wyjątki domyślnie przerywają normalny bieg programu i, o ile nie są złapane, crashują apkę.

Łapanie wyjątków systemowych to głupota - przecież kiedy taki poleci, to choćbym napisał sto tysięcy linijek kodu obsługującego błędy, nie jestem w stanie naprawić sytuacji. Kiedy poleci OutOfMemoryException to program przecież musi się scrashować, siła wyższa.

Ale co w kwestii wyjątków wynikłych z bugów w programie? (NullReferenceException, IndexOutOfRangeException, itp) Rozmawiałem z szefem i z klientem i muszę przyznać, że uznaję ich argumenty.

Ich mentalność jest mniej więcej taka:

Załóżmy, że spada zamówienie, ale jego przeniesienie do ERP generuje wyjątki. Co się wtedy dzieje w najlepszej możliwej sytuacji? Obsługa sklepu czyta logi, dzwoni do supportu, następnego dnia przychodzi człowiek z supportu, bada sytuację, aha jest bug, naprawimy, znowu następnego dnia powstaje nowa wersja naszej apki, umawiamy się na wdrożenie, mija kilka dni co najmniej zanim sklep zacznie wreszcie realizować zamówienie, klient sklepu wściekły. Nie możemy pozwalać sobie na powodowanie kilkudniowych przestojów w działalności sklepu!

Bardziej realistycznie: nikt nie czyta logów, nikt nie wie nawet, że program się crashuje, obsługa sklepu gapi się tylko w ERP, tam nie ma zamówień bo nie są przenoszone ze strony ecommerce, więc popijają herbatkę. I to trwa tydzień, zanim ktoś wreszcie wpadnie na pomysł by sprawdzić, co się dzieje.

Zwróć uwagę YetAnohterone, że to są małe firmy, nie zatrudniające często adminów na pełen etat. Cała załoga firmy to często ludzie nietechniczni. Oczekują, by program, po instalacji, pozwolił o sobie zapomnieć. Ostatnią rzeczą, jaką sobie życzą, jest bycie molestowanymi komunikatami o błędach, z którymi mają coś robić.

Co się powinno stać zamiast odmowy przeniesienia zamówienia albo (co gorsza) crasha: błąd winien zostać zapisany do logów, ale zamówienie winno zostać przeniesione do ERP, choćby połowicznie. Support jest automatycznie informowany, że poleciał błąd, więc zaczyna prace nad diagnozą i naprawą. Adres odbiorcy zawiera śmieci? Nic strasznego, sklep może zacząć kompletować zamówienie, a adres może zostać poprawiony chociażby ręcznie. W międzyczasie support naprawi błąd i następne podobne przypadki nie będą źle przenosić adresu. Brakuje jakiejś pozycji na zamówieniu? Sklep przeprosi i dośle klientowi brakujący towar. Cena na fakturze jest zła? Wystawi się fakturę korygującą. Wszystko to, choć niepożądane, jest o niebo lepsze, niż odmowa przeniesienia zamówienia.

Bug oczywiście trzeba naprawić, ale niedopuszczalne jest, by pojawienie się buga blokowało pracę załogi sklepu.

Nie mam argumentów na coś takiego. Mentalność programistów jest zazwyczaj taka, by za wszelką cenę nie dopuszczać do błędnego działania programu, jeśli trzeba, to ubijając program. Ale mentalność managmentu jest taka, by za wszelką cenę nie dopuszczać do przerwy w działaniu programu, jeśli trzeba, to poświęcając poprawność zwracanych danych. To jest chyba decyzja biznesowa, a nie techniczna, by zważyć, która z tych opcji jest mniej szkodliwa. Więc chyba nawet nie jestem odpowiednią osobą, by forsować dogmat, że należy robić early fail.

Ale jak mam to zrobić w C#? Każdą linijkę kodu otaczać w try/catch?? To absurd.

Tak sobie pomyślałem, że gdybym pisał w PHP, to byłoby łatwiej się z tym chrzanić, bo tam można ustawić domyślne ignorowanie błędów, ale - oczywiscie - nikt tego w poważnych projektach nie robi.

Trochę nie wiem, co mam z tym zrobić.

Side note: Zdaje się, że nie jestem sam z tym problemem. Widziałem już w internecie osobę, która twierdziła, że tez pisała połączenie między ecommerce i erp i miała dokładnie takie same wymogi biznesowe.

5

Zapewnij szefa że cena projektu wzrośnie x100, bo potrzebny będzie też UPS i alternator a i zapas paliwa na 10 lat, no i oczywiście bunkier odporny na atak atomowy bo wiadomo - Putin, a i jeszcze człowiek który zgodzi się w tym bunkrze w odcięciu żyć przez 30 lat bo wiadomo pandemie, trzeba by też coś poradzić na bardziej tragiczne w skutkach zdarzenia typu czarne dziury i spontaniczne obniżenie się energii próżni...

EDIT: A teraz na poważnie - powiedz szefowi żeby ustalił SLA - czyli procent czasu w którym aplikacja działa bez problemowo być może 99% oraz śledzenie tego za pomocą metryk przekona szefa do zmiany zdania. SLA to sprawdzona praktyka, każdy dostawca chmurowy podaje np. S3 ma 99.99% availability i znacznie więcej 9'tek durability. Generalnie to co się integruje z systemem też musi być mądre w jakiś sposób i jak dostanie 500kę to powinno przynajmniej powtórzyć żądanie z 2 lub 3 razy...

6
0xmarcin napisał(a):

Zapewnij szefa że cena projektu wzrośnie x100, bo potrzebny będzie też UPS i alternator a i zapas paliwa na 10 lat, no i oczywiście bunkier odporny na atak atomowy bo wiadomo - Putin, a i jeszcze człowiek który zgodzi się w tym bunkrze w odcięciu żyć przez 30 lat bo wiadomo pandemie, trzeba by też coś poradzić na bardziej tragiczne w skutkach zdarzenia typu czarne dziury i spontaniczne obniżenie się energii próżni...

Błąd argumentacyjny, o nazwie "równia pochyła".

Jego szef chce po prostu wyciszyć wszystkie błędy w aplikacji, na poziomie integracji backendu z webówką. To jest normalny requirement.

@YetAnohterone Odniosę się do Twojej wypowiedzi tak:

Ciągle słyszę, że najgorsze, co się może zdarzyć, to nieprzeniesienie jakiegoś zamówienia. Szef żąda, by KAŻDY(!) wyjątek był bezwarunkowo łapany, zapisywany do logów i ignorowany. Nawet systemowy.

To co powinieneś zrobić to:

  • Przyznać rację szefowi, kiedy mówi Ci że najgorsze co się może stać to nieprzeniesienie zamówienia. To on decyduje co jest ważne a co nie.
  • Szef żąda, by KAŻDY(!) wyjątek był bezwarunkowo łapany, zapisywany do logów i ignorowany. Nawet systemowy. - tutaj mówisz szefowi, że Ty ogarniesz requirement, i żeby nie mówił Ci jak masz pracować.

Szef, jeśli nie jest techniczny, to nie wie jak coś zrobić dobrze.

Twoja rola tutaj jest taka: Musisz dołożyć wszelkich starań, żeby nie było czegoś takiego jak nieprzeniesione zamówienia. Musisz wyliczyć wszystkie możliwe przyczyny nieprzeniesienia zamówienia, i zapobiec każdej z nich w taki sposób, w jaki Ty znasz za najlepszy. Jeśli szef da Ci jakiś pomysł ogarnięcia tego, z którym Ty się nie zgadzasz, to musisz mu to zakomunikować: "Rozumiem co chcesz osiągnąć, ale zrobimy to inaczej". Jeśli szef znajdzie jakiś case, gdzie uda mu się nieprzenieść zamówienia, dodaj to do swojej listy usecaseów, i ogarnij.

YetAnohteronenapisał(a):
stivens napisał(a):

A macie testy? — stivens 40 minut temu

Nie, ale to decyzja szefa, nie moja.

Bzdura! Oczywiście, że to jest TWOJA decyzja i tylko Twoja. Jeśli uznasz, że testy są potrzebne to je dodaj. Testy to jest narzędzie developerskie. Żaden szef nie powinien Ci wtykać nosa i mówić jak masz wykonywać swoją pracę. Jeśli testy to droga do odpowiedniej implementacji, to je wykonaj.

1

Swoja to droga to ja osobiscie bylbym bardziej wsciekly jakbym zaplacil i nie dostal towaru. Jak aplikacja mi uczciwie mowi, ze jest blad to jaki problem? :)

4

IMHO to jest rozmowa na moim poziomie z mechanikiem samochodowym, przy czym totalnie nie znam się na autach. Powinieneś wyjść z inicjatywą i znaleźć „middle ground”.

Pokaz klientowi/szefowi jakie SLA ma AWS - to nie jest 100%. Z drugiej strony trzeba podrazyc jaka jest faktyczna potrzeba (łapanie wyjątków i ignorowanie to na pewno nie jest cel sam w sobie) i skierować rozmowę na właściwe tory - czyli zarządzenie ryzykiem (prawdopodobieństwo i impakt danego scenariusza) i jak ma działać recovery.

EDIT: pare przemyśleń i wniosków z dyskusji pod poniższym postem:

  1. Żadne testy nie wykazują poprawności systemu. Pokazują, że system realizuje nasze założenia, które testujemy.
  2. Użytkownika nie obchodzi, jaka cześć systemu nie zadziałała. Jak paczkomat nie chce mi otworzyć drzwiczek, to nie interesuje mnie, że jakiś 3rd party komponent miał timeout. Z tego względu trzeba zadbać o całość UX, czego nie da się zrobić testami automatycznymi.
  3. Na mockach zadziała większość happy path. W integracjach często sypią się rzeczy typu tokeny, klucze, certyfikaty, timeouty, niestabilność czy limity API. Trzeba to przeklinać i monitorować.
  4. Testy automatyczne są mega istotne, ale nie wystarczające w całym procesie QA. Nie klikacie swoich rzeczy na produkcji? Ile razy zdarzyło się, ze jednak coś nie działa?
  5. Nie wystarczy powiedzieć klientowi, ze coś działa w 99%, bo dla niego ważny jest ten 1% - trzeba transparentnie pokazać jakie tam czają się ryzyka i jak je mitygujemy. Nikt nie chciałby być w tym 1% i podświadomie zakłada, ze nigdy nie będzie. Tak działa człowiek.
4

Może zaproponuj alternatywę, która rozwiąże problem, ale w inny sposób? Skoro wszystko sprowadza się do tego by nie pominąć zamówienia, dopisać funkcjonalność, która sprawdzi, czy wszystkie przeszły.

W końcu logi ktoś będzie musiał sprawdzać, więc jakieś opóźnienie w realizacji są dopuszczalne. Więc trzeba dać narzędzie użytkownikowi, aby to weryfikował.

1

Wydaje mi się, że musisz się wybitnie nagimnastykować aby pomimo głupot od usera (klient ecommerca) ERP to przyjął (naprawa danych?)

Dodać do tego jakieś retry mechanismy, aby m.in sieć nie powodowała że coś nie przejdzie

Dodać do tego jakiś przejrzysty interfejs który pozwoli użytkownikowi obsługującemu (twój klient) dowiedzieć się co się dzieje z danym zamówieniem z ecommerce np. nie przechodzi bo klient nie dał NIPu, na pewno nie przeglądanie logs.txt

Monitorowanie zmian w każdym z 3rd systemów, aby reagować i testować gdy robią zmiany.

3

@YetAnohterone:

Załóżmy, że spada zamówienie, ale jego przeniesienie do ERP generuje wyjątki. Co się wtedy dzieje w najlepszej możliwej sytuacji? Obsługa sklepu czyta logi, dzwoni do supportu, następnego dnia przychodzi człowiek z supportu, bada sytuację, aha jest bug, naprawimy, znowu następnego dnia powstaje nowa wersja naszej apki, umawiamy się na wdrożenie, mija kilka dni co najmniej zanim sklep zacznie wreszcie realizować zamówienie, klient sklepu wściekły. Nie możemy pozwalać sobie na powodowanie kilkudniowych przestojów w działalności sklepu!

[...]

Nie rozumiem. Jest zrobiona integracja ERP --> System sklepowy. Integracja została zrobiona bazując na jakichś ustalonych wymaganiach. Po nich następuje walidacja i zaczytanie zamówienia.

Co się powinno stać zamiast odmowy przeniesienia zamówienia albo (co gorsza) crasha: błąd winien zostać zapisany do logów, ale zamówienie winno zostać przeniesione do ERP, choćby połowicznie. Support jest automatycznie informowany, że poleciał błąd, więc zaczyna prace nad diagnozą i naprawą.

W przypadku błędu krytycznego powinna być możliwość ręcznego zaczytania takiego wadliwego zamówienia i możliwość ewentualnej poprawy nieprawidłowych danych. Potem błąd jest zgłaszany, poprawiany a zamówienie jest realizowane w tym czasie.

Nie mam argumentów na coś takiego. Mentalność programistów jest zazwyczaj taka, by za wszelką cenę nie dopuszczać do błędnego działania programu, jeśli trzeba, to ubijając program. Ale mentalność managmentu jest taka, by za wszelką cenę nie dopuszczać do przerwy w działaniu programu, jeśli trzeba, to poświęcając poprawność zwracanych danych.

Wydaje mi się że błędnie/mylnie to interpretujesz. Klientowi nie chodzi o to aby ignorować błędy i aby za wszelką cenę je zasysać, tylko aby nie było przerwy w działaniu biznesu, bo na tym traci szmalec. Klient nie wie jak to przedstawić, więc stara się mówić takim językiem aby to było dla Was zrozumiałe ;)

Pamiętaj, że klienta nie obchodzi jak to ma pod maską działać, tylko nie chce tracić szmalcu, a obsuwa przy imporcie zamówienia powoduje straty - chodzi o to aby ewentualne błędy w jego imporcie nie powodowały obsuw.

Brakuje jakiejś pozycji na zamówieniu? Sklep przeprosi i dośle klientowi brakujący towar.

Tylko to nie jest błąd systemu...Ehhh System ERP powinien miec jakiś use case biznesowy który bo umiał obsłużyć takie zamówienia - np. wydzielić pozycje na chwilę obecną niedostępne do oddzielnego zamówienia i dać możliwość zdecydować klientowi co chce zrobić - czekać na całość czy np. tylko na chwilowo niedostępne rzeczy czy co innego jeszcze.

Cena na fakturze jest zła? Wystawi się fakturę korygującą.

No dokładnie. Realizacja i skorygowanie dokumentów aby nie zatrzymać procesów biznesowych.

Wszystko to, choć niepożądane, jest o niebo lepsze, niż odmowa przeniesienia zamówienia.

No tak. Wyobraź sobie, że sklep dostaje zamówienie na 20k i przez np. problem w adresie nie może go zrealizować, a by wystarczyło tylko dać wybór - wczytać bez adresu i uzupełnić go po stronie ERPa.

Wydaje mi się że brakuje wam osób biznesowych ze znajomością branży, które będą w stanie opisać zachodzące procesy biznesowe.

0
Charles_Ray napisał(a):

to naiwne podejście - nie uwzględniasz tysięcy rzeczy, jak np nieważny token, timeouty, błąd w kontrakcie itd Takie rzeczy i tak potrafią wyjść podczas przekliku i wiem to z doświadczenia przy integracjach a nie teoretyzowania :)

No przecież nie mówię, że jak sprawdzisz coś, to to będzie zawsze działać 100% czasu :| I jak takie testy przejdą, to nie dam sobie uciąć ręki, żę system działa.

na mockach wszystko świetnie działa a w szczególności happy path! :)

Na mockach nie przetestujesz integracji, to prawda, ale przetestujesz swoją aplikację. To ciągle jest użyteczne - nie tak użyteczne jak testy na prawdziwej aplikacji, ale użyteczne nadal.

Mam wrażenie że prezentujesz taką dziwną dychotomię. Wydaje mi się że przyjmujesz postawę: "Albo napisz testy które wykryją 100% przypadków, będą idealnie bezbłędne, i wykryją każdy jeden najbardziej hardkorowy przypadek; albo rób manualnie".

To jest niezdrowe podejście. Testuj aplikację, manualnie i automatycznie. To co da się przetestować automatycznie - zautomatyzuj, to czego się nie da - testuj nadal ręcznie.

A odnosząc się do szczegółu Twojej wypowiedzi, nie uwzględniasz tysięcy rzeczy, jak np nieważny token, timeouty, błąd w kontrakcie itd. Czyli o co chodzi konkretnie? Że jeśli użyję w aplikacji niepoprawnego tokenu, to mój test miałby tego nie wykryć? Na mockach oczywiście nie, na prawdziwej integracji automatyczny test to wykryje. Że integracja zwróci timeout? To nie rozumiem co niby miałoby się stać? Napisz test, który specjalnie timeoutuje i zobacz czy Twoja aplikacja poprawnie to obsłuży. Błąd w kontrakcie? Again, mockami tego nie wykryjesz, na prawdziwej integracji tak. - dlatego dobrze jest korzystać z API które udostępnia testowe endpointy.

Nie rozumiem w ogóle do czego dażysz? Że nie warto pisać testów automatycznych, bo znajdą się przypadki kiedy nie przetestujesz wszystkiego? To absurd.

4

Ja bym się zastanowił nad takim rozwiązaniem jak w pierwszej kolejności zapisanie danych raw, a dopiero potem je wrzucić w odpowiednie tabele, jak się wysypie to zawsze są dane raw, które można zinterpretować.

10

Tu mi się przypomina historia sprzed ponad 20 lat. Pracowałem w jednym średniej wielkości januszexie i po prostu nie dawaliśmy sobie w pewnym momencie rady z zarządzaniem bugami (przylatywały do programistów ustnie, excelami i mailami).
Zaproponowałem wprowadzenie bugzilli, żeby jakoś zarządzać tymi błędami (bugzilli (taka JIRA tamtych czasów tylko 100 razy gorsza) - wtedy nie było wiele alternatyw).
W odpowiedzi szefostwo wprowadziło zakaz popełniania błędów przez programistów - i już nie była potrzebna bugzilla, i już nie było błędów - problem rozwiązany.

W ogóle:

A macie testy?
Nie, ale to decyzja szefa, nie moja.

To się nadaje na oprawienie w ramkę.

Brak testów razem z nakazem ignorowania wyjątków ma sens i oznacza dość dokładnie: robimy soft najgorszej możliwej jakości. Poprawność działania nie ma żadnego znaczenia. Niespójne z tym jest tylko pisanie w C#, bo należałoby się przenieść na jakąś platformę gdzie kompilator nie przeszkadza w wypuszczeniu dowolnego kodu na produkcję (JS, albo PHP byłyby jednak bardziej spójne z resztą decyzji).

1
mr_jaro napisał(a):

Ja bym się zastanowił nad takim rozwiązaniem jak w pierwszej kolejności zapisanie danych raw, a dopiero potem je wrzucić w odpowiednie tabele, jak się wysypie to zawsze są dane raw, które można zinterpretować.

Dobry pomysł.

1

Ciągle słyszę, że najgorsze, co się może zdarzyć, to nieprzeniesienie jakiegoś zamówienia.

Co to znaczy nieprzeniesienie zamówienia?

Jest to radykalnie sprzeczne ze zwykłą mądrością programistyczną, wedle której wyjątki (a) systemowe oraz (b) wynikłe z bugów w moim programie winny spowodować early fail.

To może i jest mądrość programistyczna ale dla studenta który pisze program na zaliczenie. W realu, gdy program działa u klienta który zapłacił za program pierdyliard cebulionów i myśli że ma najlepszy soft jaki cebuliony mogą kupić apka broń boże nie może crashować na produkcji (chyba że klient świadomie zamówił funkcję "tak crashujmy jak błąd" - jeszcze tego jednak nie widziałem). To jest zwykłą mądrość biznesowa w IT - apka działa świetnie i nigdy nie crashuje (a jak coś nie działa to popatrzmy w logi).

Ja bym przyznał szefowi rację. (mówisz o szefie szefie, czy to jakiś manager pod szefem?)

0
Patryk Mieleszko napisał(a):

Jest to radykalnie sprzeczne ze zwykłą mądrością programistyczną, wedle której wyjątki (a) systemowe oraz (b) wynikłe z bugów w moim programie winny spowodować early fail.

To może i jest mądrość programistyczna ale dla studenta który pisze program na zaliczenie. W realu, gdy program działa u klienta który zapłacił za program pierdyliard cebulionów i myśli że ma najlepszy soft jaki cebuliony mogą kupić apka broń boże nie może crashować na produkcji (chyba że klient świadomie zamówił funkcję "tak crashujmy jak błąd" - jeszcze tego jednak nie widziałem). To jest zwykłą mądrość biznesowa w IT - apka działa świetnie i nigdy nie crashuje (a jak coś nie działa to popatrzmy w logi).

Ja bym przyznał szefowi rację. (mówisz o szefie szefie, czy to jakiś manager pod szefem?)

Bzdura. To programista wie jak wykonać pracę dobrze, a nie ten kto ją zleca.

12

Myślę, że jest tu podstawowy brak zrozumienia potrzeb klienta. Moim zdaniem wymaganie klienta jest takie, że problemy integratora mają nie blokować procesu sprzedażowego w sklepie internetowym. Czyli ma to działać asynchronicznie. Zamówienie ze sklepu zapisujesz w bazie. A potem na ich podstawie robisz import do ERP. Jak ERP czegoś nie zatwierdzi to wówczas oznaczasz zlecenie błędem, jaki dostałeś z ERP. Pracownik sprawdza takie zamówienie i je obsługuje. Probolem zanany i jak poczytasz trochę o wzorcach integracyjnych itp to będziesz wiedział jak to rozwiązać.

4
YetAnohterone napisał(a):

Jest to też radykalnie sprzeczne z domyślnym zachowaniem C#, gdzie wyjątki domyślnie przerywają normalny bieg programu i, o ile nie są złapane, crashują apkę.

Domyślnie w języku istnieją mechanizmy do obsługi wyjątków, domyślnie po to, aby ich używać.

Co się powinno stać zamiast odmowy przeniesienia zamówienia albo (co gorsza) crasha: błąd winien zostać zapisany do logów, ale zamówienie winno zostać przeniesione do ERP, choćby połowicznie.

Co to znaczy "połowiczne przeniesienie zamówienia"?

Adres odbiorcy zawiera śmieci? Nic strasznego, sklep może zacząć kompletować zamówienie, a adres może zostać poprawiony chociażby ręcznie. W międzyczasie support naprawi błąd i następne podobne przypadki nie będą źle przenosić adresu. Brakuje jakiejś pozycji na zamówieniu? Sklep przeprosi i dośle klientowi brakujący towar. Cena na fakturze jest zła? Wystawi się fakturę korygującą. Wszystko to, choć niepożądane, jest o niebo lepsze, niż odmowa przeniesienia zamówienia.

Bardzo dziwny jest ten proces migracyjny, skoro wykrywa błędne adresy i ceny.

Ale jak mam to zrobić w C#? Każdą linijkę kodu otaczać w try/catch?? To absurd.

Każdej nie, tylko te, które jesteś w stanie natychmiast obsłużyć.
Wyjątki np. komunikacji sieciowej jest sens ponawiać przy użyciu narzędzi typu Polly.
Poza tym musisz mieć jakąś globalne logowanie błędów, żeby zarejestrować jeśli cokolwiek się przemknie.
No i musisz mieć dokładne logowanie każdego etapu procesu, żeby móc poinformować support, od jakiego etapu trzeba wznowić migrowanie ręczne.

Mam ogólnie wrażenie, że nie odróżniasz przerwania jakiegoś procesu biznesowego od scrashowania się aplikacji.

2

Nie, ale to decyzja szefa, nie moja.

@YetAnohterone co? xD To trochę tak jakbyś napisał że klepiesz wszystko w main bo decyzją szefa nie wolno ci pisać funkcji. Zadaniem specjalisty od programowania jest decydowanie o tym jak piszesz. Szef może najwyżej decydować o zakresie funkcji systemu, o tym co masz zaimplementować.

  1. Wracając do głownego pytania, masz chyba bardzo słabą wyobraźnie, szef podobnie. Bo co jeśli błąd sprawia że dane są corrupted i "przeniesiesz" zamówienie które zamiast 1 artykułu ma ich 128 i sklep skompletuje komuś takie zamówienie i każde za to płacić? Albo jeśli wyciszysz błąd potwierdzenia płatności a user ją anulował? Kto będzie ponosić koszty takich wpadek?

Obkładanie wszystkiego try..catchami to pomysł bez sensu. Musicie jasno zdefiniować z klientami w wymaganiach co się ma stać w przypadku każdej przewidywalnej wpadki. Dla tych trudnych do przewidzenia (np. OOM) jedyna poprawna droga to shutdown i najwyżej niech klient zrobi reset aplikacji, a ta powinna rozpoząć pracę od miejsca w którym stanęła. Albo w ogóle sprzedawać jakieś rozwiązanie klastrowane z hot-spare i load balancerem i crash jednego noda jest niewidzialny dla klienta, bo drugi node przejmuje requesty,a pierwszy się resetuje.

11

Właśnie dlatego mówi się tyle o soft skillach z IT. 2 strony i tylko @UglyMan dotarł do sedna problemu. Klientowi wcale nie zależy na tłumieniu wyjątków, kończeniu procesu za wszelką cenę itp. Biznes ma prostą potrzebę - WSZYSTKIE zamówienia złożone w sklepie muszą pojawić się w ERP. Jest to bardzo logiczna potrzeba. Drugie doświadczenie klienta jest takie, że odkręcenie błędu jest długotrwałe bo albo obsługa nie widzi/nie reaguje na błędy w integracji, albo programistom zajmuje kilka dni odkręcenie tego i jeszcze pewnie przy okazji blokuje kolejne zamówienia.

Klient wymyślił sobie rozwiązanie (albo co gorsza przestawiono mu dwa - albo pomijamy albo importujemy z błędem) i z opcji, które zna wybiera import z potencjalnymi błędami bo to dla niego mniejsze zło.

A rozwiązań jest na pewno więcej. Jedna z opcji to na przykład jasne komunikaty w panelu obsługi zamówień, że jakieś zamówienie utknęło i na przykład możliwość wprowadzenia go ręcznie i zmapowanie. Zapewne w waszym workflow możecie wymyślić 3-4 inne sposoby rozwiązania problemu oraz przestawić konkretne ryzyka związane z błednymi importami typu wysyłka zamówień nieopłaconych, niekompletnych etc. Bardzo ważne też jest zrozumienie ograniczeń obsługi systemu - system często po drugiej stronie obsługują osoby, które nie podejmą samodzielnie akcji dopóki ich coś do tego nie zmusi. Na przykład ja często proponuje przypisanie jednego pracownika na którego imiennie przypisywane są takie "dziwne" sytuacje i jak bierze urlop to wymuszenie przekierowania ich na kogoś innego, bo inaczej nikt ich nie podejmuje.

Także tak jak @UglyMan mówi zrozumcie "co boli" Waszego klienta i nie dyskutujcie z nim na poziomie kodu/ignorowania wyjątków etc tylko na poziomie jego potrzeb biznesowych. Jak zrozumiecie co jest źródłem problemu to możecie wymyślić jakieś rozwiązanie zgodne ze sztuką ale i potrzebami klienta. Inaczej fiksujecie się na 2 opcjach.

2

To czego od ciebie oczekują, to nie "100% dostępności" i "0% carsh". Chcą, żeby było "no data loss". Jak można coś takiego osiągnąć:
Klient składa zamówienie. Dane zamówienia są synchronicznie pakowane w kolejkę (a w rzeczywistości więcej kolejek) - jeżeli się uda, klient dostaje potwierdzenie, że złożono zamówienie. Jeżeli się z jakiegoś powody nie udało, klient dostaje informację, że "nie udało się złożyć zamówienia"

Po drugiej stronie masz swoje ERP, do którego jakimś ETL próbujesz załadować dane:
robisz peek wiadomości (podgląd + czasowa rezerwacja)
przetwarzasz i pakujesz do ERP
jeżeli się uda, to to puszczasz ACK na kolejkę, wiadomość zostaje skonsumowana (znika z kolejki, lub przestaje być dostępna)

Do tego dorabiasz sobie:
Geograficzną nadmiarowość dla kolejek, czyli masz 2 kolejki, ładujesz na nie tą samą wiadomość, do tego masz na kolejkach ustawioną wzajemną replikację, bo jeżeli padnie ci kolejka, to możesz stracić dane. W przypadku 2 kolejek szansa na jednoczesny pad jest mniejsza.

Dorabiasz jakiś monitoring dla wiadomości zalegających w kolejce.

Na wszelki wypadek dorabiasz monitoring syntetyczny, sprawdzający np. czy liczba zamówień złożonych w sklepie / płatności zgadza się z liczbą przyjętych w ERP.

3
YetAnohterone napisał(a):

Kiedy poleci OutOfMemoryException to program przecież musi się scrashować, siła wyższa.

No niekoniecznie, jeżeli nagle robisz wielką alokację to możesz się pogodzić z faktem że sfailowała i… nie wiem, wyświetlić message boxa że nidyrydy?

Inna sytuacja jeżeli system jest już tak zapchany że out of memory lecą przy zwykłych, „rutynowych” alokacjach, wtedy to nawet wyświetlenie czegokolwiek może się nie udać.

0
YetAnohterone napisał(a):

Kiedy poleci OutOfMemoryException to program przecież musi się scrashować, siła wyższa.

Jeżeli taki wyjątek poleci przy próbie utworzenia obiektu (uruchomienie konstruktora), to da się złapać. Raz kiedyś pisałem apkę, która tworzyła duże obiekty i coś takiego zadziałało (pisze z pamięci, więc mogą być błędy, ale mniej więcej o to chodziło):

Obj = null;
try
{
    Obj = new ObjType();
}
catch
{
    System.GC.Collect();
    try
    {
        Obj = new ObjType();
    }
    catch
    {
        Console.Write("Za mało pamięci");
    }
}

Rzecz w tym, że zaśmiecona pamięć może być przyczyną przepełnienia przy tworzeniu obiektów i w większości przypadków wymuszenie odśmiecania pomaga.

3

@andrzejlisek: Nie rozumiem toku rozumowania tutaj. Przynajmniej w JVM jest tak, ze jesli zapelni sie pamiec, GC najpierw zrobi miejsce, a potem zaalokuje obiekt. Jesli pomimo GC nie ma miejsca - wtedy leci OOME. Czyli to co zakodowales robi juz maszyna wirtualna.

Czyli troche jak:

boolean isEqual(x, y) {
  if (x != y) {
    return x == y; // może teraz?
  } else {
    return true;
  }
}
2
Charles_Ray napisał(a):

Czyli to co zakodowales robi juz maszyna wirtualna.

Niby tak, ale słyszałem o przypadku na starej JVM że takie łapanie OutOfMemory, wywoływanie sprzątania i ponowne tworzenie obiektu naprawiło problem. Ale to był 2012 rok

0

@andrzejlisek nie wiem jak w .NET ale w JVM to co napisałeś to kompletna bzdura. Po pierwsze GC zam sobie odpali konsolidacje i odśmiecanie jak nie będzie się dało zaalokować obiektu, więc ręczne wołanie czegokolwiek nie ma zadnego sensu. Po drugie OOM poleci tylko kiedy sytuacja jest juz krytyczna i generalnie łapanie go nie ma sensu, bo nie masz żadnej pewności że maszyna wirtualna jest nadal w spójnym stanie.
@KamilAdam: pics or it didn't happen czy tam citation needed

1
Shalom napisał(a):

@KamilAdam: pics or it didn't happen czy tam citation needed

a co ja mam ci zacytować :D słyszałem to od seniora gdy sam byłem juniorem. Dlatego mówię że to moglo działać na starych JDK. Senior zapewniał że to pomogło. Ja tej aplikacji nawet na oczy nie wiedziałem :P

nawet nie bede sie upierac ze mial racje xD

1
Shalom napisał(a):

@andrzejlisek nie wiem jak w .NET ale w JVM to co napisałeś to kompletna bzdura. Po pierwsze GC zam sobie odpali konsolidacje i odśmiecanie jak nie będzie się dało zaalokować obiektu, więc ręczne wołanie czegokolwiek nie ma zadnego sensu. Po drugie OOM poleci tylko kiedy sytuacja jest już krytyczna i generalnie łapanie go nie ma sensu, bo nie masz żadnej pewności że maszyna wirtualna jest nadal w spójnym stanie.

Mam autentyczny przykład w swojej aplikacji BackupToMail linia 139. Program od czasu do czasu wysypywał się z wyjątkiem OutOfMemory na funkcji GetMessage, a jak obsłużyłem tak, jak widać w linku, to przestał się wysypywać w tym miejscu, czyli coś jest na rzeczy. A CleanUp to:

public static void CleanUp()
{
    GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
}

Obiekty ImapClient_ i Pop3Client_ są typu odpowiednio ImapClient i Pop3Client zdefiniowane w bibliotece MailKit i MimeKit, więc nie mam wpływu na to, co dokładnie robi funkcja GetMessage.

0
andrzejlisek napisał(a):
YetAnohterone napisał(a):

Kiedy poleci OutOfMemoryException to program przecież musi się scrashować, siła wyższa.

Jeżeli taki wyjątek poleci przy próbie utworzenia obiektu (uruchomienie konstruktora), to da się złapać.

Dokumentacja OutOfMemoryException twierdzi inaczej:

The exception that is thrown when there is not enough memory to continue the execution of a program.

Oraz, pomijając pewien szczególny przypadek związany ze StringBuilderami:

OutOfMemoryException exception represents a catastrophic failure. If you choose to handle the exception, you should include a catch block that calls the Environment.FailFast method to terminate your app and add an entry to the system event log, as the following example does.

EDIT: Eric Lippert także twierdzi inaczej (choć co prawda post jest z 2008 roku):

Fatal exceptions are not your fault, you cannot prevent them, and you cannot sensibly clean up from them. They almost always happen because the process is deeply diseased and is about to be put out of its misery. Out of memory, thread aborted, and so on. There is absolutely no point in catching these because nothing your puny user code can do will fix the problem. Just let your finally blocks run and hope for the best. (Or, if you’re really worried, fail fast and do not let the finally blocks run; at this point, they might just make things worse. But that’s a topic for another day.)

Edit2: Nie jestem pewien, czy na pewno jest rozsądne zaprzeczać devom C# w tego rodzaju sprawie bez naprawdę silnych argumentów?

0

Nie będę upierać się przy swoim, bo to nie ma sensu, możliwe, że przypadek na Githubie powinien być inaczej rozwiązany (w jaki sposób?), ja swego czasu zrobiłem, jak zrobiłem doświadczalnie i uzyskałm zamierzony efekt. Teraz zrobiłem inny test i stwierdziłem, że jak nie ma wywołania GC.Collect, to program zrobi 8 obiektów zbudowanych za pomocą TestObj(1), żadnego nie usuwa i system od razu uwali cały program (w konsoli Ubuntu pojawia się słowo "Unicestwiony" i tyle, nie pokazuje się słowo "END" z programu), a jak jest GC.Collect, to program zrobi 20 obiektów, w międzyczasie większość zniszczy. Natomiast, jak się użyje TestObj(2), to niszczenie poprzednich obiektów (w miarę potrzeb) działa bez wywołania GC.Collect, czyli wygląda na to, że to zależy od tego, jakie obiekty w sobie ma dany obiekt. Akurat efektu OOM nie udało mi się uzyskać w tym przypadku. Liczba iteracji, wielkość tablicy i bitmapy dobrałem doświadczalnie. Jak widać, tutaj udało się znaleźć wyjątek niemożliwy do przechwycenia, ale wymuszenie odśmiecania zapobiega przepełnieniu pamięci.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace TestThr
{
    class MainClass
    {
        class TestObj
        {
            List<Bitmap> BmpX;
            List<int[]> ArrayX;
            public TestObj(int T)
            {
                Console.Write("+");
                BmpX = new List<Bitmap>();
                ArrayX = new List<int[]>();
                for (int I = 0; I < 10; I++)
                {
                    if (T == 1)
                    {
                        BmpX.Add(new Bitmap(10000, 10000));
                    }
                    if (T == 2)
                    {
                        for (int II = 0; II < 1000; II++)
                        {
                            ArrayX.Add(new int[1000000000]);
                        }
                    }
                }
            }
            ~TestObj()
            {
                Console.Write("-");
            }
        }

        public static void Main(string[] args)
        {
            Console.Clear();
            TestObj Obj = null;
            Console.WriteLine("BEGIN");
            for (int I = 0; I < 20; I++)
            {
                GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
                try
                {
                    Obj = new TestObj(1);
                }
                catch (Exception E)
                {
                    Console.WriteLine(E.Message);
                }
            }
            Console.WriteLine();
            Console.WriteLine("END");
        }
    }
}
0

@andrzejlisek: odnosnie Twojego rozwiazania - 2x na dobe zesputy zegarek pokazuje dobra godzine ;)

0
  1. @Afish możesz potwierdzić ze GC w .NET jest jakiś upośledzony? (patrz 2 posty wyżej)
  2. @andrzejlisek problem w tym, że wcale nie masz pewności ze po OOM nadal wszystko działa poprawnie. Bo OOM jest globalny dla całej VM a nie tylko dla twojego wątku. Więc jak ty dostałeś OOM i go sobie złapałeś, to równie dobrze jakis inny krytyczny wątek mógł się właśnie poskładać. W efekcie tobie się wydaje że wszystko jest ok, bo sobie złapałeś, ale w praktyce połowa aplikacji właśnie się uwaliła. To jest ksiażkowy przepis na jakieś data corruption. W jakimś hello world tego nie zasymulujesz, ale w prawdziwej aplikacji to może być katastrofa.

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