armia amerykańska rocznie zużywa około 200,000 sztuk opon do humvee – wojskowego pierwowzoru hummerów.
humvee są świetnie opancerzone, ale na kołach nadal mają prawie zwykłe opony. prawie zwykłe – przeżyją przestrzelenie i jazdę bez powietrza. ale nie za długą i nie za szybko. a i przestrzelina powinna być raczej mała.
a to jest już poważany problem – w czasie misji typu irak, afganistan czy cokolwiek tego typu różnica między mobilnością a jej brakiem to także różnica miedzy życiem a śmiercią.
rozwiązanie wymyśliła pewna mała firma z wisconsin.
wymyślili mianowicie koło bez zamkniętej, pompowanej przestrzeni:
tego typu opony już były. to co wyróżnią tę to fakt, że podobno uporano się z wszystkimi problemami z tego typu sprzętem. co z tego wyjdzie? zobaczymy. ja jestem pełen optymizmu – jeśli w tym roku miałoby to wejść do wojska, to za jakieś 2-3 pojawiłoby się w samochodach cywilnych. a to może oznaczać, że szybciej pojawi się rozwiązanie problemu dziur w drogach niż nasze ekipy rządzące te dziury usuną.
Author: depesz
międzynarodowa współpraca
w 2005 roku francja i niemcy podpisały międzynarodowe porozumienie w ramach którego miała być stworzona nowa wyszukiwarka internetowa.
do tego "sojuszu" przystąpiły też bertelsman oraz france i deutche telecomy.
prawda, że pięknie?
osobiście myślałem, że padnę ze śmiechu jak gdzieś wyczytałem, że projekt będzie miał budżet 300 milionów dolarów. na tyle się państwa członkowskie dogadały. w skrócie – od początku widać, że jest to żart a nie prawdziwe zagrożenie dla google'a.
no i stało się. jak się okazuje niemcy stwierdzili, że dogadanie się z francuzami jest nierealne i wycofują się w projektu. stworzą własną wyszukiwarkę. a francuzi będą kontynuować budowanie swojej. prawda, że piękne?
na ile oceniacie szanse, że którykolwiek z tych teamów zrobi coś co będzie w stanie konkurować choćby z ask.com, nie mówiąc już o google'u? 0.1% 0.01%?
meta-poprawka
najczęstsza przyczyną problemów z bezpieczeństwem są błędy w zarządzaniu pamięcią – znane grupowo pod nazwą buffer overflowów. poza nimi jest kilka innych typów błędów które co prawda nie tworzą zagrożeń bezpieczeństwa systemu, ale np. po wodują krytyczne wyjścia albo zawieszenia z aplikacji.
błędy te mają różną postać, ale w większości sprowadzają się do mniej więcej tego samego.
zauważyli to badacze z uniwersytetu w amherst, którzy następnie wraz z programistami z intela i microsoftu stworzyli interesujące narzędzie: diehard.
diehard jest biblioteką której załadowanie przed uruchomieniem programu zabezpiecza przed większością standardowych problemów w obsłudze pamięci.
diehard nie zapewnia bezpieczeństwa – on po prostu powoduje, że pewne błędy są lepiej obsługiwane – w sposób nie zagrażający ani bezpieczeństwu ani stabilności.
biblioteka ta jest napisana tak by działała zarówno na windows jak i linuksie (są też źródła, więc da się odpalić na innych systemach). z tym, że na windowsach potrafi jak na razie zabezpieczać tylko jedną aplikację – firefoxa. na linuksie może zabezpieczać dowolny soft.
rozszerzanie funkcjonalności serwisów webowych
pewnie spora część was – moich czytelników – wie co to apache i nie raz stawiała serwisy pod nim. a ilu/ile z was zna i korzysta z mod_rewrite?
mod_rewrite jest najwspanialszym modułem jaki kiedykolwiek powstał dla apache'a. i najgorszym koszmarem.
w dokumentacji do tego modułu można znaleźć dwa motta:
"The great thing about mod_rewrite is it gives you all the configurability and flexibility of Sendmail. The downside to mod_rewrite is that it gives you all the configurability and flexibility of Sendmail."
to jest zabawne, ale tylko dla ludzi znających sendmaila.
drugie motto jest zdecydowanie prostsze w odbiorze:
"Despite the tons of examples and docs, mod_rewrite is voodoo. Damned cool voodoo, but still voodoo."
czy jednak tak faktycznie jest? czy jest to niemożliwe do opanowania?
dziś chciałbym pokazać dwa proste przykłady użycia mod_rewrite.
jest sobie taki program – request tracker. służy on rejestracji i obsługi wszelkiego rodzaju "zgłoszeń". zgłoszenia są numerowane, a dodatkowo każde zgłoszenie jest w określonej "kolejce" – czymś jakby katalogu/kategorii.
system działa nawet fajnie (z dokładnością do wydajności, ale o tym już pisałem). natomiast brakuje mi kilku prostych rzeczy. dokładniej – możliwości podania prostego w urlu, że chcę zobaczyć zgłoszenie numer XXX, czy dane z kolejki YYY.
o ile zgłoszenie numer XXX ma jeszcze prostego urla:
http://rt.domena/Ticket/Display.html?id=XXX
o tyle url do obejrzenia kolejki jest "wygięty", gdyż działa z użyciem mechanizmu search'a, więc wygląda tragicznie:
http://rt.domena/Search/Results.html?Query=Queue%20=%20'YYY'%20AND%20(Status%20=%20'open'%20OR%20Status%20=%20'new')&Rows=50
wkurzające. i niemożliwe do wpisania "z palca".
tak więc stwierdziłem, że dobuduję sobie obsługę odpowiednich urli. ideałem byłoby dorobienie tego bez modyfikowania kodu – aby nie musieć back-portować poprawek po każdy upgrade'dzie.
tak więc, do definicji wirtuala rt.domena dopisałem te 3 linijki:
RewriteEngine on
RewriteRule ^/([0-9]+)$ /Ticket/Display.html?id=$1 [R,L]
RewriteRule ^/([A-Za-z0-9-]+)$ /Search/Results.html?Query=Queue\%20=\%20'$1'\%20AND\%20(Status\%20=\%20'open'\%20OR\%20Status\%20=\%20'new')&Rows=50 [NE,R,L]
co one robią?
pierwsza linijka – po prostu włącza silnik rewrite'ów.
druga – każdy url zaczynający pasujący do regexpa ^/([0-9]+)$ (czyli każdy typu: http://rt.domena/123, gdzie zamiast 123 może być dowolna liczba) zamienia na http://rt.domena/Ticket/Display.html?id=123
oczywiście numer ticketu (123) jest przepisywany.
flagi na końcu – [R,L] oznaczają odpowiednio, że (R) wynikiem rewrite'a ma być redirect (http/302), oraz (L), że przetwarzanie reguł ma się zakończyć na tej – o ile zostanie dopasowana.
druga linijka dopasowuje się do wszystkich urli takich jak: http://rt.domena/COSTAM
gdzie COSTAM jest ciągiem znaków składającym się z dużych i małych liter oraz cyfr.
tym razem do flag dodałem dodatkowo "NE". ‘NE' oznacza, że url ma zostać przesłany do przeglądarki bez dalszej obróbki url'i (escape'owania przy pomocy url-encodingu).
i to wszystko.
dzięki temu teraz mogę używać urli:
http://rt.domena/NUMER_TICKETU
http://rt.domena/NAZWA_KOLEJKI
i działają one zgodnie z przewidywaniami 🙂
tak więc – polecam przyjrzenie się mod_rewrite'owi. pozwala on w trywialny sposób dodać nowe funkcjonalności do istniejących serwisów. i popsuć wszystko w sposób który będzie się wydawał całkowicie magiczny. no cóż – jak voodoo, to voodoo.
mimo tego – polecam poznanie tego modułu. osobiście ratował mi odwłok przynajmniej kilkanaście razy.
drzewa w sql’u – metoda wielu tabel
tak naprawdę to nazwa "metoda wielu tabel" nie oddaje w pełni tego o co chodzi, natomiast jest pewnym przybliżeniem.
zgodnie z tą metodą należy stworzyć dla każdego poziomu zagnieżdżenia tabelę.
w oparciu o nasze dane testowe należy stworzyć system tabel:
CREATE TABLE categories_1 (
id BIGSERIAL PRIMARY KEY,
codename TEXT NOT NULL DEFAULT ''
);
CREATE UNIQUE INDEX ui_categories_1_cn ON categories_1 (codename);
CREATE TABLE categories_2 (
id BIGSERIAL PRIMARY KEY,
parent_id INT8 NOT NULL DEFAULT 0,
codename TEXT NOT NULL DEFAULT ''
);
ALTER TABLE categories_2 ADD FOREIGN KEY (parent_id) REFERENCES categories_1 (id);
CREATE UNIQUE INDEX ui_categories_2_picn ON categories_2 (parent_id, codename);
CREATE TABLE categories_3 (
id BIGSERIAL PRIMARY KEY,
parent_id INT8 NOT NULL DEFAULT 0,
codename TEXT NOT NULL DEFAULT ''
);
ALTER TABLE categories_3 ADD FOREIGN KEY (parent_id) REFERENCES categories_2 (id);
CREATE UNIQUE INDEX ui_categories_3_picn ON categories_3 (parent_id, codename);
CREATE TABLE categories_4 (
id BIGSERIAL PRIMARY KEY,
parent_id INT8 NOT NULL DEFAULT 0,
codename TEXT NOT NULL DEFAULT ''
);
ALTER TABLE categories_4 ADD FOREIGN KEY (parent_id) REFERENCES categories_3 (id);
CREATE UNIQUE INDEX ui_categories_4_picn ON categories_4 (parent_id, codename);
i w nim dane:
# select * from categories_1;
id | codename
----+----------
1 | sql
(1 row)
# select * from categories_2;
id | parent_id | codename
----+-----------+------------
1 | 1 | oracle
2 | 1 | postgresql
(2 rows)
# select * from categories_3;
id | parent_id | codename
----+-----------+----------
1 | 2 | linux
2 | 1 | linux
3 | 1 | solaris
4 | 1 | windows
(4 rows)
# select * from categories_4;
id | parent_id | codename
----+-----------+----------
1 | 2 | glibc1
2 | 2 | glibc2
(2 rows)
jak widać pojawia się pewien problem – nieobecny w innych systemach tabel – aby prawidłowo zidentyfikować element drzewa nie wystarczy nam jego numer, ale musimy też znać poziom zagłębienia.
oczywiście możliwe jest wymuszenie nadawania numerów kolejnych tak aby nie powtarzały się w różnych tabelach, ale wtedy znalezienie który element jest w której tabeli będzie nietrywialne.
teraz pora na zadania testowe dla tego układu tabel:
1. pobranie listy elementów głównych (top-levelowych)
> SELECT * FROM categories_1;
proste i miłe.
2. pobranie elementu bezpośrednio "nad" podanym elementem
dane wejściowe:
- ID : id elementu
- X : poziom zagłębienia.
jeśli X == 1 to:
brak danych
w innym przypadku:
> SELECT p.* FROM categories_[X] c join categories_[X-1] p ON c.parent_id = p.id WHERE c.id = [ID]
pojawia się problem. zapytania są zmienne. nie jest to bardzo duży problem, ale np. utrudnia wykorzystywanie rzeczy typu "prepared statements".
3. pobranie listy elementów bezpośrednio "pod" podanym elementem
dane wejściowe:
- ID : id elementu
- X : poziom zagłębienia.
> SELECT c.* FROM categories_[X] p join categories_[X+1] c ON c.parent_id = p.id WHERE p.id = [ID]
znowu pojawia sie problem z zapytaniami o zmiennej treści (nie parametrach – treści – nazwach tabel!). dodatkowo – musimy gdzieś przechowywać informację o maksymalnym zagnieżdżeniu i sprawdzać ją przed wykonaniem zapytania – aby się nie okazało, że odwołujemy się do nieistniejącej tabeli.
4. pobranie listy wszystkich elementów "nad" danym elementem (wylosowanym)
dane wejściowe:
- ID : id elementu
- X : poziom zagłębienia.
jeśli X == 1 to:
brak danych
jeśli X == 2 to:
> SELECT c1.* FROM categories_2 c2 join categories_1 c1 ON c2.parent_id = c1.id WHERE c2.id = [ID]
jeśli X == 3 to:
> SELECT c1.*, c2.*
FROM categories_3 c3 join categories_2 c2 on c3.parent_id = c2.id join categories_1 c1 ON c2.parent_id = c1.id
WHERE c3.id = [ID]
itd.
jak widać tu problem się multiplikuje. nie tylko zmieniają nam się nazwy tabel, ale wręcz cała konstrukcja zapytania zaczyna przypominać harmonijkę – z każdym ruchem (wgłąb struktury) wyciąga się.
5. pobranie listy wszystkich elementów "pod" danym elementem (wylosowanym)
dane wejściowe:
- ID : id elementu
- X : poziom zagłębienia.
- MaxX : maksymalny poziom zagłębienia w systemie
SELECT c[X+1].* FROM categories_[X+1] c[X+1] WHERE c[X+1].parent_id = [ID]
UNION
SELECT c[X+2].*
FROM categories_[X+1] c[X+1] join categories_[X+2] c[X+2] ON c[X+1].id = c[X+2].parent_id
WHERE c[X+1].parent_id = [ID]
UNION
SELECT c[X+3].*
FROM categories_[X+1] c[X+1]
join categories_[X+2] c[X+2] ON c[X+1].id = c[X+2].parent_id
join categories_[X+3] c[X+3] ON c[X+2].id = c[X+3].parent_id
WHERE c[X+1].parent_id = [ID]
...
łaaaał. robi się coraz gorzej. napisanie tego dla np. 16 poziomów zagnieżdżenia przerasta moje chęci.
6. sprawdzenie czy dany element jest "liściem" (czy ma pod-elementy)
dane wejściowe:
- ID : id elementu
- X : poziom zagłębienia.
SELECT count(*) from categories_[X+1] WHERE parent_id = [ID]
zasadniczo proste, ale trzeba pamiętać o wcześniejszym sprawdzeniu czy przypadkiem X+1 nie jest większe od maksymalnie obsługiwanego limitu – aby uniknąć błędów w bazie.
7. pobranie głównego elementu w tej gałęzi drzewa w której znajduje się dany (wylosowany) element
dane wejściowe:
- ID : id elementu
- X : poziom zagłębienia.
jeśli X == 1 to:
> SELECT c1.* FROM categories_1 c1 WHERE id = [ID]
jeśli X == 2 to:
> SELECT c1.* FROM categories_2 c2 join categories_1 c1 ON c2.parent_id = c1.id WHERE c2.id = [ID]
jeśli X == 3 to:
> SELECT c1.*
FROM categories_3 c3 join categories_2 c2 on c3.parent_id = c2.id join categories_1 c1 ON c2.parent_id = c1.id
WHERE c3.id = [ID]
itd.
eh. chyba widzicie do czego to zmierza.
jak widać – zapisywanie tak drzew ma swoje problemy. i raczej niewiele rzeczy można w tym zrobić ładnie szybko i porządnie.
zasadniczo – nie opisywałbym tej metody gdyby nie fakt iż z nieznanych powodów jest ona bardzo często sugerowana przez ludzi których odpytuję w ramach rozmowy kwalifikacyjnej. no – a skoro się pojawia, to trzeba ją omówić. choćby pobieżnie.
tapeta na dziś
drzewa w sql’u – wstęp
jakiś czas temu zainteresowała mnie metoda przechowywania wszelkiego rodzaju struktur drzewiastych w bazach danych.
dla wyjaśnienia – struktura drzewiasta jest to taki uporządkowany zbiór danych gdzie każdy element ma swój element nadrzędny, chyba, że jest elementem najwyższego rzędu.
dając przykłady z życia – drzewami są opisane wszelkiego rodzaju hierarchie – służbowe, kategorie (np. w sklepie internetowym), różnego rodzaju podziały. drzewami są też wiadomości na forach dyskusyjnych – a dokładniej – w drzewa są poukładane wątki z tych wiadomości.
ogólnie – struktury drzewiaste mają sporo zastosowań.
z tego powodu stały się one obiektem mojego zainteresowania, testów i badań. o struktury drzewiaste pytam potencjalnych pracowników na rozmowie kwalifikacyjnej.
w czasie swojej pracy z "drzewami" wyodrębniłem kilka metod które kiedyś już opisałem (link do http://www.dbf.pl/faq/tresc.html?rozdzial=1#o1_9), ale tu postaram się opisać je dokładniej i podając przy okazji wyniki pewnych testów na wydajność.
ze względu na obfitość materiału tekst ten podzielę na kilka wpisów: wstęp (to co czytacie), następnie po jednym wpisie na każdą z opisywanych metod, potem jakieś podsumowanie i na koniec kilka uwag praktycznych do wybranej przeze mnie metody.
ponieważ testowanie wydajności powinno odbywać się na sporych zestawach danych, a jednocześnie pokazywanie "o co mi chodzi" powinno być na możliwie małych ilościach – na potrzeby tego tematu przygotowałem 2 zupełnie różne zestawy danych:
1. małe drzewko w celach dydaktycznych:
to drzewko, zgodnie z konwencjami stosowanymi w tym dokumencie, ma następujące elementy:
- sql
- sql.oracle
- sql.oracle.linux
- sql.oracle.linux.glibc2
- sql.oracle.linux.glibc1
- sql.oracle.solaris
- sql.oracle.windows
- sql.postgresql
- sql.postgresql.linux
2. spory zestaw danych pobrany z katalogu dmoz – lista kategorii
dane z dmoza poddałem obróbce usuwając z nich znaki spoza zestawu A-Za-z0-9_, i ustawiając separator elementów drzewa na ".".
powstała lista elementów ma 478,870 elementów.
ścieżki z dmoz zawierają od 3 do 247 znaków, największe zagłębienie w drzewo jest w elemencie Regional.North_America.United_States.New_York.Localities.N.New_York_City.Brooklyn.Society_and_Culture.Religion.Christianity.Catholicism.Eastern_Rites.Maronite
w celu porównania wydajności różnych metod zapisu drzew będę testował następujące scenariusze testowe:
- pobranie listy elementów głównych (top-levelowych)
- pobranie elementu bezpośrednio "nad" podanym elementem
- pobranie listy elementów bezpośrednio "pod" podanym elementem
- pobranie listy wszystkich elementów "nad" danym elementem (wylosowanym)
- pobranie listy wszystkich elementów "pod" danym elementem (wylosowanym)
- sprawdzenie czy dany element jest "liściem" (czy ma pod-elementy)
- pobranie głównego elementu w tej gałęzi drzewa w której znajduje się dany (wylosowany) element
dodatkowo zostanie sprawdzona wielkość tabel i indeksów na założonej strukturze danych.
dla porównania – zapis w postaci takiej jak w pliku źródłowym zajmuje:
– tabela dmoz: 57,319,424 bajtów
– index pkey: 8,609,792 bajtów
– unikalny indeks ścieżki: 51,888,128 bajtów
tak więc – w ciągu najbliższego czasu będę pokazywał kolejne metody oraz wyniki ich testów.
two for the money / podwójna gra
obejrzałem sobie tę (dosyć nową (2005) w sumie) produkcję.
ocena ogólna – około 7/10. film nie jest zły. jest nawet dobry. ogląda sie go przyjemnie.
jedynym problemem tego filmu jest to, że spodziewałem się więcej. w roli głównej al pacino. człowiek legenda. heat (gorączka) oglądam co jakiś czas by zobaczyć kawał wspaniałego kina. ojciec chrzestny, adwokat diabła (film zebrał takie sobie oceny, ale rola ala w nim jest niesamowita).
tu – oczywiście nadal jest tym samym diabłem. świetnym. tyle, że trochę jednak starszym. mającym swoje problemy.
na dokładkę do ala w filmie jest rene russo – z mojego punktu widzenia znana głównie z zabójczej broni 3 i 4, ale zdarzyło jej się zagrać też w innych, nie najgorszych, filmach.
no i trzeci aktor pierwszoplanowy – matthew mcconaughey. ciekawe ile z was kojarzy nazwisko. mnie sie on głównie kojarzy z rolą w świetnym kontakcie (contact) z jodie foster.
i rene i matthew zagrali poprawnie. w/g mnie przemiana brandona w john'a jest mało przekonująca (spokojnie, nie zdradzam tu nic tajnego). russo jest zdecydowanie bardziej przekonująca – może dlatego, że ta rola powstała dla niej i z myślą o niej jako odtwórczyni 🙂
kończąc – co by się nie rozpisywać i nie zdradzać nic z fabuły – film jest przyjemny. nie jest to arcydzieło kina światowego, ale spędziłem miłe 2 godzinki oglądając go. drugi raz nie zamierzam, ale żałować, nie żałuję.