Az előző post folytatásaként ismét néhány jellegzetes hibatípust gyűjtöttem össze amelyekkel jómagam foglalkoztam a stabilizációs projekt kapcsán. Hát igen, a DynaTrace ezen hibák megtalálásában is elég hasznosnak bizonyult, nézzük is meg hogy hogyan segített.
Hiányzó TimeOut-ok
Sok helyen találkoztam azzal, hogy a WebService, EJB, URLConnection vagy DB lekérdezéseknél nem voltak timeout-ok definiálva. A timeout-ok hiánya néha komoly stabilitási problémákhoz is vezetett, pl. többször is előfordult, hogy egy belassult web-szolgáltatás miatt a kérések feltorlódtak így a válaszidők is megnövekedtek. Ha már a timeout-okat állítgatjuk, a programozott (java kódbeli) megadás helyett érdemes inkább a konfigurációs paraméterrel való megadást választani, így akár futásidőben is módosíthatjuk az értékét.
Ahogy az alábbi ábra is mutatja, a timeout jellegű hibákat a DynaTrace segítségével elég egyszerűen be lehet azonosítani!
Prepared Statement problémák
Szintén gyakori eset, hogy az SQL lekérdezések paraméterei string konkatenációval voltak beillesztve a PreparedStatement-nél preferált paraméter binding helyett. Ezzel két probléma is van, egyrészt SQL injection-ra adhat lehetőséget másrészt a statement cache-t nem tudjuk majd jól kihasználni.
A lekérdezések végrehajtása alapvetően 2 fázisra osztható az előkészítésre és a paraméterek behelyettesítésével történő végrehajtásra. Az előkészítés egy cpu igényes művelet, ami minden statement legelső végrehajtásakor lefut. Ez a fázis a parszolás, a szintaktikai ellenőrzés és fordítás valalmint a végrehajtási terv elkészítésének a lépéseit tartalmazza. Az előkészített statement, a db connection-onként nyilvántartott statement cache-ben eltárolódik és amíg ebben a cache-ben megtalálható, addig a következő végrehajtások során csak a paramétereket kell behelyettesíteni, az erőforrás igényes előkészítést már nem kell újból végrehajtani.
Nézzünk erre egy példát! Vegyük a "SELECT ID id, DESCRIPTION desc FROM mytable WHERE BL=? ORDER BY desc" lekérdezést, ahol is helyesen a WHERE feltételnél található paraméter nincs beégetve, így ebben a formában a lekérdezés a statement cache-ben csak 1 helyet foglal, függetlenül attól hogy milyen paramétert használunk a lekérdezés során. Az alábbi DynaTrace-el beazonosított SQL lekérdezés pedig a kerülendő változat, miszerint ugyanaz a lekérdezés 8 helyet foglal el a statement cache-ben mivel a paraméter be lett égetve a lekérdezésbe.
Szintén gyakori eset, hogy az SQL lekérdezések paraméterei string konkatenációval voltak beillesztve a PreparedStatement-nél preferált paraméter binding helyett. Ezzel két probléma is van, egyrészt SQL injection-ra adhat lehetőséget másrészt a statement cache-t nem tudjuk majd jól kihasználni.
A lekérdezések végrehajtása alapvetően 2 fázisra osztható az előkészítésre és a paraméterek behelyettesítésével történő végrehajtásra. Az előkészítés egy cpu igényes művelet, ami minden statement legelső végrehajtásakor lefut. Ez a fázis a parszolás, a szintaktikai ellenőrzés és fordítás valalmint a végrehajtási terv elkészítésének a lépéseit tartalmazza. Az előkészített statement, a db connection-onként nyilvántartott statement cache-ben eltárolódik és amíg ebben a cache-ben megtalálható, addig a következő végrehajtások során csak a paramétereket kell behelyettesíteni, az erőforrás igényes előkészítést már nem kell újból végrehajtani.
Nézzünk erre egy példát! Vegyük a "SELECT ID id, DESCRIPTION desc FROM mytable WHERE BL=? ORDER BY desc" lekérdezést, ahol is helyesen a WHERE feltételnél található paraméter nincs beégetve, így ebben a formában a lekérdezés a statement cache-ben csak 1 helyet foglal, függetlenül attól hogy milyen paramétert használunk a lekérdezés során. Az alábbi DynaTrace-el beazonosított SQL lekérdezés pedig a kerülendő változat, miszerint ugyanaz a lekérdezés 8 helyet foglal el a statement cache-ben mivel a paraméter be lett égetve a lekérdezésbe.
Érdemes tudni, hogy a WebSphere alkalmazás szerver alatt a prepared statement cache size alapértelmezett értéke 10, JBoss alatt pedig ha nincs definiálva akkor 0, így ennek az értékét még akkor is érdemes megnövelni ha egyébként paraméterezett sql-eket használtunk!
A statement cache pontos bekonfigurálásához érdemes monitorozást végezni a WebSphere admin console integrált IBM TivoliPerformance Viewer vagy a DynaTrace segítségével a Prepared Statement Cache Discard Count PM-re feliratkozva. Sőt a DynaTrace ezen felül lehetőséget biztosít a parszolás nélkül végrehajtott lekérdezések arányának a monitorozásához is az Executions without parse ratio metrika segítségével.
Ne maradj le a folytatásról sem!
Java implementációs hibák sokasága
A konfigurációs problémák mellett rengeteg java implementációs hiba is napvilágra került, éppen csak néhány példát megemlítve:
- Néhány lapozó komponens lekérte az összes rekordot, nemcsak az adott oldalon megjelenítendőket.
- Explicit garbage collector hívások a Java kódban.
- Túl gyakori és felesleges Runtime.exec() hívások.
- Le nem kezelt (és néha a felületre is kijutott) kivételek.
- Memória szivárgások. (Memory Leak)
- Custom megoldások implementációs hibái: Saját DB Connection Pool szinkronizációs és logikai hibák.
Ne maradj le a folytatásról sem!
Az explicit gc és a Runtime.exec() bizony elég meredek dolgok. A saját DB connection pool pedig inkább arról árulkodik, hogy a készítők azt hitték, hogy bármiből jobbat tudnak írni. (és ez nem meglepő módon nem sikerült) Miért nem egy létezőt egészítettek ki a saját igényeiknek megfelelően? (bár ez csak költői kérdés)
VálaszTörlésÍgy leírva persze triviálisnak tűnik, de banki környezet révén a kódbázis hatalmas és egy része bizony elég régi is, továbbá a külső beszállítók is elég sokmindent behoztak ide komolyabb ellenőrzések nélkül. Szóval én nem csodálkoztam, hogy ilyenek is becsúsztak.
TörlésA custom db connection pool szintén egy legacy dolog volt amihez senki nem akart hozzányúlni, amíg a lehalások egy részét a DynaTrace segítségével rá nem bizonyítottuk...