Egy ilyen tipikus forgatókönyv, amikor egy alkalmazásszerver áll éles üzembe és szolgálja ki a kezdeti igényeket, majd a növekvő adatmennyiségnek, és terhelésnek köszönhetően fokozatosan csökken az elvárt szolgáltatási minőség, egy idő után már a kritikus szint alá. Főbb tünetek ilyenkor a lassúság, és a soha nem elegendő memória. Ekkor persze számos oldalról lehet neki fogni a teljesítmény növelésének, de a cél minden esetben a szűk keresztmetszet(ek) megtalálása, ahol ha javítunk, módosítunk, hardver esetében bővítünk, akkor újra erőre kap a rendszer.
Egy-egy ilyen tűzoltás jellegű művelet nagyon összetett és sokszor időigényes lehet. Mit lehet tenni, és mit teszünk ilyenkor?
- Futás idejű Java kód vizsgálatát profiling eszközök segítségével, melynek eredménye komplett részek újraírását is magában foglalhatja. Itt az indokolatlannak tűnő memóriafogyás és a hosszú lefutású műveletek okait vizsgáljuk.
- A perzisztálást végző adatbázisok tuningolását. Ez egyszerűbb esetben néhány jól megválasztott index, hint, rosszabb esetben az adatbázis struktúra komolyabb módosítása.
- A hardver teljesítményének, kapacitásának növelését. Ha a kapacitást úgy növeljük, hogy újabb szervert állítunk üzembe, akkor újabb lassító/megoldandó problémák merülnek fel. Ilyen például webes alkalmazások esetében a session replikáció megoldása.
- ...
Ráadásul ezeknek a feladatoknak sokszor a megtérülési aránya sem determinálható előre. Ezért általában nem örül ennek a garanciális tevékenységnek a beszállító, de még az ügyfél sem. „Jobb” esetben ugyanis csak a belső munkát hátráltatja, de ha a cég ügyfelei is tapasztalják kellemetlen hatásait, akkor az már komolyabb bevételkiesést eredményezhet, az elégedettségi mutatók csökkenésén kívül. Nem véletlen, hogy az effajta tevékenységre számos tanácsadó cég szakosodott, mert többnyire idő alapon történik a beavatkozás, és a sokszor rosszul méretezett, tervezett rendszereknek hála; meglehetősen nagy piaca is van.
Most induljunk ki abból, hogy ideálisak a körülmények a bedőlésre hajló rendszerünkben, vagyis
- a kód nem tartalmaz (vagy csak kevés) architektúrális, és algoritmikus hibát.
- Az átadás-átvétel kritériuma volt egy végrehajtott teljesítménytesztelés, amely figyelembe vette a nem funkcionális követelményként rögzített jövőbeni igényeket is (számosság, várható kliensek száma, stb.)
- Az adatbázis tervezése relációs szempontból megfelelő.
A premisszák ismeretében persze felvetődhet a kérdés, hogy miért van itt szükség további optimalizálásra. Fókuszáljunk arra, hogy a rendszer alapvetően jól volt megtervezve, implementálva, vagy a beavatkozásnak köszönhetően már elértük a lehető legoptimálisabb állapotot. Ha ez így van, akkor is elérhetünk egy olyan határt, amit már nem triviális kezelni, és megkérdőjelezheti a rendszer jövőállóságát.
Most lássuk a leggyakoribbat, a már korábban is említett problémák közül részletesebben:
- Memória (Heap) elfogyasztása
Ennek sajnos sokszor gyakorlati és elvi határai is vannak. Mint tudjuk ez 32 bites környezetben is maximum csak 3,5Gb lehet. De hiába az adott esetben költséges 64 bites komponensekre való átállás, a memóriát is karban kell tartani. Minél nagyobb az allokált memória a garbage collectornak annál több munkába telik takarítani a szemetet. Természetesen erre is vannak nagyteljesítményű eszközök, ilyen például az Oracle JRockit JVM, amely a karbantartásra fordított CPU-idő mennyiségét garantáltan minimális szinten tartja. Ráadásul ez akár mindössze néhány ms(!).
- A perzisztencia rétegben bekövetkező lassulás
A CRUD- (Create, Retrieve, Update, Delete) műveletekben tapasztalható lassulás legtöbbször az adatbázisnak, mint szűk keresztmetszetnek köszönhető. Az alkalmazásnak ebben a rétegében is számos finomhangolást, teljesítménynövelő lépést végezhetünk. Fontos megjegyezni, hogy itt valójában nem a middle-tearben használt perzisztenciakezelő keretrendszer lassú, hanem maga az adatbázison végrehajtott műveletek. De mégis az alkalmazás szemszögéből ez úgy érezhető, mint ha az ORM- (Object-Relational Mapping) művelet lenne lassú. Sajnos egy adott ponton túl hiába a hardver teljesítményének növelése, az optimalizálás. Az egymást érő, gyakorlatilag már sorban álló üzleti igények (alkalmazások DML-utasításai, ki és betöltést végző interfészek, riportok futtatása, stb.) kiszolgálásához alig-alig elegendő az egyetlen, izmos adatbázis szerverünk. Ezen próbál segíteni az alkalmazásban lévő perzisztenciakezelő keretrendszerekben megvalósított cache bekapcsolása, vagy egyedi gyorsító tárak implementálása. Emiatt viszont hamarabb szembesülünk az előbb említett memóriafogyással. Felmerül még ilyenkor az adatbázisszerver replikálása is. A legtöbb esetben ez nem alkalmas megoldás, bár rögtön megoldott a magas rendelkezésre állás is, de csak az adatbázis szintjén.
Akárhogy is nézzük a fent említett gyakorlatot, egyrészről nem praktikus ezeket az adott esetben komoly módosítási igénnyel bíró feladatokat az üzemelő, éles rendszeren kísérletezgetve megoldani. Ha mégis, akkor a teljesítménynövelés érdekében célzottan, a megfelelő eszköz megválasztásával, minél kisebb változással (=kockázattal) kell megtenni a szükséges beavatkozást. De még jobb, ha a többletszolgáltatást nyújtó technológiai komponenseinket már a rendszer tervezésekor, implementálásakor felhasználjuk.
Egy hatékony megoldás
Az Oracle Coherence egy olyan in-memory (csak memóriában jelen levő) adat-grid, ami napjaink webes alkalmazásainál egyszerre old meg több feladatot, és problémát. Ha le kellene egyszerűsíteni, azt lehetne mondani, hogy egy profin megírt cache. Használhatjuk a perzisztencia keretrendszer L2 cache elemeként, saját cache-ként az egyszerű HashMap helyett. A benne lévő Java objektumainkat (POJO, EJB) indexelhetjük, tranzakcionálisan, szál-biztosan kezelhetjük. A bean tulajdonságok alapján szűrhetünk, és módosíthatjuk is azokat úgy, hogy nem kell végigmennünk egyesével az elemeken.
Ez önmagában persze még nem elegendő a vizsgált probléma megoldásához, de már rengeteg plusz funkcionalitás egy egyszerű cache-hez képest.

Adat-grid
Az igazi plusz az, hogy az egyes szervereken (JVM-ekben) futó Coherence szerver (adatot tároló) elemek teljesen önállóan felderítik egymást, és egy nagy cache-ként érhetőek el a Coherence kliensek számára. Ezzel ráadásul rengeteg olyan feladat kerül megoldásra, ami egyszerre biztosítja a teljesítményt és a nagy megbízhatóságot:
Kiemelkedő a rendelkezésre állás
Mivel egymás között (a konfigurációnak megfelelő) replikációt végeznek, nincs adatvesztés. Amint az egyik node kiesik, öngyógyító módon egy másik veszi át szerepét. Az éppen elvesztett adatok, valójában másolatok, egy másik kiválasztott (megegyezett) node-ra kerülnek.
Bárhonnan csatlakozunk a grid-hez minden(!) adatot ugyanolyan (rendkívül kicsi) késleltetéssel érünk el. Ennek köszönhetően a middle-tear rétegben is biztosított az adatok megosztása az egyes alkalmazásszerverek között.
Kapacitása gyakorlatilag korlátlan. Minden egyes node bekapcsolásával újabb Gb nagyságrendű kapacitás áll rendelkezésünkre. Tehát nem probléma a heap mérete.
- Tetszőlegesen indexelhető bean tulajdonságok
Gyorsítják a keresést.
- ORM-feladat minimalizálása
Ha már beolvastuk (read-through), vagy elmentettük (write-through, write-behind) a cache-be, nincs ok arra, hogy kikerüljön. Így csökken a cache szinkronizációval járó többlet ráfordítás is. hiszen bármelyik alkalmazásszerver akarja elérni a kérdéses adatot, ugyanazt a cache-t látja, használja, függetlenül, attól hogy a kért adat valójában az ő fizikai szerverén, vagy éppen JVM-jében található vagy sem.
Maga az implementáció Java, de van hozzá C++, .NET-kliens is.
- Eseményfigyelők elhelyezése
Gyakorlatilag az adatbázisok trigger mechanizmusának megfelelő eseménykezelések.
- Valós párhuzamos lekérdezés, feldolgozás
Szűrés a cache-ben tárolt adatokra, valamint a tárolt elemek feldolgozása anélkül, hogy végig kellene iterálni egyesével az összes objektumon. Minden egyes résztvevő node elosztottan hajtja végre a dedikált feladatot.
A kérdéses cache-ben tárolt, bekerülő adatok folyamatos lekérdezése valós időben.
Ha jobban belegondolunk a képességek ismeretében, egy olyan sokoldalú eszközről van szó, amely egyszerre több technológiai rést hivatott befoltozni. Számos gyakori problémára, kihívásra ad zseniális megoldást. A Coherence alkalmazásának elsajátítása után pedig csak a fantáziánk szab határt, hogy hol mire vethető be a teljesítmény és a megbízhatóság érdekében.
Megoldás dobozból
A kész megoldások iránti igényt maguk a fejlesztők is hamar felismerték, és elkészültek az ehhez szükséges implementációk, kiegészítések.
- CoherenceWeb A webes alkalmazások állandó visszatérő problémája. A sokszor nagy méretűre dagadt session információ gyors elérésű tárolása és hatékony replikációja. Számos alkalmazásszerverhez érhető el ez kiegészítés, ami néhány lépés után már üzembe is állítható.
- Perzisztenciakezelő keretrendszerekkel történő integráció.
-
-
-
-
- Hibernate és Toplink L2 cache.
- Coherence alatt Hibernate és Toplink. Automatikus cache tartalom perzisztálás az adatbázisba.
Hardver követelmények
Lévén in-memory alkalmazás, a tényleges követelmény a gyors memória, CPU-, és hálózati kapcsolat. A hálózati csatolás tervezésekor kifejezetten a Gigabit ajánlott, míg a CPU és memória tekintetében bármilyen átlagos képességű, belépő szintű szerver paraméterei már elegendőek. Mint a működéséből is látható, itt nem szűk keresztmetszet a lassú háttértár.

Mikor és hol
A Coherence ugyan alapvetően a teljesítmény és kapacitás növelését célozza meg, de ez korán sem jelenti azt, hogy vésztartalékként kellene kezelnünk. Ellenkezőleg, egy jól átgondolt tervezés során már az implementálásra váró rendszer, az architektúra része lehet. Használni olyan egyszerű, mint egy HashMap-et. Gyorsan telepíthető, konfigurálható.
Mára számos referenciával rendelkezik, ahol bizonyította képességeit. Ezek között ismert például olyan felhasználás is, ahol a cache adatainak mentése az adatbázisba naponta egyszer, adott időpontban történik meg. Tehát olyan megbízható objektum alapú tárolást biztosít közel az alkalmazás réteghez, hogy nincs is szükség az adatbázis folyamatos aktualizálására.
Nagy Péter, Middleware műszaki tanácsadó, Oracle Hungary