2010. február 23., kedd

Kis elmélkedés a rákról

A rák mint betegség mindig is lenyűgözött a maga gonosz és halálos módján, hiszen nagyon különbözik minden más betegségtől. Inkább valami olyan dolog, ami elrejtve ott van abban a mechanizmusban, amit életnek hívunk. Kicsit groteszk és ambivalens dolog, hogy a rákot végül is pont azok a folyamatok alakítják ki, amelyek annak idején magának az életnek a kialakulását is felelősek voltak.

Az élet alap építő köve a DNS, amely arra vonatkozó instrukciókat hordoz, hogy az egyes aminosav darabkák milyen sorrendben kapcsolódjanak össze. A darabkák fehérjéket alkotnak, a fehérjék, és azok bonyolult ökoszisztémája sejteket, a sejtek, és azok még bonyolultabb ökoszisztémája szerveket, a szervek pedig élőlényeket. Olyan bonyolult hálózat ez, ami emberi ésszel felfoghatatlan, és kezelhetetlen.

(Ezeket a rendszereket véleményem szerint teljes egészében sohasem leszünk képesek átlátni, erre csak gépek képesek, és ha valaha teljes egészében megértjük az élet működését, az csakis azt jelentheti, hogy rendelkezünk olyan szoftverekkel, amelyek képesek bármilyen feltett kérdésünkre választ adni. Lehetetlennek tartom, hogy ezt csupán emberi ésszel átlássuk. De ez megérne egy újabb posztot, így nem is írok róla többet.)

A komplexitás ellenére azért vannak olyan kulcsfolyamatok, amiket már egész jól értünk, és ilyen a DNS célja és működése is. A DNS-re épülő szervezeteket pont az tette ilyen sikeressé, hogy képesek a változásra. Az evolúció maga is csakis ezért mehetett végbe, mert a DNS képes a mutálódásra. A földön lévő folyamatosan változó őslevest végül a természetes szelekció csiszolta a ma ismert élővilággá. Az evolúció ugyanakkor emberi léptékkel mérve nagyon lassú folyamat, a teljes emberiség története evolúciós léptékkel csak néhány pillanat.

(Valójában napjainkban a föld történetét alakító folyamatokban nem is az élőlények evolúciója a mérvadó, hanem inkább a mémeké. Az evolúcióhoz hasonló szelekciós folyamat alakította ki a törvényeket, a ma ismert társadalmakat, de ez mozgatja a technológia fejlődését, vagy éppen a közgazdaságtant is. Éppen ezért ügyelnek olyan kínosan a kapitalista társadalmak a szabad verseny meglétére, hogy ne lassuljon meg, illetve ne állhasson meg ez az evolúciós folyamat, és ezzel a technológiai/társadalmi/gazdasági fejlődés. De ez megint egy másik történet.)

És hogy mindez hogy jön a rákhoz? Nos, a rák tulajdonképpen az élőlények hozománya azokból az időkből, mikor még fontos volt az élőlények evolúciója, a rákot ugyanis pont ugyanez a folyamat alakítja ki. Az emberi sejtekben lévő DNS jellegéből adódóan mindenféle külső/belső események hatására megváltozhat. Vannak bizonyos anyagok (pl. a nikotin, alkohol, és még rengeteg más) amelyek változtatják a DNS szerkezetét. Mivel ez általában negatív következményekkel jár, azt mondjuk roncsolják, de ez nem feltétlen helytálló, hisz pl. a mostani kutatások szerint az Archeimer kór kialakulását éppen hogy blokkolja a nikotin. Ugyanígy a sugárzás (pl. UV sugárzás) is képes a DNS megváltoztatására, amire megint csak negatív hatásként tekintünk, de nem szabad elfelejteni, hogy e nélkül valószínűleg nem alakulhatott volna ki az élet. A DNS csak úgy magától is megváltozhat. A DNS másoló mechanizmusok, ahogyan minden más élettel kapcsolatos dolog, evolúciós "tákolmányok", nem pedig pontosan megszerkesztett gépezetek. Gyakran előfordul, hogy a sejtosztódás alkalmával egyszerűen hibásan másolódik le a DNS. Tehát a DNS jellegéből adódóan változékony, és ez által egyben sebezhető is, ami kétélű dolog. Egyfelől e nélkül nem lehetnénk itt, másfelől emiatt alakul ki bennünk a rák (és sok más egyéb betegség is). A helyzet persze nem annyira rossz. Szervezetünk rendelkezik néhány javító mechanizmussal, amik felismerik, és kiküszöbölik ezeket a hibás sejteket. Vannak ellenőrző mechanizmusok, és olyanok is, amelyek egyszerűen elpusztítják a hibás sejteket. Probléma akkor van, mikor a DNS hibák hatására éppen ezek a folyamatok sérülnek, és a sejt elszabadul. Egy ilyen kontrollálatlan sejt kiesik a jól összehangolt ökoszisztémából, és beindul egyfajta evolúciós folyamat. A sejt burjánzik, alakul, valószínűleg sok utódja el is pusztul, de marad néhány erősebb. Ezekből lehet, hogy sok megint csak elbukik, talán az immunrendszer áldozatai lesznek, de mindig maradhat néhány, akik új kolóniákat hozhatnak létre. Először akkor válik veszélyessé a helyzet, mikor a sejtek a korlátlan mutálódás következtében megszerzik a halhatatlanság és a korlátlan szaporodás képességét. Ilyenkor elkezd növekedni a daganat. A növekedés egy idő után megáll, hiszen a néhány milliméteres sejttömeg közepe éhezni kezd, társai elszigetelik, és a daganat nem jut kellő tápanyaghoz. Itt a folyamat akár meg is állhatna, de a folytonos mutáció és burjánzás nem marad abba. Addig alakul a renegát sejtcsomó, míg végül "rájön" hogy juthat tápanyaghoz, és vérereket növeszt. Valójában persze a tumor nem okos, csak mindenféle anyagokat köpdös ki magából, és csak idő kérdése, hogy olyan anyagok kerüljenek ki, amelyek a környező sejteket vérerek alkotására késztetik. Ez az evolúció vak (hiszen hagyományos értelemben nem rendelkezik tudattal) zsenialitása. Amikor a daganat tápanyaghoz jut, újra növekedésnek indul. Ekkor már gondot okozhat a környező szervek működésében, elzárhat ereket, nyomhat szerveket, stb. Ha ilyen lokális állapotában sikerül észrevenni, és elpusztítani, vagy egyszerűen kimetszeni a daganatot, akkor a beteg még jó eséllyel meggyógyulhat. Persze már ilyenkor is lehet olyan helyen (pl. az agyban), amihez képtelenség úgy hozzáférni, hogy a beteg túlélje, a nagyobb baj mégsem ez szokott lenni. A daganatban ugyanis kialakulhat a képesség, hogy leszakítva egyes darabjait azok a szervezet különböző helyein újabb kolóniákat hozzanak létre. Ilyenkor beszélünk áttétes daganatról. Ilyen esetben már csak nagyon drasztikus eszközökkel lehet támadni. Sugarazni, mérgezni kell, ami az egész szervezetet megviseli, de még innen is vissza lehet jönni. A rák tehát kicsit olyan mint az ember története a földön. Ha nem sikerül elkapni, idővel ő kerül a tápláléklánc csúcsára, és átveszi a hatalmat a test felett még végül saját magával együtt elpusztítja azt. A jelenlegi környezetszennyező, fogyasztói életmódot látva szinte adja magát a hasonlat a rák és az ember között, aki korlátlanul felélve az erőforrásokat végül a teljes élővilág pusztulását okozhatja (saját magát is beleértve). Persze reméljük nem így lesz, és nem úgy vonulunk be a föld történetébe, mint a föld rákja.

Végül egy kicsit a gyógymódokról. Az egyik leghatékonyabb eszköz az, ha időben, gyenge korában diagnosztizálható és elpusztítható lenne a rák. Vannak ilyen irányú kutatások. Olyan vegyületek fejlesztésén dolgoznak, melyek a rákra jellemző kísérő molekulákra, sejti elváltozásokra tapadnak, és jelzik a kezdődő rák jelenlétét, esetleg mérget juttatnak a rákos sejtbe. El tudok képzelni például olyan megoldást, ahol a kapcsolódó molekulákhoz kis méreganyag "tartályokat" (pl. fulleréneket) kötnek, amik a rákos daganat felszínén megtapadnak. Ezeket a kis "aknákat" valamilyen külső hatás (pl. sugárzás vagy valamilyen másik anyag szervezetbe juttatása) kioldaná és megmérgeznék a beteg sejtet. De ugyanígy vannak próbálkozások olyan módosított vírusok létrehozására, amelyek csak a rákos sejteket támadják meg. A rák diagnosztizálására is vannak érdekes módszerek. Például a rákos sejtek által kiválasztott bizonyos anyagokat a kutyák képesek kiszimatolni, így diagnosztizálható a bőrrák vagy a tüdőrák csupán az által, hogy a mintát megszagoltatjuk a kutyával. Erre építve valószínűleg mesterséges szag érzékelőkkel is lehetne diagnosztizálni.

A rák operálására is vannak módszerek. Olyan esetben például mikor a daganathoz nem lehet hozzáférni, használhatnak gamma kést. Ez úgy történik, hogy az ember testét MRI-vel (vagy CT-vel, ebben nem vagyok biztos) végigpásztázzák, így a műtéti terület megbolygatása nélkül meghatározhatják a daganat helyét. Eztán több forrásból irányított sugárnyalábokat indítanak a terület felé. A sugárnyalábok egyenként gyengék, így nem roncsolják áthaladásukkal a sejteket, de a fókuszpontban elég erősek ahhoz, hogy elpusztítsák azt.

És végül mostanában olvastam egy új módszerről, amely tulajdonképpen a poszt apropóját is adta. Ennek az a lényege, hogy olyan anyagokat juttatnak a szervezetbe, amik gátolják a vérerek képződését, így megakadályozzák, hogy a tumor tápanyaghoz jusson, így megölve azt. Ez az út is rendkívül ígéretesnek tűnik.

Hát, hirtelen ennyi jutott eszembe erről a veszélyes, de maga nemében különleges képződményről, amely annyira mélyen az életbe és az evolúcióba van kódolva, hogy megjelenése tulajdonképpen elkerülhetetlen. Ha valakit mélyebben érdekel a téma, van egy nagyon jó összefoglaló könyv Ha egy sejt megkergül címmel.

2010. február 15., hétfő

JBoss POJO Cache mint adatbázis

Az memória alapú adatbázisok kapcsán kicsit utána néztem a postban felvetett megoldásnak, vagyis hogy a JBoss POJO Cache-re építve lehetne-e memória alapú adatbázist készíteni. A POJO Cache dokumentációja alapján elvileg minden adott ehhez. Ahogyan már írtam, clusteres kialakítás esetén a POJO cache önmagában képes a benne tárolt objektum fa transzparens replikálására, méghozzá úgy, hogy csak a változásokat szórja a rendszerben. A cache ezen kívül kezeli az objektum referenciákat, és még tranzakció támogatást is nyújt, tehát valóban minden rendelkezésre áll ahhoz, hogy adatbázis rétegként használjuk. Létezik egy mechanizmus, aminek segítségével a cache-ből kikerülő elemeket perzisztens tárba menthetjük, így a passziválás is megoldott. Ugyanígy létezik mechanizmus a változások mentésére, valamint a cache állapotának perzisztens tárolóból történő visszaállítására. A JBoss POJO Cache tehát elvileg könnyen alkalmassá tehető arra, hogy memória alapú adatbázisként működjön, sőt sokkal egyszerűbben, mint gondoltam. E ellett olyan feture-ök is eleve adottak, mint például a tranzakció kezelés. Egyedül az volt csak fura, hogy hiába kerestem a neten, nem találtam példát ilyen megvalósításra, pedig szinte adja magát a dolog. Már csak egy projekt kellene, ahol a gyakorlatban is ki lehet próbálni a fentieket ...

Memória alapú adatbázisok

A napokban ismertem meg egy új adatbázis szervezési filozófiát, az Object Prevalence -t, amit jobb híján memória alapú adatbázisnak fordítottam (tudom, hogy a szó szerinti fordításnak semmi köze ehhez, de ez adja vissza legjobban magyarul, hogy miről is van szó). A filozófia alapgondolata, hogy napjainkban a memória nem túl drága erőforrás, és megfelelő mennyiségben rendelkezésre is áll ahhoz, hogy egy rendszer teljes adatbázisát ott tároljuk. Gondoljunk csak bele, ha mondjuk egy felhasználó tárolása 5K memóriát igényel (és 5000 karakterbe aztán tényleg rengeteg adat belefér), akkor 1000 felhasználó mindössze 5Mb-ot, 10 000 pedig 50Mb-ot igényel. 10 000 felhasználó már egész jelentősnek mondható, ugyanakkor 50 Mb a jelenlegi RAM méretek mellett elenyésző. Ez alapján szinte adja magát a gondolat, hogy a teljes adatbázist tartsuk a memóriában, célszerűen az adott programozási nyelv jól megszokott eszközeivel (pl. Java esetén sima Map-ek és List-ek). Kicsit olyan érzésem van a dologgal kapcsolatban, mintha a relációs adatbázis kezelők csak mindössze a nyakunkon maradtak volna azokból az időkből, mikor még a memória és a tároló kapacitás drága volt.

Más szempontból vizsgálva azt is mondhatjuk, hogy a memóriába ágyazott adatbázisok a jelenlegi rendszerek optimalizálásának logikus következő lépései. Gondoljunk csak egy általános vállalati rendszerre, ahol az adatbázist valamilyen ORM rétegen keresztül (pl. Hibernate) érjük el. Egy entitás objektum módosítása esetén módosítjuk a memóriában lévő lokális változatot, amit az ORM engine SQL utasításokra alakít, átpaszírozza őket egy socketen, ott az adatbázis szerver értelmezi az SQL utasításokat, és módosítja a winchesteren található fájlokat. Ha adatot olvasunk, ugyanez megy visszafelé. Az az ironikus a dologban, hogy a winchesteren lévő adatbázis fájlokat a gyakori használat miatt az operációs rendszer jó eséllyel felcache-eli a memóriába, így végső soron ugyanúgy a memóriából dolgozunk, csak ehhez a szükséges erőforrások sokszorosát használjuk el. Memória tekintetében rossz esetben legalább 2x megvan az adat (az ORM által kezelt memória reprezentáció, valamint az SQL adatbázis felcache-elt része), és itt még nem beszéltünk a közbe iktatott mindenféle cache-ekről, amit az adattáblák és a Java alkalmazás közé pakolunk, hogy gyorsítsuk az elérést. Számítási erőforrást tekintve megint csak elég rossz a helyzet, hiszen az adatok módosítása egy rakás SQL-t eredményez, amit az adatbázis szervernek értelmeznie kell, majd végrehajtania a változásokat, újraindexelni a táblákat, stb. A memória alapú adatbázisok segítségével ezek a felesleges köztes lépések kiküszöbölhetőek.

Jelenleg két implementációt találtam Java nyelven a memóriába ágyazott adatbázisokra. Az egyik a Prevayler a másik pedig a valamivel okosabbnak tűnő Space4j.

Persze az első kérdés, ami felmerül, hogy ha minden adatot a memóriában tartunk, mi történik egy esetleges rendszer leállás/áramszünet, stb. esetén. A Prevlayer/Space4j megoldása a problémára, hogy minden adatmanipuláló műveletet un. Command-okba csomagolunk és a memória objektumok manipulálását csak és kizárólag ezeken keresztül végezhetjük. Minden Command végrehajtását log-oljuk, így ha leáll a rendszer, nincs gond, hiszen a Command-okat újra lefuttatva visszakapjuk az eredeti állapotot. A gondolat maga nem új keletű. Így működnek az adatbázis rendszerek tranzakciói, vagy éppen a HSQLDB, ami egy ugyancsak memóriában működő, de SQL alapú adatbázis. Hogy a log fájl ne nőjön túl nagyra, bizonyos időközönként a rendszer csinál egy memória snapshot-ot, amikor a teljes állapotot lemezre menti. Ez kicsit erőforrás igényesebb művelet, de általában ez is viszonylag gyorsan megtörténik, és csak ritkán kerül rá sor.  Egy ilyen adatbázisban tehát az olvasás extrém gyorsan történik, hiszen egyszerű memória műveletekről van szó, és a módosítás is nagyságrendekkel gyorsabb, mint egy relációs adatbázis esetén, hiszen mindössze csak a Command-ot kell serializálni, és letenni a log-ba, nem kell SQL-eket értelmezni, indexeket karbantartani, fájlokat átrendezni, stb.

A következő kérdés, ami felmerül egy ilyen rendszerrel kapcsolatban, hogy mi van, ha valahogy mégis olyan hatalmasra nőne az adatbázis, hogy nem fér el a memóriában, esetleg másnak is kell a memória. A Space4j-ben ennek megoldására létezik egy mechanizmus, ami a nem használt adatokat a lemezre "passziválja". Egy adatbázis esetén valószínűleg sok ilyen objektum van, amire épp aktuálisan nincs szükség. A mechanizmus hasonló az operációs rendszerek virtuális memória kezeléséhez, oly annyira, hogy talán nem is érdemes ezzel alkalmazás szinten foglalkozni. Bár gyakorlati tapasztalatom nincs, valószínűleg egy linux rendszer esetén elég lenne annyi, hogy megfelelően nagy swap területet hozunk létre. Bár nem tudom, hogy a kernelnél hol a határ, de ha jól tudom, a mai modern processzorok  legalább 4 Tb memória megcímzésére képesek. Tehát elvileg nem tiltja semmi, hogy akár Tb-os méretű swap használatával gigantikus virtuális memóriát hozzunk létre, amiben már akármilyen adatbázis kényelmesen elfér. Ráadásul az operációs rendszer natív virtuális memória kezelő mechanizmusa biztosan nagyságrendekkel gyorsabban és hatékonyabban működik, mint egy Java-ban programozott megoldás.

A memória alapú adatbázisok működése tehát egyszerű, elegáns, gyors, és erőforrás takarékos, ugyanakkor szinte mindenre képesek, amire egy relációs adatbázis. Például nagy adathalmazok esetén ugyanúgy definiálhatunk indexeket, de itt a megoldás jellegéből adódóan nem kell bonyolult index struktúrákban gondolkodni, elég egy bináris fa, amit az egyetem első félévében minden programozó tanonc fejébe belevernek.

Bár még a gyakorlati tapasztalatom nincs ezekkel a rendszerekkel, az látszik, hogy legnagyobb hátrányuk a Command-ok viszonylagos bonyolultsága lehet, ezekben a rendszerekben ugyanis a Command-ok a módosítás atomi műveletei, amelyek működésébe a rendszer nem lát bele. Az egyetlen dolog, amire a rendszer képes, hogy végrehajtsa a Command-okat. Minden adatellenőrzést, ütközés figyelést, stb. a Command-on belül nekünk kell leprogramozni, ezekhez a rendszer nem nyújt semmilyen támpontot. A másik dolog, ami csak nagyon problematikusan oldható meg, az a konkurens tranzakciók használata. Ez csak oly módon kivitelezhető, hogy másolatot készítünk a tranzakcióban részt vevő objektumokról, majd a tranzakció végeztével a módosításokat végrehajtjuk a memóriában lévő globális objektumokon. A másolást nem tudja nekünk transzparens módon elvégezni a rendszer, hiszen nem lát bele az atomi Command-ok működésébe, így azt sem láthatja, hogy azok milyen adatokat módosítanak. Így az egyetlen lehetőségünk, hogy leprogramozzuk ezeket a másolásokat.

Ami még nagy előnye a rendszernek, hogy nagyon egyszerűen clusterezhető. Minden cluster elem rendelkezik egy saját objektum fával, és bármelyik rendszerben valamilyen módosítás történik az adatbázison, egyszerűen broadcast-olnia kell a módosítást végző command-ot. Persze ilyen elosztott rendszerben a command-okat érdemes timestamp-el ellátni, a hálózati lag-ból eredő elcsúszások kiküszöbölésére (a Command-ok megfelelő sorrendben történő végrehajtása nagyon fontos). Maga a megoldás (módosítások broadcastolása a rendszerben) megint csak nem új keletű. Tulajdonképpen az összes clusterezhető cache (memcached, ehcache, JBossCache, stb.) így működik. Ha valahol változik valami, azt jelszik a többi szerver felé, így tartva konzisztensen a globális állapotot.

Tulajdonképpen a clusterezés gondolatán elindulva jutott eszembe, hogy a Cluster Cache-ek filozófiáját és a memória alapú adatbázisok filozófiáját ötvözve létre lehetne hozni valami perzisztens cache szerű dolgot, ami mentes lenne a Command-ok bonyolultságából eredő hibáktól. A rendszer alapja a JBossCache, vagy valamilyen ahhoz hasonló megoldás lehetne. A JBoss POJO Cache ugyanis tud valami nagyon ügyeset. Komplett Java objektum fákat tárolhatunk benne, mégis gyorsan képes szinkronizálni a cluster egyes elemeit. Ezt úgy éri el, hogy mindenféle proxy-k (és talán AOP) segítségével figyeli, hogy a cache-ben tárolt adatok mikor változnak, és csak a fa változásait küldi szét a többi szervernek. Ez nagyon hasonlít egy cluster-be kötött memória alapú adatbázis működéséhez, ahol a command-ok a JBossCache-ben tárolt objektumok változásai. Nem kellene tehát más tenni, mint a cluster cache-t kiegészíteni egy olyan mechanizmussal, amely folyamatosan log-olja a műveleteket, és néha snapshot-ot készít az aktuális állapotról. Ezt neveztem el én perzisztens cache-nek. Egy ilyen megoldásban nem lenne szükség command-ok írására, így az adatbázis kezelés valóban mindössze memóriában tárolt Java objektumok használatából állna, a rendszer valóban teljesen transzparens lenne. Ami a command-ok egyéb funkcióját illeti (adat ellenőrzés, indexelés, stb.), azt AOP segítségével aggathatnánk rá az adott objektumokra, így megőrizve a rendszer transzparenciáját.

JBoss hibakeresés

Mostanában elég sokat szívtunk néhány hibával egy JBoss-on futó rendszerben. A legtöbb hiba persze fejlesztéskor rögtön szemet szúr, vagy ha mégsem, úgy a log alapján hamar megtalálható az oka, van azonban néhány elég nehezen felderíthető. A legutálatosabb, mikor a nem optimális kód, vagy egy rejtett (akár több metódus híváson keresztül ívelő) végtelen ciklus annyira leterheli a rendszert, hogy az képtelen válaszolni (kvázi DoS-olja azt). PHP esetén van erre egy nagyon egyszerű mechanizmus, ami figyeli, hogy mi mennyi ideig fut, és ha túl sok idő telt el, egyszerűen kilövi. Nem is csoda, hogy a hosting szolgáltatók körében a PHP a leggyakoribb, hiszen erre könnyen lehet biztonságos hosting szolgáltatást építeni (a jogosultságok, memória limitek, idő limitek jó beállításával kiküszöbölhető, hogy a felhasználó megölje a szervert). Ilyen mechanizmust nem találtam JBoss-hoz (Tomcat-hez, mert valójában az intézi a web-es dolgokat), így marad a szerver újraindítása, és a log fájlok bújása.

Az ilyen hibák felderítésére a legegyszerűbb módszer, hogy a JBoss PID-re kiadunk egy kill -3 -at, aminek hatására a Java VM a log-ba hányja az aktuális szálak állapotát stack trace-el. Ilyen kill -3 -ból érdemes többet is nyomni néhány másodperces eltéréssel, hogy később ne csak az utolsó állapotot lássuk a szálaknak, hanem az egyes szálak működését folyamatában (pl. egy több metódusos végtelen ciklust így lehet leginkább kiszűrni). A baj az ilyen thread dump-okkal az, hogy a processzor használatot nem látjuk belőlük, így nem látszik, hogy pontosan melyik szálak próbálják megenni a processzort. Hogy ezt megtudjuk, adjunk ki egy ps -L auxH parancsot, ami az egyes szálak processzorhasználatát mutatja. A log-ot, és a ps eredményét az LWP/nid köti össze. A ps-ben látható LWP a szál egyedi azonosítója, amit hexadecimális formában nid néven a thread dumpban is megtalálunk. Tehát nincs más dolgunk, mint megkeresni a kiugró, extrém magas processzor használatot mutató szálakat, az ezekhez tartozó LWP-ket hexadecimálisra váltani, majd belekeresni a logban, hogy a hozzá tartozó szál mit csinál. Ezzel a módszerrel viszonylag egyszerűen megtalálhatjuk a kód kritikus részeit.

Annak idején én külföldi blog-ok alapján szedtem össze ezt a módszert, így gondoltam megosztom magyar nyelven is, hátha mások is hasznát veszik.

2010. február 14., vasárnap

Óvatosan a Hibernate-el

Nemrégiben egy projekt kapcsán felmerült egy elég csúnya hiba. Nagyobb megterhelés esetén a rendszer processzor használata extrém magasra pörgött fel, olyannyira, hogy képtelen volt a webes kérések kiszolgálására, így újra kellett indítani. A rendszer fejlesztésével egyre instabilabbá vált a rendszer, aztán egy ponton elérte a teljes használhatatlanság fokát. Sokáig kerestük a hiba okát, mire rájöttünk, hogy mi okozta.

A Hibernate egy rendkívül kényelmes JPA alapú ORM rendszer. Az ember létrehoz néhány felcímkézett osztályt, és ide tárolja az adatokat. A Hibernate pedig szinte láthatatlanul elvégzi az adatbázis leképzést, és a kapcsolódó adatbázis műveleteket. A hibát ott követtük el, hogy kihasználva ezt a kényelmességet úgy használtuk ezeket a perzisztens objektumokat, mintha a memóriában lennének. Ciklusok futkároztak keresztül kasul a listákon, olvasgattuk/írogattuk az elemeket, mindeközben fittyet hányva arra, hogy milyen komoly adatbázis műveletek futnak a háttérben. Mint később kiderült, annyira komolyak, hogy néhány száz felhasználó taccsra tudott vágni egy 8 processzoros gépet. Mikor bekapcsoltuk az SQL-ek loggolását, elborzadva láttuk az eredményt, egy-egy webes kérés kiszolgálásakor százával pörögtek a 3-4x-es join-olt lekérdezések. Nem csoda, hogy az alkalmazás hamar felzabálta a gépet. Végül is az adatbázis használat optimalizálásával, és cache-ek használatával hamar úrrá lettünk a problémán. Ezt a kis affért tekinthetnénk akár egyszerű programozási hibának is, de engem gondolkodóba ejtett.

Egy régi kollégámmal annak idején sokat vitatkoztunk a Python és a Java nyelv kapcsán. Ő Python párti volt, én viszont nem nagyon tudtam megbarátkozni a nyelvvel. Ez azóta persze megváltozott. Script nyelvként nagyon megszerettem a Pythont, de komolyabb projektbe nem mernék belefogni vele. A vitánk alapja az volt, hogy szerintem a Python nagyon megengedő nyelv. Nem típusos, és szinte bármit felül lehet benne definiálni. Ez egyfelől nagyon kényelmes, ugyanakkor véleményem szerint egyben veszélyes is. Sokan egy programnyelv típusosságára vagy az operator overloading hiányára úgy tekintenek, mint koloncra, ami gúzsba köti a fejlesztőt, nem adja meg a kellő szabadságot, hogy kényelmes és elegáns kódot készítsünk. Én ugyanakkor ezeket a megkötéseket segítségnek tekintem, amelyek segítenek abban hogy hibátlan és optimális kódot készítsünk. A barátom érve ezzel szemben az volt, hogy ezeknek az eszközöknek a használata nem kötelező. Aki nem tud programozni, az ne használjon ilyesmit, aki jó fejlesztő, nem követ el szarvas hibákat. A probléma csak az, hogy az ember hamar elcsábul, vagy a felhasznált programkönyvtárak miatt belekényszerül abba, hogy éljen ezekkel a lehetőségekkel. A másik probléma, hogy ezek a nyelvek általában nem is adnak lehetőséget arra, hogy határok közé szorítsuk magunkat. Python esetén például ha akarnánk sem tudnánk típusos függvényeket írni, ami azért néha hasznos tud lenni, hiszen segít abban, hogy ne rontsuk el a paraméterezést (két paraméter véletlen felcserélése komoly galibát tud okozni). Van egy harmadik ok is, amiért a szigorú nyelvek mellett tettem le a voksomat. Egy komoly fejlesztés sokszor igen feszített tempóban történik. Kemények a határidők, sok a feature igény, kevés az ember. Ilyen körülmények között nem ér rá az ember gondolkodni, hogy vajon szép-e ez a kód, nem okoz-e majd később galibát, stb. Egyszerűen csak darálja a funkciókat, hogy elkészüljön a szoftver. Hasonló hibába estünk bele mi is a Hibernate kapcsán. Kihasználtuk az eszköz által nyújtott kényelmet, daráltuk a kódot, és nem gondoltuk végig, hogy ennek milyen következménye lehet. És ez a kis programozási nyelvekkel kapcsolatos kitérő itt kapcsolódik vissza az eredeti témához. Kényelem vs. biztonság.

A Hibernate-es probléma kapcsán tehát elgondolkodtam, hogy milyen elvek szerint kellene vajon felépíteni egy ORM rendszert úgy, hogy ne futhasson bele az ember ilyen hibákba, hasonlóan ahhoz, ahogyan a Java típusossága segít abban, hogy ne cserélhessük fel a paramétereket úgy, ahogy Pythonban. Az első gondolatom az volt, hogy talán vissza kellene térni a tiszta SQL-hez. Így rá vagyunk kényszerítve, hogy explicit módon adjuk meg a lekérdezéseket, és rá vagyunk szorítva arra, hogy ezek a lekérdezések optimálisak legyenek. (Legalábbis a nem optimális lekérdezések kibökik a szemünket.) Egy ilyen rendszerben SQL-el kérdeznénk le az adatokat, leképeznénk őket objektumokba, elvégeznénk a szükséges műveleteket, majd SQL-el pakolnánk őket vissza az adatbázisba. Ez tipikus DAO filozófia, és pont azért alakultak ki az ORM rendszerek, mert ez így azért elég kényelmetlen. E mellett azt se felejtsük el, hogy a manuális leképzés megint csak sok hibalehetőséget rejt magában. Az ORM rendszerek automatikus leképzése, és az objektum szintű típusosság sok gondot levesz a vállunkról, ráadásul az alkalmazás adatbázis rendszer független lesz, ami megint csak nagy előny. Tehát az ORM-től nem olyan könnyű megszabadulni, és valószínűleg nem is kell, de nagyon oda kell rá figyelni, ami nem jó, és mint látható, komoly hibákat szülhet.

Ami mostanában körvonalazódik a szemeim előtt, az valami olyan megoldás, ami ötvözi a JPA és a DAO-k előnyeit. A durva SQL-ek amiatt születtek, mert a rendszer felhúzott sok csatolt entitást is (ez okozta a sok join-t). Éppen ezért én a saját ORM rendszeremből kihagynám a kapcsolatokat, vagy legalábbis nem tenném annyira transzparenssé, ahogyan a Hibernate. Például egy entitáshoz kapcsolódó további entitásokat egy entitás osztályban definiált függvény szippantaná fel. Tehát az entitások tábla szintűek lennének, a kapcsolatok pedig explicit módon jelennének meg lekérdezések formájában. Bár nem okozott akkora galibát, de némi fejtörést igen, hogy bonyolultabb objektum manipulációk esetén az objektum struktúrát több request/response szakaszig a session-ben kell tartani, és csak ezután szabad visszarakni az adatbázisba. Mivel az entityManager csak 1-1 request alatt él, hamar problémát okozott, hogy egy session-ben lévő perzisztens objektumot (ami közben már elveszítette perzisztens jellegét) vissza akartunk rakni az adatbázisra (Lazy...Exception). Ennek kiküszöbölésére valamilyen long transaction mechanizmust tudnék elképzelni, ahol több request-en átívelő kérésekhez lehetne kérni egy long transaction-t. A long transaction session szintű objektum lenne, és ha felolvasunk/módosítunk valamit, ide kerülne. Végül egy commit-al a végén kerülne vissza az összes objektum az adatbázisra. Itt lehetne valamilyen ütközés figyelés (pl. verzionáljuk az objektumokat), ami konkurens írás esetén exception-el jelez. Végül pedig kellene valamilyen transzparens cache az adatbázis és az alkalmazás közé, hogy ha valamit felolvastunk, azt ne kelljen még egyszer felszedni. Valahogy így nézne ki egy ideális ORM rendszer.  Ami még kicsit zavaró, az a sok dinamikus proxy, ezek teljesen feleslegesen terhelik a processzort, ezek helyett hatékonyabb lenne statikus (fordítási idejű) kódgenerálást alkalmazni.

Találkoztam még egy érdekes Space4j nevű projekttel, ami az egész dolgot megfordította. Nem az adatbázist képezi memóriára, ehelyett az objektumokat a memóriában tartja, ami nem kell, azt perzisztensen kipakolja onnan, és e mellett mindent megold. Itt tehát nem a perzisztens objektumok cache-elése a feladat, hanem a cache (memória) perzisztens mentése. Talán pont egy ilyen megoldás lenne a leginkább ideális, persze kérdés, hogy mindezt hogy lehetne integrálni a már meglévő rendszerekkel, amennyiben hostolt rendszerben szeretnénk azt használni (pl. Google App Engine). Emellett még nem próbáltam a rendszert, tehát nem tudok nyilatkozni róla, hogy milyen rejtett hátrányai lehetnek egy ilyen megoldásnak.

WordPress

Sikeresen összelőttem a WordPress engine-t. A telepítése meglepően egyszerű volt, a szerverünkön ugyanis biztonsági okokból néhány PHP függvényt letiltottunk, így eddig szinte minden portálrendszernél bele kellett nyúlni egy picit a kódba. Nos, a WP az első kivétel. Letöltöttem, felpakoltam FTP-n, beállítottam az adatbázist, és már ment is. Minimális hozzáértéssel bárki megcsinálja, ami nagy pozitívum a szememben.

A rendszer testreszabása sem okozott túl nagy problémát. Az alap design nem igazán tetszett, így nekiálltam keresni valami témát. Végül is az iNove-ra esett a választás. Bár ez sem tökéletes, talán ez illeszkedik leginkább az igényeimhez. A későbbiekben persze lehet, hogy öcsivel nekiállunk, és készítünk egy új témát, de ehhez még hiányzik a megfelelő késztetés, úgyhogy ez marad.

A következő dolog, amit mindenképp be akartam állítani, az az RSS csatornák követése. Erre FeedBurner-t használok, mint mindenki. Ez kb. a Google Analytics (a FeedBurner is a Google-é) RSS-ekhez való változata. A beállítása elég egyszerű: megadjuk az RSS forrásunk URL-jét, a FeedBurner pedig ad egy másikat. Ezt az új URL-t kell beállítani a hírcsatornákhoz, hogy bárki aki feliratkozik, ezen keresztül tegye. Ehhez van egy FD Feedburner nevű bővítmény. Itt annyi a trükk, hogy a sima kérdőjeles URL-ekkel nem működik, úgyhogy ha használni akarjuk, át kell állítanunk a bejegyzések URL-jét valami szebb formára. Ehhez a szerver oldalon .htaccess és mod_rewrite támogatás kell. Ha ez nincs meg, valamit hackelni kell a bővítményen. Mondjuk WP-ben ez sem túl bonyolult, hiszen beépített szerkesztő van az admin felületen a bővítmények php fájljaihoz.

Amit még nagyon szerettem volna, az egy "Ezt olvastam mostanában" widget a jobb oldalra. Ide akarom gyűjteni az általam érdekesnek gondolt cikkeket/oldalakat. A web ilyen és ehhez hasonló módon való szűrése szerintem egyre fontosabb, hiszen ebben a hatalmas információtengerben egyre nehezebb megtalálni a valódi gyöngyszemeket. Rövid keresgélés után végül a delicious mellett döntöttem. Ez egy nagyon kellemes kis szolgáltatás. Van hozzá egy kellemes kis Firefox plugin, amivel az aktuális oldal egy kattintásra bekerül a megosztott könyvjelzőim közé, amit aztán RSS feed-en keresztül el is érhetek. A WordPress-ben pedig van RSS widget, így néhány kattintással meg is lett a menüpont.

Amit még szerettem volna, az a szokásos social bookmarking csík alulra. Itt a legjobb megoldásnak a Light Social-t találtam. Egy kattintással lehet bookmarkolni az oldalt. Itt szerintem létfontosságú az egyszerűség. Gondolkodtam még a ShareThis szolgáltatáson, de végül elvetettem, mert ott kettőt kell kattintani, és el bírom képzelni, hogy valaki egy kattintásra még hajlandó, de kettőre már lusta. A plugin szerkesztőben láttam, hogy a Light Social kódja milyen egyszerű, úgyhogy gyorsan készítettem még három ikont, egy iwiw-et és egy startlapot (ha már magyar blogról van szó), és egy mail-t.

Ezzel így kb. meg is volt az alapbeállítás. Még egy Google Analytics hiányzik, de ez sem lehet több néhány percnél. Ahhoz képest, hogy saját engine fejlesztését fontolgattam, kb. 1 óra alatt lett egy szinte minden igényemet kielégítő blogom. Nem rossz. Nagy piros pont a WordPress-nek. (No meg annak a jó néhány webes szolgáltatásnak, amiket felhasználtam.)

2010. február 13., szombat

Első

Már jó ideje tervezem, hogy saját blogot indítok, csak valahogy sosem szántam rá magam. Publikus blog szolgáltatásra nem akartam regisztrálni, mert ahogy magamat ismerem, előbb-utóbb frusztrált volna, hogy nem alakíthatom tökéletesen olyanra, amilyenre szeretném. Úgy gondoltam, fejlesztek valami egyszerűbb blog engine-t magamnak, de mindig volt fontosabb dolgom. Végül rászántam magam, és a kettő közötti legoptimálisabb megoldást választottam, feldobtam egy WordPress-t. Így nem kell saját rendszer fejlesztésével vesződnöm, ugyanakkor a pluginek, témák segítségével mégis saját képemre formálhatom. Első bejegyzésnek talán ennyi elég is, remélem megmarad bennem a kellő lelkesedés, és ezt a bejegyzést hamarosan követi a többi.