Skip to main content

Čas dát NHibernatu sbohem #1 - nízká transparentnost

Mám rád frameworky. Použít sílu, inteligenci a práci ostatních, kterou nabízí zdarma, mi přijde jako dobrý nápad, který šetří lidské zdroje a člověka učí. Proto také v našem .NET produktu HP Service Virtualization používáme NHibernate, Spring.NET, NUnit atd.

Ostatně princip ORM je dobrý nápad. Team, starající se o persistenci, rychle nabootuje do problematiky persistence, nevynalézá kolo s různými koncepty jako je optimistic locking, session handling, podporuje relativní přenositelnost mezi databázemi a persistentní úroveň napíšete za zlomek času. V následujicí sérii článků bych rád popsal pár postřehů, proč se skoro po třech letech chceme NHibernatu na našem produktu zbavit. Pořadí problému se nemusí nutně shodovat s jejich závažností :-)

Poslední dobou slýchávám názor, že jsou věci složité jinak by je dělal každý. Tohle tvrzení je skoro v rozporu s dobrými programátorskými mravy :-) Asi před rokem jsem četl knížku Tajemství inovací Steva Jobse. Ač nejsem milovník Apple, kniha ve mě zásadně vylepšila náhled na složitost naprogramovaného kódu. Od té doby se snažím dbát na jednoduchost všeho co dělám.

NHibernate je vysoce konfigurovatelný, umí mnoho věcí nastavit, často velmi umě schovává hodně složitou funkcionalitu za jednoduché koncepty. Přesto to pro složitější aplikace nemusí stačit.

Jeden příklad složitosti za všechny. Hibernate Session. Výborný koncept, který umí pomocí optimistického přístupu cachováním šetřit přístup do DB. Zaručuje dokonce prakticky vyšší izolační level než standardní Read Committed - pokud jednou načtete entitu, dostanete během životnosti session vždy stejnou instanci - samozřejmě s určitými vyjímkami. Což jsou přesně ty složitosti o kterých mluvím.

Problém je, že tento koncept musíte vystavit mimo databázovou vrstvu, např. s použitím Transaction API ve Springu v komponentách, které řeší transakce. Jakmile tohle uděláte, nemáte věci pod kontrolou. Potom pracuje se session, alespoň na pozadí, váš celý tým. Všichni členové pak musí umět správně se session fungovat, vědět kdy co dostanou, kdy mají co updatovat, kdy refreshovat. Handlování práce se session začne prorůstat mimo databázi. Mužete ho sice skrýt za repository, ale už je vidět.

Pokud je počet uživatelů podobného API větší, vždycky se najde pár jedinců, kteří jej používají špatně. Vše se později zvrtne k tomu, že místo šetřeni dotazů do databáze se queruje vždycky.

V kombinaci s magičností sklouzneme zákonitě k tomu, že bychom lépe spali, kdybychom si session komponentu psali sami, vynutili si jasný lifecycle manažovaných entit a měli vše pod kontrolou, protože celý zbytek teamu nerozumí detailně session logice a nemá čas číst stovku stránek NHibernate manuálu.

Proč magičností? Springové transakční API handluje lifecycle session. Rádi bychom během celého processení jednoho callu na náš server měli session jako unit of work. Springové transakční API se bohužel nechová tak, že bychom mimo něj poznali, kdy vytvárí novou session a kdy ji znovupoužívá - což je chování, kterého chceme dosahnout. Tímto přesně zajistíme konzistenčnost během celého zpracování.

Další magičnost? Např. v Second Level Cache není občas jasné proč je držena starší entita než očekáváme a několik dnů strávených debugováním NHibernate knihoven nenese ovoce. Musíte potom obcházet celý koncept, tu a tam session či cache clearovat nebo naopak flushovat, aby se v ní objevilo to správné. Začíná se to pak celé nebezpečně hákovat.

NHibernate je mocný nástroj, ale jako vždycky věci normalizuje. Zjednodušení konceptu má vždy velkou nevýhodu a to skrytí určitých věcí o kterých rozhoduje sám framework za vás. Pokud je ale chcete měnit, musíte si přepsat tu a tam nějakou komponentu. Prostě vám občas nesedí defaultní chování.

Pokud tohle chcete, raděj se HNibernatu vyhněte.

Co bude příště? Performance. Kdy a proč se vyhnout NHibernate, když chcete vysokou performance.

Comments

Popular posts from this blog

ETCD: POST vs. PUT understanding

ETCD is distributed key value store used as a core component in CoreOS . I've already send a post earlier this week. Here is a page describing how to use ETCD basic commands = ETCD API. Code snippets placed in a page mostly use put , but ETCD allows to use post as well.  Most of us understand differences between those two commands in a notion of a REST(ful) service, but how does it work in key value store? POST Example over many words. curl -v http://127.0.0.1:2379/v2/keys/test -XPOST -D value="some value" curl -v http://127.0.0.1:2379/v2/keys/test -XPOST -D value="some value" Two same command result into following content: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 { "action" : "get" , "node" : { "key" : "/test" , "dir" : true , "nodes" : [ { "key" : "/test/194" , "value" : &

NHibernate performance issues #3: slow inserts (stateless session)

The whole series of NHibernate performance issues isn't about simple use-cases. If you develop small app, such as simple website, you don't need to care about performance. But if you design and develop huge application and once you have decided to use NHibernate you'll solve various sort of issue. For today the use-case is obvious: how to insert many entities into the database as fast as possible? Why I'm taking about previous stuff? The are a lot of articles how the original NHibernate's purpose isn't to support batch operations , like inserts. Once you have decided to NHibernate, you have to solve this issue. Slow insertion The basic way how to insert mapped entity into database is: SessionFactory.GetCurrentSession().Save(object); But what happen when I try to insert many entities? Lets say, I want to persist 1000 libraries each library has 100 books = 100k of books each book has 5 rentals - there are 500k of rentals  It's really slow! The inser