Dziś jest piątek, 25 lipca 2008 roku (z kalendarza...)

Przyszłościowe skrypty

Icon

04.10.2007, 16:14

PHP

Komentarze (11)

Powrót

Opracowanie rozwiązań mających służyć przez długie lata bez konieczności wprowadzania większych zmian lub wywalania dotychczasowych osiągnięć na śmietnik jest czasochłonne. Niezbędna jest umiejętność planowania oraz przewidywania. Obecnie tworzę taki właśnie skrypt, który ma z założenia wytrzymać w użyciu dłużej, niż rok. Nie poprzestaję jedynie na elastycznej strukturze, lecz staram się także wdrożyć rozwiązania przyszłościowe bądź dostępne wyłącznie tzw. "garstce wybrańców". W tym wpisie pragnę omówić kilka z nich.

Pierwszym z tematów jest reprezentacja czasu w bazach danych oraz w skrypcie. MySQL udostępnia formaty DATE oraz TIME, a dzięki odpowiednim funkcjom można łatwo wyciągnąć z nich np. informację o miesiącu, pozostawiając wszystkie inne. Dla potrzeb przetwarzania przez skrypt oraz szybkiej arytmetyki stosuje się jednak zazwyczaj uniksowe znaczniki czasu, czyli zwyczajne liczby określające ilość sekund, jakie upłynęły od 1 stycznia 1970 roku. W bazie danych można je składować w zwyczajnym polu INT ze znakiem, gdzie liczby ujemne to liczba sekund, jakie pozostały do wymienionej wyżej daty. Każdy informatyk szybko sobie przeliczy, że za pomocą 32-bitowego pola można reprezentować ok. 4 miliardów różnych liczb. Nałożywszy ten zakres na oś czasu wychodzi, że najwcześniejsza dająca się zapisać tak data to 13 grudnia 1901 roku, zaś najpóźniejsza - 19 stycznia 2038 roku. Jeśli licznik zegaru dojdzie do tej daty, zgodnie z zasadą działania komputerowej arytmetyki, zostanie on zawinięty do przeszłości, dokładnie tak samo, jak to miało miejsce ze słynnym niewypałem pt. problem roku 2000.

Aktualnie proponuje się dwa rozwiązania problemu roku 2038: pierwsze z nich polega na zmianie formatu znacznika czasu na liczbę 32-bitową, ale bez znaku, dzięki czemu zakres wydłuży się do okolic roku 2100. Drugie z rozwiązań jest znacznie drastyczniejsze; polega na przejściu na czas 64-bitowy. Obsłuży on ponad 290 miliardów lat, czyli nowy zakres będzie znacznie większy, niż dotychczasowy wiek wszechświata. Jeśli chodzi o skrypty, ich aktualizacja nie jest zbyt kłopotliwa. Wystarczy wydłużyć wszystkie pola przechowujące datę z INT do BIGINT; cała reszta zależy już od admina, a konkretniej użytej wersji interpretera oraz systemu operacyjnego. W FreeBSD typ time_t w języku C odpowiedzialny za timestamp został już wydłużony do 64 bitów, lecz w Linuksie dalej jest ono dwukrotnie krótsze. Ponadto niezbędna może okazać się aktualizacja interpretera - dla PHP będzie to przynajmniej wersja 6, ponieważ dopiero tam pojawia się obsługa 64-bitowych liczb. Przebudowa oprogramowania nie jest taką pilną sprawą; jeśli faktycznie mamy zamiar pisać teraz skrypt/bazę danych, która ma chodzić przez 30 lat, możemy zawczasu podjąć środki zaradcze; w przeciwnym wypadku należy być jedynie świadomym, że 32-bitowy INT nie jest wieczny.

Kolejny stosunkowo nowy wynalazek to IPv6, który wydatnie rozszerza pulę dostępnych adresów IP tak, że na jednego mieszkańca Ziemi przypada ich ok. 5*10^28. W skryptach PHP adresy IP wykorzystuje się do kontroli sesji lub notowania np. przy komentarzach, lecz większość z nich używa do tego celu wyłącznie 32-bitowych pól, przez co będą one nieprawidłowo pracować w sieciach IPv6. Perspektywa przesiadki na takowe jest znacznie bliższa w czasie, niż problemy dat. Szacuje się, że pula adresów IPv4 wyczerpie się ok. 2011 roku, zaś rządy niektórych krajów wydały już zarządzenia w celu przebudowy swej infrastruktury. Administracja USA ma czas do końca 2008 roku na przejście na nowy protokół, zaś Chińczycy wdrażają projekt China Next Generation Internet. Niektórym krajom przydzielono już także rekordy DNS typu AAAA (IPv6) dla ich narodowych domen najwyższego poziomu. Tyle tytułem wprowadzenia, przejdźmy do implementacji w skryptach i bazach danych. Jest to nieco skomplikowane, ponieważ ciężko znaleźć odpowiedni typ danych do składowania. MySQL-owe liczby kończą się na 64 bitach i jedynym rozsądnym rozwiązaniem jest typ CHAR(16) (128 bitów = 16 bajtów). W analogiczny sposób adres zapiszemy w PHP, jednak tu musimy też zwrócić uwagę na problem z jego translacją do czytelnej postaci. PHP posiada funkcje inet_ntop() oraz inet_pton(), które obsługują zarówno IPv4, jak i IPv6, lecz nie są one dostępne na platformie Windows, gdy tymczasem duża grupa programistów pisze skrypty właśnie w tym systemie. W komentarzach do dokumentacji PHP można znaleźć skryptowe implementacje obu funkcji, które można użyć w takiej sytuacji.

Nawet jeśli aktualnie korzystamy z IPv4, przejście na IPv6 może oszczędzić nam kłopotów w przyszłości. Nowy protokół przewiduje specjalną klasę: ::ffff:0:0/96, którą można wykorzystać do zapisywania adresów IPv4 w strukturze IPv6 (zamrożone pierwsze 96 bitów, z czego 80 ustawione jest na 0, a 16 na 1, natomiast dalej idą 32 bity adresu IPv4). Odpowiednio zaprojektowana już teraz aplikacja będzie mogła dzięki temu niemal transparentnie przejść na IPv6.

Ostatnie rozwiązanie, jakie zamierzam zaimplementować, to obsługa replikacji serwerów MySQL. Technologia ta pojawiła się w MySQL-u 3.23 i umożliwia rozładowanie obciążenia. Mamy tutaj wyróżnionych n maszyn, z którymi możemy nawiązać połączenie do odczytu danych oraz jeden wyróżniony serwer prowadzący dziennik zmian. Modyfikacja danych możliwa jest wyłącznie za jego pośrednictwem. Wysyłając do niego np. zapytanie UPDATE, jego wyniki zostaną rozpropagowane między wszystkie pozostałe serwery, dzięki czemu dane pozostaną zsynchronizowane. W takim środowisku skrypt musi utrzymywać dwa połączenia z bazą danych: jedno do odczytu i drugie do zapisu, przy czym pierwsze jest nawiązywane np. z losowym dostępnym serwerem. Bardziej wymagające serwisy wymagają obecności klastra MySQL, gdzie do każdej z maszyn posiadamy możliwość odczytu i zapisu. Dość prosto jest tak napisać skrypt, aby mógł pracować bezproblemowo z każdą konfiguracją. Moja implementacja przewiduje udostępnienie skryptowi dwóch zmiennych obiektowych: $sqr oraz $sqw. Są one wypełniane następująco:

  1. Dla połączeń z pojedynczym serwerem: odczytujemy z konfiguracji adres serwera, nawiązujemy z nim jedno połączenie i obiekt PDO wprowadzamy do obu tych zmiennych.
  2. Dla serwerów z replikacją: skrypt tworzy dwa obiekty PDO wprowadzane do każdej ze zmiennych. Jeden zostaje nakierowany na serwer główny obsługujący zapis, natomiast drugi na losowo wybraną z pozostałych maszyn.
  3. Dla klastrów: skrypt tworzy jeden obiekt PDO, wprowadza go do obu zmiennych, przy czym adres serwera wybierany jest losowo z dostępnej puli.

Dodatkowo stosuję nakładkę Open Power Driver z opcją "leniwej ewaluacji", czyli nawiązywania połączenia dopiero wtedy, gdy faktycznie jest ono niezbędne. Jeśli teraz wszystkie zapytania SELECT będziemy obsługiwać poprzez zmienną $sqr, a INSERT/UPDATE/DELETE poprzez $sqw, ten sam kod będzie działać bez przeszkód w każdej sytuacji. Tyle teoria, a jak wyjdzie w praktyce, to zobaczymy. Aktualnie kończę instalowanie systemu na drugim komputerze, aby móc skonfigurować poprawnie MySQL-a do pracy.

Powrót

Komentarze

Napisał Loonatic w czwartek, 4 października 2007 o 17:07

"dla PHP będzie to przynajmniej wersja 6, ponieważ dopiero tam pojawia się obsługa 32-bitowych liczb."

Chyba chodziło o liczby 64 bitowe?

Napisał mitrandir77 w czwartek, 4 października 2007 o 19:52

Ciekawy artykuł. Dał mi dużo do myślenia bo nigdy jeszcze nie robiłem aplikacji IPv6 wychodząc z założenia, że jeszcze czas. A tutaj Zyx już na wszystko znalazł sposób :)

Napisał Zyx w czwartek, 4 października 2007 o 21:09

Loonatic -> racja, pomyłka w trakcie pisania, chodziło oczywiście o 64-bitowe integery.

IPv6 to już wcale nie tak odległa przyszłość. Wiele skryptów (szczególnie tych większych) będzie pracować przez co najmniej kilka lat i może się zdarzyć, że zdążą już się załapać na oficjalne uruchomienie sieci IPv6 w Polsce oraz przyłączenie do niej pierwszych węzłów. Doszedłem do wniosku, że lepiej być zawczasu przygotowanym, niż później mieć po uszy roboty z poprawianiem wszystkiego :). Jak to działa w praktyce, okaże się, gdy wreszcie jakąś sieć lokalną na IPv6 zrobię i w niej puszczę taki skrypt.

Napisał sf devblog w czwartek, 4 października 2007 o 22:09

Dla mnie aktualnie wymienione problemy są zdecydowanie nie na czasie. Jest dużo innych rzeczy, które są pilniejsze do zrobienia niż data po 2038 czy ipv6 ;) Jeszcze z kilka razy się przebuduje swoje skrypty zanim nadejdzie realne zapotrzebowanie na wymienione aspekty.

Napisał Bob w piątek, 5 października 2007 o 17:05

Raczej wątpię aby teraz ktoś martwił się napisaniem skryptu który ma służyć przez 30 lat. W końcu i tak świat skończy się w 2012 ;)
A jeżeli chodzi o trzymanie daty to tylko Mysql jest taki ograniczony. W postgresie data ma zakres 4713 BC - 5874897 AD, w mssql 1.1.1753 - 31.12.9999, DB2 0001 - 9999, Oracle -4712 - 9999. Duże firmy korzystające z tych rozwiązań nie mają się czym przejmować. To samo dotyczy IPv6. Inne bazy oprócz Mysql mają odpowiednie typy danych do jego przechowywania. Pisząc porządną aplikację i tak większość z operacji przerzuci się na silnik bazodaniowy.

Napisał Termit w piątek, 5 października 2007 o 19:44

Cóż, kolejny ciekawy artykuł. Dobrze zawczasu wiedzieć, co czeka w przyszłości. Chociaż czy jako czterdziestolatek jeszcze będę się zajmował pisaniem stron... tu dopiero trudno wyrokować ;-).

Napisał Bob w sobotę, 6 października 2007 o 06:49

Poprawka bo wczoraj nie doczytałem a jak doczytałem to nie bardzo rozumiem sensu Twojej wypowiedzi Zyx. Sugerujesz żeby niby datę w bazie zapisywać jako integer zamiast odpowiednich pól datowych? Zmiana na bigint (9223372036854775807) to żaden problem i raczej ktoś projektujący bazę powinien to przewidzieć, także PHP ma dopiero 10 lat więc co tu mówić o okresie +30. Zasugerowałem się jeszcze dodatkowo że data w mysql jest do roku 2038 ale jest do 9999. Już teraz normą są 64 bitowe procesory (chociaż firmy mogą jeszcze nie wdrażać oprogramowania x64) a za kilka lat pewnie będzie to 512 lub w ogóle jakieś organiczne inaczej działające. Wychodzenie tak mocno w przód to w zasadzie tylko gdybanie.

Napisał malcom w poniedziałek, 8 października 2007 o 15:36

"Kolejny stosunkowo nowy wynalazek to IPv6..."
IPv6 jest stare ;)
Wedle zalozen juz w 2000 roku mial on wyprzec v4, a jak jest to wszyscy wiemy :P

Napisał Zyx w poniedziałek, 8 października 2007 o 18:00

Stare to jest prawie 30-letnie IPv4 :).

Napisał Komentator newsa w poniedziałek, 29 października 2007 o 23:27

Czy ktoś z Was znalazł kod do aktualizacji pól "left" i "right" w strukturach "nested sets" na podstawie ID i parent_id?

Napisał Zyx w wtorek, 30 października 2007 o 11:47

Oczywiście - to nie jest przesadnie skomplikowane. Musisz jedynie zrobić sobie odpowiednią strukturę, która potrafi wykonać następujące operacje:
1. Zwrócić element na podstawie ID.
2. Zwrócić wszystkie dzieci podanego elementu.

Zasysasz jednym zapytaniem wszystkie rekordy i korzystając z pierwszej własności budujesz w pamięci skryptu normalne drzewko, podpinając dzieci pod rodziców. Jedna pętla załatwia sprawę.

Drugi etap to przejście sobie tego drzewka klasyczną metodą preorder zdefiniowaną następująco:
1. Jeśli element jest liściem, to zwraca 2.
2. Jeśli element ma dzieci, to zwraca sumę wyników zwróconych dla wywołania tej funkcji na każdym z dzieci.

Z takim zwróconym wynikiem możemy teraz zrobić prostą rzecz: mamy parametr left i jak dodamy to do niego powiększone o 1, to uzyskamy "right" dla danego elementu. Przechodząc do kolejnego dziecka na tym samym poziomie, przyjmujemy mu za "left" wartość ostatniego "right" zwiększoną o 1 i to wszystko. Na końcu w drzewku mamy przeliczone wszystkie wartości.

Pisane z palca, więc mogą być małe błędy, ale mniej więcej oddaje ideę:

function preorder($itemId, $left)
{
	global $tree;
	if(!$tree -> hasChildren($itemId))
	{
		$tree -> setLeft($itemId, $left);
		$tree -> setRight($itemId, $left + 1);
		return 2;
	}
	else
	{
		$tree -> setLeft($itemId, $left);
		$i = $left;
		foreach($tree -> getChildren($itemId) as $child)
		{
			$i += preorder($child -> getId(), $i + 1);	
		}
		$tree -> setRight($itemId, $i + 1);
		return $i - $left;
	}
} // end preorder();
 
// Rozpoczecie przeliczania
preorder(1, 1);

Strona 1 z 1 :: 1

Skomentuj

NickInformacja
E-mailTylko do użytku wewnętrznego.
WWWNie zapomnij o http://
LayoutNapisz tu, czy widzisz dzienny czy nocny layout.
WpisFormatowanie wiki
Internauto, pamiętaj! Wolność to nie samowola - dbaj o kulturę wypowiedzi oraz dyskusji w sieci.

Na Zyxist.com panuje swoboda wyrażania opinii oraz krytyki pod dowolnym adresem. Jedyny warunek: musi być ona kulturalna i rzeczowa. Na chamstwo, prostactwo lub jawne obrażanie kogokolwiek nie ma tu miejsca i takie komentarze są bardzo szybko usuwane. Jeśli zamierzasz polemizować z treścią wpisu, wpierw uważnie ją przeczytaj.

© Tomasz "Zyx" Jędrzejewski 2005 - 2008 | Wykonanych zapytań: 2 | Serwer wirtualny zapewnia