Saturday, September 29, 2012

Database caching, NHibernate and Redis-powered

How we wish, that with a flick of a switch, this sample query will perform as fast as you needed it be when it’s repeatedly called:


var peopleList =
   (from p in session.Query<Person>()
     .Fetch(x => x.CountryBorned)
    orderby p.CountryBorned.CountryId, p.PersonId 
    select new { PersonName = p.PersonName, BornedAt = p.CountryBorned.CountryName }).ToList();
                    


Fortunately for folks who are using good ORM like NHibernate, it has a mechanism to do so:


var peopleList =
  (from p in session.Query<Person>()
    .Fetch(x => x.CountryBorned)
    .Cacheable() // do my bidding automagically
   orderby p.CountryBorned.CountryId, p.PersonId 
   select new { PersonName = p.PersonName, BornedAt = p.CountryBorned.CountryName }).ToList();



You can do your own manual caching but that would be a little problematic when the user adds, update or delete a record. They would be mildly amused when they can’t see what they have saved, imagine opening an Excel file from a network drive and it gives you the yesterday version of it, instead of the one you have edited earlier this morning just because your network doesn’t employ smarter way to cache things. In this regard, the system leaks an abstraction, your users would no longer think that a network drive is the same thing as the drives (thumbdrives, memory card, etc) they are familiar with. You then need to inform your users all the wonderful things about caching and whathaveyou.


With NHibernate’s caching, your cache won't ever become stale as long as all your data manipulation goes through it, just follow this simple rule, and you are golden. You and NHibernate will be chums for life. With a caching mechanism bolted right to your ORM, stale cache is a thing of the past. That’s the advantage if things are seamlessly integrated, It Just Works.



See the demo code here: https://github.com/MichaelBuen/Demo_NHibernate_Plus_Redis


Here's the benchmark that I've used:
function test() {
 
 var start = new Date().getTime();
 
 var content = $('table > tbody');
 
 var repeat = 200;
 
 var z = 0;
 for (i = 1; i <= repeat; ++i) {
 
  $.ajax({ url: '/Home/People', success: function (data) {
 
   ++z;
 
   if (z == repeat) {
    var elapsed = new Date().getTime() - start;
    $('#hei').text(elapsed);
   }
 
   content.empty();
 
   for (var x in data) {
    var p = data[x];
 
    var newRow = $('<tr/>');
    var tdN = $('<td/>').text(z);
    var tdLastname = $('<td/>').text(p.PersonName);
    var tdFirstname = $('<td/>').text(p.BornedAt);
 
    newRow.append(tdN).append(tdLastname).append(tdFirstname);
 
 
    content.append(newRow);
 
   }
 
  }, async: true
  , error: function (a, b, c) { alert(a); }
  });
 }
 
 
}


It pounds the web server 200 times, then it shows the elapsed time. On my machine, without caching it took 38 seconds, with caching it's just 23 seconds.


On the demo solution, I also included a commandline project where you can persist an object to the database. NHibernate caching can see when you persist an object to the database, and it can cache those objects on-the-fly.


To setup Redis caching with NHibernate, just follow this instruction: http://www.d80.co.uk/post/2011/05/17/NHibernate-Caching-with-Redis.aspx

No comments:

Post a Comment