by Mikael Henriksson
19. September 2009 13:12
NHibernate performance sucked yesterday. Not because NHibernate itself is slow but because I wasn’t using it correctly. In a (by yours truly) newly created system I was not only using NHibernate for minimal performance. I was also using it in an unsafe manner. In case something went to hell there was no easy way for me to roll back the changes. This is particularly bad in a system for let’s say billing. Let’s say we grade/rate/prepare 80,000 customers for being billed. I don’t want this process to complete if there is something wrong with even a single record I want it to hit everyone in the face that –“Hey guys we have no customers to bill, wtf should be done about it?”. At the same time rating 1000 records was taking around 5 minutes.
After some tweaking here and there without much of a difference I decided maybe it’s time to try something crazy like using a unit of work for the entire process. The logical unit of work with minimal effort is of course the transaction. Easy enough I place all the existing code in a transaction, check if a transaction is open in the dao/repository/data context and then only issue a SaveOrUpdate(entity) if this is true. At the end of the processing of all the message I issue a transaction.Commit() and I have set the FlushMode to FlushMode.Commit. I went from 1000 records in 5 minutes to 20,000 records in 30 seconds. After the first 20,000 inserts/saves/updates depending on what the rating does it becomes slower. This does not surprise me a bit since I am not releasing any resources and that has nothing to do with NHibernate. That has to do with bad resource allocation and bad crap management on my end.
This is the short version of what the main code would look like:
using (session.BeginTransaction())
{
try
{
session.FlushMode = FlushMode.Commit;
IEnumerable<Record> records = GetAllRecords(); // load tons of records
foreach (var record in records)
{
// do loads of operations
_recordRepositoryDataContextDAO.SaveOrUpdate(record);
}
session.Transaction.Commit();
}
catch
{
session.Transaction.RollBack();
throw;
}
}
And the method to persist the changes:
if (_session.Transaction == null)
{
using (var transaction = _session.BeginTransaction())
{
_session.SaveOrUpdate(billingRecord);
transaction.Commit();
}
}
else
{
_session.SaveOrUpdate(billingRecord);
}