Skip to main content

Čas dát NHibernatu sbohem #2 - performance


V minulém díle jsem jsem rozebíral pár nedostatků NHibernatu, které mají z mého pohledu zásadní vliv k nasazení či nenasazení toho výborného frameworku. V tomhle díle bych rád rozebral performance.

Performance lze různě ladit, jak jsem již před pár lety rozbíral. Je spousta možností, jak věci delat. Cachovani, stateless session atd. Ve chvíli, kdy se dostane člověk na hranu rychlosti databáze, musí se začít zaobírat méně obvyklými věcmi, které se běžně neladí.

Kontrukce entit

Vždy jsme na našel projektu ctili DDD, čili chtěli jsme mít rich entity a ne naked. Tím chci říct, že všechny atributy/fieldy jsou encapsulované get/set metodami. Pokud nechcete zveřejnit privátní fieldy, musíte začít používat reflection, což při velkém měřítku při používání pro většinu mapovaných entit začíná být pro .NET problém. Je to pomalé.

Existuje několik frameworků (např. FastReflect), které dokážou cachovat reflection přístup, čistou performance přístupu k privátním fieldům to pak zrychlí v našem případě 20x. Samozřejmě custom reflection framework není snadné do NHibernatu nejak integrovat. Je nutné si vyvinout vlastní komponentu, která bude konstruovat entity a tu tam zaintegrovat.

Konstrukce hierarchií entit

Popsaný problém pomalé reflexe plynule přechází v další issue, které člověk musí řešit. Většinou z persistence netaháte jeden typ class, ale hierachii tříd. To znamená, že se persistentního frameworku dotazuji na jeden objekt, který má však další dependance. Typicky v DDD se jedná o agregát, který je separovaný od ostatních agregátů - nemají mezi sebou přímou relaci - a ten obsahuje kolekce dalších druhů entit a tak dál rekurzivně. V našem případě jeden typ agregátu nabobtnal až na dvacet druhů class.

Z logiky věci NHibernate konstruuje celý agregát pomocí mappingu, tzn. map klíčů, entit a typů class. Jinak řečeno, materializované entity bude NHibernate ukládat do session a pro dané klíče a typy je linkovat mezi sebou do výsledného agregátu, až z toho nakonec vypadne pouze root instance. Ve chvíli, kdy těch typů a instancí není málo, snadno narazíme na to, že konstruování takového agregátu je dost pomalé.

Nhibernate je obecný framework, který se snaží za runtime zkonstruovat něco, co vy si můžete napsat sami. Performance programového přístupu versus vašeho zakompilovaného je samozřejmě velká v řádu desítek procent ušetřeného času.

Jediné, co musí programátor udělat, je napsat si vlastní linker a vlastní session, která je schopná absorvovat entity a propojovat je přes identifikátory.

Mapping

Přestože se to nemusí zdát, i samotné mapování může mít dopad na výkon Vaší aplikace. Ačkoliv je NHibernate jeden z nejvíc ohebných frameworků, co jsem poznal, mapping neumí zdaleka všechno. Pokud máte složitejší věci, např. kolekce kolekcí nebo dictinary kolekcí, musíte v mapování přijít s workaroundem, protože tohle standardně mapovat nelze. V našem případě jsem vždycky pro daný záznam vytvořili novou (zbytečnou) entitu, která tohle dokázala obalit a tu také namapovali.

Místo přirozeného řešení jsem kvůli jemnému omezení museli vytvořit entitu navíc. Samozřejmě, že takový zásah má vliv na performance, např. vytváření entit s následným garbage-collectingem atd. V serverových aplikacích je občas nutné hledět na každou zbytečnou entitu.

Garbage Collecting

Poslední téma, kterého bych se rád dotknul, je produkce entit a následný garbage collecting. Škálovatelná server-side aplikace by neměla plýtvat zdroji. Pokud to dělá, brzy v rámci optimalizací performance narazí. Tahle metrika je vždy strašně těžko měřitelná, protože každý profiler nebo typ měření dává jiné výsledky.

Jedna věc je však jistá. Jakékoliv ORM bude produkovat víc odpadu, protože bude vždy víc obecné - to znamená, že bude produkovat obalovací entity - než Vaše custom persistenční logika, která je prostě na míru. Bude produkovat víc obecný kód - jak jste to již popisoval v předchozí kapitole.

Při nasazení NHibernatu do produkce zvedne vždycky čas v GC.

Rozhřešení

Po několika letech používání NHibernatu jsme dospěli do situace, kdy jsme si pro zrychlení persistentní vrstvy museli přepsat určité komponenty nebo vyměnit NHibernate celý. Začali jsme s tím prvním. Jak jsme skončili uvidíme v další kapitole.
Post a Comment

Popular posts from this blog

Http and TCP Load Balancing with Kubernetes

Kubernetes allows to manage large clusters which are composed of docker containers. And where is large computation power there is large amount of data throughput. However, there is still a need to spread the data throughput so it can reach and utilize particular docker container(s). This is called load balancingKubernetes supports two basic forms of load balancing. Internal among docker containers themselves and external for clients which call you application from outside environment - just users.


Internal Load Balancing with Kubernetes Usual approach during the modeling of an application in kubernetes is to provide domain models for pods, replications controllers and services. Look at the great documentation if you are not aware of all these principles.

For simplification, pod is a set of docker containers which are always located on the same node. Replication controller allows and guarantees scalability. The last but not least is the service which hides all instances of pods - cre…

Validating nginx config file using docker approach

I try to setup nginx as a load balancer. The configuration is just a file with a lot of constrains so I need a validation. There is no online validation service, as e.g. CoreOS has, and I don't want to install nginx on my laptop as I work on a distributed app.

Docker is right approach for me. Let say I have following config:


In short, I'm going to pass nginx config to running nginx instance and look to the logs.

Put you nginx.config to the temp and start the docker image:

sudo docker run --name nginx -v /tmp/nginx.config:/etc/nginx/nginx.conf:ro -d nginx It uses volume mapping so the command just starts a new docker container and mounts a local /tmp/nginx.config to the given in-container path. You can obviously change the volume path to your personal path. Is it working or not? Look at logs.

sudo docker logs nginx If there is no entry, your file is fine. In the case of an error, you can see something like this:

2016/01/08 11:37:31 [emerg] 1#1: unexpected "}" in /etc/…