drobny błąd

a dziś napiszę o tym jakim to łosiem można być. niechcący.

na potrzeby jednego z projektów napisałem własnego orm'a (object-relationship mapper). nie znacie? takie cos co pozwala widziec rekordy z tabel jako obiekty. ogólnie – każdy orm jest bez sensu. mój tym bardziej, ale służył do prostego celu – uproszczenia robienia eksportów.

działał.

do czasu.

ostatnio na jednej z maszyn eksporter zaczął zżerać cały ram. calutki. i wywalać maszynę. usiadłem do debugowania. i oto co ujrzałem:

...
sub _table { my $self = shift; return $self->{ 'table_name' } }
sub _db    { my $self = shift; return $self->{ 'db' } }
sub _log   { my $self = shift; return $self->{ 'log' } }
sub _refresh {
my $self   = shift;
my $sql    = sprintf 'SELECT * FROM %s WHERE id = ?', $self->_table;
my $record = $self->_db->get_single_record( $sql, $self->{ 'data' }->{ 'id' } => 'INT8' );
if ( $record ) {
$self->{ 'data' }    = $record;
$self->{ 'fetched' } = 1;
return;
}
$self->log->critical(
"Cannot refresh record data from table " . $self->_table . " for id = " . $self->{ 'data' }->{ 'id' } );
croak( "DB Error" );
}
sub _get {
my $self = shift;
my ( $field ) = @_;
$self->_refresh unless $self->{ 'fetched' };
return $self->{ 'data' }->{ $field };
}
sub AUTOLOAD {
my $self   = shift;
my $method = $AUTOLOAD;
$method =~ s/.*:://;
return unless $method =~ m{ \A [a-z][a-z0-9_]* \z }xmso;
return $self->_get( $method );
}

zasada działania bardzo prosta – jeśli mam obiekt klasy dziedziczącej z tego orm'a, i wykonam na nim metodę:

$obiekt->jakies_pole

(gdzie jakies_pole nie jest nazwa istniejacej metody), to request trafi do autoloada, który wywoła _get. _get sprawdzi czy obiekt załadował z bazy dane. jak tak – zwróci odpowiednie pole i po sprawie.

a co jeśli nie?

wtedy kod trafia na _refresh(). refresh odczytuje cały rekord z bazy w oparciu o id (które musi być). fajne.

jedno pytanie: co się stanie gdy rekordu w bazie nie będzie?

tradycyjna odpowiedź: kod zaloguje informacje o błędzie i wykona croak(). czyli taki die.

ale nie. niestety. złośliwy los i brak dobrych oczu spowodował, że napisałem $self->log->(), podczas gdy obiekt loggera jest dostępny przed $self->_log.

efekt? metody log nie ma. trafiamy na autoloada. autoload odsyła do _get'a, _get do _refresha. _refresh znowu nie znajduje rekordu, więc … i kółeczko się zamyka.

nieskończona rekurencja, 3 giga zużytej pamięci, kilka godzin pracy paru osób. przez brak jednego “_".

a czemu o tym piszę? abym pamiętał. i sprawdzał kod. i bym miał okazję się pochwalić jakie “fajne" błędy potrafię wygenerować, a potem wykryć. i pochwalić “perl -d" – debugger jest wkurzający, mało sympatyczny. i ratuje d… jak oczy zawiodą.

aha. i jak fajnie wygląda “T" pod debuggerem po np. 50 przebiegach tej rekurencji 🙂 (T == stack trace).

miejski samochód

massachusetts institute of technology – jedna z najbardziej znanych uczelni technicznych na świecie. i do tego miejsce które co chwilę ma nowe fajne projekty badawcze.

tym razem badacze z mit wymyślili nowy samochód. miejski. na czym polega jego “nowość"? na modelu używania, ilości zajmowanego miejsca itd.

ponowie opracowali samochód 2 osobowy który można “składować" jak wózki w supermarkecie. działa na silnik elektryczny i ma interesujący model działania/używania. mianowicie nie miałby to być samochód do kupienia. idea jest taka, że miasto kupuje ich dużo, rozmieszcza w potrzebnych miejscach. a użytkownicy? podchodzą do dowolnego samochodu, wsiadają i jadą. płacą kartą przy użyciu. minus – muszą odstawić na miejsce. niekoniecznie to z którego wzięli – wystarczy dowolny “parking" tych samochodzików.

nie jest to na pewno samochód na długie podróże. ale na proste “podjechanie do klienta" czy “skoczenie do sklepu" – może się okazać wystarczający. pomysł ciekawy.

na stronie boston globe można znaleźć artykuł, ale co ciekawsze – flashową prezentację tego jak to ma działać.

porównanie implementacji ruby’ego

dzięki znajomemu (hi tmarc) dowiedziałem się o ciekawym tekście. autor porównał w nim wydajność kilku (41) testów w różnych implementacjach języka ruby.

udział wzięły kanony:

  • ruby 1.8 (na linuksie i na windows vista
  • ruby 1.9

ale także wersje mniej standardowe:

  • jruby
  • gardens point ruby .net (wersja beta, na windows vista)
  • rubinius
  • cardinal

porównano wydajność każdej z implementacji w stosunkdu do złotego wzorca – czyli 1.8 na linuksie.

efekt? no cóż. 1.9 zdecydowanie rządzi. ruby 1.8 na linuksie ma drugie miejsce. wersja na windowsach jest trochę wolniejsza, ale (co ważniejsze) wywala się na 2 testach!

pozostałe implementacje: wolno i kiepsko. “hitem" jest cardinal – większość testów zakończona błędem, dwa trwały powyżej  15 minut i zostały ręcznie ubite (te testy były “zrobione" przez 1.8 w czasach 9.5 i 0.5 sekundy!). choć trzeba przyznać, że w 3 testach cardinal osiągnął najlepszy wynik. wydajnościowo gorszy był rubinius, ale on miał mniej errorów (“ledwie" koło połowy).

można spojrzeć by rozwiać złudzenia, lub nauczyć się czegoś z testów.

nowa technologia kości ram

ibm ogłosił, że opracowali właśnie nową technologię produkcji kości ram.

nowe kości są rozwinięciem idei i metody produkcji dram, ale prędkością zbliżone do sram. czyli około 2 krotnie szybsze od obecnych kostek.

sram od zawsze był szybszy, ale te kości były sporo większe niż standardowe dramy. więc się nie przyjmowały w standardowych zastosowaniach. teraz jednakże – gdy nowy dram ma osiągnąć wydajność sram, można liczyć na spory przełom.

poza zwiększoną szybkością nowe kości mają być też pojemniejsze. trzykrotnie!.

podsumowując: 3 razy więcej ramu, działającego 2 razy szybciej. ciekawe tylko kiedy to wejdzie do seryjnej produkcji i sprzedaży – na razie ibm szacuje, że pierwsze handlowe kości pojawią się w 2008 roku i będą to pamięci serwerowe. inne typy pamięci pojawią się później. a kiedy do laptopów?

dlaczego “kocham” mysql’a

postawiłem sobie w miarę nowego mysql'a: Server version: 5.0.32-Debian_2-log Debian etch distribution.

zrobiłem sobie testową tabelkę:

mysql> create table x (id int primary key);
Query OK, 0 rows affected (0.05 sec)

sprawdzam czy dobrze się założyła, wygląda ok:

mysql> desc x;
+-------+---------+------+-----+---------+-------+
| Field | Type    | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id    | int(11) | NO   | PRI |         |       |
+-------+---------+------+-----+---------+-------+
1 row in set (0.08 sec)

no to teraz pora na zabawę. wstawiam tam 1 rekord:

mysql> insert into x (id) values (6);
Query OK, 1 row affected (0.06 sec)

nadal ok. to teraz wstawiam 10 rekordów z celowym błędem:

mysql> insert into x (id) values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
ERROR 1062 (23000): Duplicate entry '6' for key 1

i teraz zagadka/pytanie:

ile jest rekordów w bazie?

odpowiedź – po przerwie.

Continue reading dlaczego “kocham" mysql'a

postgres 8.3 – wishlist

dobrze wiedzieć co się pojawi w nowej wersji. ten site przedstawia skrótowo to co ma być, może być, fajnie by było jakby było, a co prawie na pewno spadnie do 8.4.

z ciekawszych rzeczy – “grouped index tuples", osbługa xml'a w bazowym postgresie, identity/generated (rany, jak mi tego brakowało), tsearch2 w postgresie bazowym, no i enumy.

co z tego faktycznie wejdzie – zobaczymy. ale zapowiada się miło.

aha. spojrzałem, że mają się też pojawić indeksy bitmapowe. sama funkcjonalność miła, ale jak dla mnie indeksy bitmapowe są ultra-ciekawe z prostego i poza technologicznego powodu. już teraz od groma ludzi myli to co jest w postgresie “bitmapy indeksowe" z tym co by chcieli by było “indeksy bitmapowe". co zrobią ci wszyscy jak do postgresa wejdą te prawdziwe indeksy?

heh.