PHP na ratunek — o generowaniu dużych plików bez gotowania przeglądarki
Decyzja o tym, gdzie generować plik CSV, stała się moją okazją do wejścia głębiej w PHP i sprawdzenia się poza strefą komfortu. Turniej tenisowy też tym razem testował moją strefę komfortu...
Programowanie
W ostatnim czasie działam nad nową funkcjonalnością związaną z generowaniem pliku CSV z danymi z systemu. Całej sprawie towarzyszyło sporo decyzji do podjęcia i przeanalizowania. Przy takich zagadnieniach trzeba się przede wszystkim zastanowić czy chcemy ten plik generować po stronie klienta - czyli przez JavaScript w przeglądarce usera czy lepiej wygenerować taki plik po stronie serwera. Oba rozwiązania mają swoje wady i zalety, a do tego dochodzą moje wewnętrzne preferencje. Programuję głównie w JavaScript. Dołączając do zespołu dowiedziałam się, że backend naszej aplikacji jest zbudowany w PHP, więc nie czekałam długo z nauką PHP. Napisałam do tej pory jedną, dużą aplikację w PHP i zrobiłam kilka kursów, więc nie jest ze mną tak źle.
Moje własne odczucia
W związku z tym, że JavaScript jest dla mnie bardziej naturalny - bo znam go dłużej, pracuję z nim na co dzień, każdy feature byłby dla mnie łatwiejszy/szybszy w JavaScript. Ale mam całkowitą świadomość, że jeżeli będę kodować wszystko z wygodą dla siebie to nie wyjdę nigdy nigdzie dalej niż właśnie ten JavaScript. Chce się rozwijać, dlatego zaczęłam się uczyć PHP, dlatego chcę być elastyczna i przyjąć na klatę każdy task, nawet taki, co “nie umiem”. A zatem opcja wygenerowania czegoś na backendzie mnie od razu podpaliła do działania - chyba właśnie takiego taska potrzebowałam. Nie zamierzałam jednak upierać się przy tym, niech los zadecyduje. Zrobiliśmy listę zalet i wad jeżeli chodzi o rozwiązanie tego na frontendzie i na backendzie i na tej podstawie zdecydowaliśmy (uwaga, spojler), że tym razem podejmę się taska backendowego (i trochę też frontendowego).
Dlaczego zrobić to na frontendzie?
Naturalnie pierwsze co przychodzi do głowy to to, że odciążamy serwer, a dociążamy tym przeglądarkę klienta. To może działać szybciej, bo nie musimy tego dużego pliku przesyłać między serwerem a klientem. ALE. To może działać wolniej, a nawet wybuchnąć przeglądarkę klienta jeżeli za bardzo ją obciążymy. A to jest w naszym przypadku całkiem realne… Założenie jest takie, żeby wygenerować plik z danymi za okres czasu, który klient sobie wybierze. No i jak on sobie tam wyklika 10 ostatnich lat, to podejrzewam, że przeglądarka tego nie przetrawi. Można sobie próbować radzić tutaj Web Workersami, stream-saverem czy innymi narzędziami.
Niestety opcji zawieszenia przeglądarki jest dużo, plik może zmrozić interfejs całkowicie, proces może wykorzystać cały RAM, a system może ubić tą przeglądarkę w akcie desperacji. Oczywiście, nie zmuszamy klientów do odpalania naszej aplikacji na super-komputerach, więc musimy liczyć się z różnymi sytuacjami. Żeby obsłużyć te możliwości zawieszeń trzeba by dołożyć pewnie bibliotek, które by zarządzały tym stanem. A zatem, przeglądarka sprawdziłaby się super do małego pliku, czyli musielibyśmy ograniczyć jakoś zakres czasu, który user może wyklikać. Tego nie chcemy.
Mogłaby tutaj mieć też sens jakaś taka sytuacja hybrydowa - czyli dla mniejszych zakresów frontendowo, dla większych backendowo. Ale zrezygnowaliśmy z opcji rozdrabniania się. Serwery wytrzymajo.
Dlaczego zrobić to na backendzie?
Analogicznie, proces obciąży serwer - OK, na to możemy sobie pozwolić. Dzięki temu nie przejmujemy się na czym odpala naszą stronę klient. Po wygenerowaniu pliku na serwerze mamy w zasadzie dwie opcje, możemy zapisać go w chmurze “na później” lub od razu odesłać do przeglądarki. Dodatkowo mamy tutaj w 100% możliwość panowania nad tym plikiem, który się wygeneruje. Gdyby robić to na froncie to u klienta w zależności od przeglądarki i ostatniej aktualizacji może być różny efekt. Co więcej, chyba największa zaleta w tym przypadku to fakt, że możemy sobie strumieniować output przez php://output albo chunked transfer. Ale też na raty pobierać dane z bazy. Więc jeżeli klient wyklika sobie 10 ostatnich lat danych, to możemy panować nad czasem pobierania danych z bazy, a jednocześnie nie obciążymy w ogóle pamięci po stronie klienta.
W tym rozwiązaniu z wad musimy rozważyć zatkanie serwera jeżeli nagle wszyscy rzucą się na nową funkcjonalność i ewentualnie przeanalizować ryzyko przechowywania tych plików. Żeby później do roku 2999 nie zalegały nam te pliki. W sumie na te potrzeby można ustawić przechowywanie plików na X dni.
Po stronie PHP możemy też ustawić “ignore_user_abort(true)”, dzięki temu nawet jeżeli user zamknie przeglądarkę to plik wygeneruje się do końca i trafi gdzieś, np. do chmury czy na maila - to może być przydatne dla dużych plików.
Czyli…
Czyli skoro pozwalamy klientowi generować duże pliki, to powinniśmy to rozwiązać na backendzie. Oczywiście po mojej stronie jest też przygotowanie przycisku, modala do wyboru zakresu dat i wywołanie skryptu generującego - a zatem jednak nadal koduję frontend. To proste, zaś przygotowanie tego na backendzie to wyzwanie, które mnie bardzo ucieszyło. Czy będę w tym czasie upierdliwa dla seniorów? Być może… Ale to jeden task upierdliwości, a później wieczna chwała!
Offtop
W mikołajki braliśmy udział w turnieju tenisowym dla początkujących. Turniej był organizowany z innym trenerem i w innym klubie niż gramy na codzień i finalnie… wróciliśmy do domu z całą paką przemyśleń związanych z metodami nauczania. Każdy trener ma własne podejście, własną “szkołę” nauki. Wcześniej nie mieliśmy świadomości, że inni są nauczani w inny sposób (chociaż to chyba oczywiste?) ale to co nas zaskoczyło, to inne spojrzenie na grę w tenisa. Czy lepsze? Tego nie wiemy. Na pewno do niektórych osób trafia system X, do innych system Y. Pewnie dlatego kluby dają często darmowe lekcje, żeby pokazać metodykę i żeby klient mógł sprawdzić czy to z nim rezonuje. Myślę, że tak to działa nie tylko w tenisie. Każdy z nas musi znaleźć swój własny sposób zdobywania wiedzy czy rozwijania się. Jedna osoba będzie wolała grywalizację i zabawę, inna osoba będzie wolała wykonanie tysiąca powtórzeń i w ten sposób szlifowanie techniki. Wszystkie drogi są dobre do znalezienia przyjemności w danej czynności, szukajcie!
A przy okazji - Wesołych Świąt!

