Oren Eini

CEO of RavenDB

a NoSQL Open Source Document Database

Get in touch with me:

oren@ravendb.net +972 52-548-6969

Posts: 7,608
|
Comments: 51,239
Privacy Policy · Terms
filter by tags archive
time to read 4 min | 768 words

This is a question that I get very frequently, and I always tried to dodged the bullet, but I get it so much that I feel that I have to provide an answer. Obviously, I am (not so) slightly biased toward NHibernate, so while you read it, please keep it in mind.

EF 4.0 has done a lot to handle the issues that were raised with the previous version of EF. Thinks like transparent lazy loading, POCO classes, code only, etc. EF 4.0 is a much nicer than EF 1.0.

The problem is that it is still a very young product, and the changes that were added only touched the surface. I already talked about some of my problems with the POCO model in EF, so I won’t repeat that, or my reservations with the Code Only model. But basically, the major problem that I have with those two is that there seems to be a wall between what experience of the community and what Microsoft is doing. Both of those features shows much of the same issues that we have run into with NHibernate and Fluent NHibernate. Issues that were addressed and resolved, but show up in the EF implementations.

Nevertheless, even ignoring my reservations about those, there are other indications that NHibernate’s maturity makes itself known. I run into that several times while I was writing the guidance for EF Prof, there are things that you simple can’t do with EF, that are a natural part of NHibernate.

I am not going to try to do a point by point list of the differences, but it is interesting to look where we do find major differences between the capabilities of NHibernate and EF 4.0. Most of the time, it is in the ability to fine tune what the framework is actually doing. Usually, this is there to allow you to gain better performance from the system without sacrificing the benefits of using an OR/M in the first place.

Here is a small list:

  • Write batching – NHibernate can be configured to batch all writes to the database so that when you need to write several statements to the database, NHibernate will only make a single round trip, instead of going to the database per each statement.
  • Read batching / multi queries / futures – NHibernate allows to batch several queries into a single round trip to the database, instead of separate roundtrip per each query.
  • Batched collection loads – When you lazy load a collection, NHibernate can find other collections of the same type that weren’t loaded, and load all of them in a single trip to the database. This is a great way to avoid having to deal with SELECT N+1.
  • Collection with lazy=”extra” – Lazy extra means that NHibernate adapts to the operations that you might run on top of your collections. That means that blog.Posts.Count will not force a load of the entire collection, but rather would create a “select count(*) from Posts where BlogId = 1” statement, and that blog.Posts.Contains() will likewise result in a single query rather than paying the price of loading the entire collection to memory.
  • Collection filters and paged collections  - this allows you to define additional filters (including paging!) on top of your entities collections, which means that you can easily page through the blog.Posts collection, and not have to load the entire thing into memory.
  • 2nd level cache – managing the cache is complex, I touched on why this is important before, so I’ll skip if for now.
  • Tweaking – this is something that is critical whenever you need something that is just a bit beyond what the framework provides. With NHibernate, in nearly all the cases, you have an extension point, with EF, you are completely and utterly blocked.
  • Integration & Extensibility – NHibernate has a lot of extension projects, such as NHibernate Search, NHibernate Validator, NHibernate Shards, etc. Such projects not only do not exists for EF, but they cannot be written, for the most part, because EF has no extension points to speak of.

On the other side, however:

  • EF 4.0 has a better Linq provider than the current NHibernate implementation. This is something being actively worked on and the NH 3.0 will fix this gap.
  • EF is from Microsoft.
time to read 3 min | 548 words

I am currently in the process of building support for the production profiling. The technical part (how to reduce the footprint of the profiler when not profiling) is pretty easy, and I spiked it out in a matter of a few hours. The non technical part has been driving me mad for days.

One of the major issues that I have to deal with with regards to production profiling is the issue of security. There are actually multiple levels to this issue. The most obvious one is that we want to secure who can start profiling. Initially I thought that providing a password and limiting IP range that can connect to the profiling endpoint would be enough, but I quickly realized that I am being overly naive.

The idea that someone can just hook into all the communication that goes on with the database is something that will give any security analyst heart palpitations. Not only do I need to secure access to the endpoint, I also need to ensure that no one can sniff this traffic, as it might very well include sensitive information. Beyond that, just to make sure that the aforementioned security analyst doesn’t show up at my door armed & pissed, it is important that only the system administrator (and not the developers) can turn that on.

That one isn’t so much to catch maliciousness, as much as to prevent people from deploying to production with their debug configuration enabled, but will also stop malicious acts of trying to open up holes in the application.

After some investigation, I decided that I probably want to use SSL (and the SslStream implementation in the BCL) to do that. This has several advantages, it means that the network traffic is encrypted and I can ignore that aspect. It also means that I can take advantage on mutual authentication to ensure that the client is really authorized to do connect to me. Finally, I can also require that the certificate will be installed on the server machine, thus meeting the requirement of the system administrator having to take an explicit step to allow it.

From design perspective, it looks nice, so the next question was to write some code to spike it. I should preface everything that follows with the admission that I have only rudimentary knowledge of the actual details of SSL and certificates. But basically, what I thought was to have the following:

  • Server certificate – self signed, server auth, bound to a particular hostname, not CA.
  • Client certificate – self signed, client auth, bound to a particular hostname, not CA.

The client certificate would need to be added as a trusted root on the server. That would allow mutual authentication between the two parties. The server will reject unknown certs or ones that do not bind to the same hostname as the server cert. I am not sure if I should require that the server cert would have to be installed on the client machine, but I am leaning on not requiring that.

Thoughts? Anything else that I might need to think about? Is this scheme really secure? Is it going to cause the ops people to go mad?

time to read 4 min | 784 words

I got a comment in this post suggesting that the code can make use of the EnterUpgradeableReadLock method to simplify this code:

public static string Intern(string str)
{
    string val;
    
    locker.EnterReadLock();
    try
    {
        if(strings.TryGetValue(str, out val))
            return val;
    }
    finally
    {
        locker.ExitReadLock();
    }
    
    locker.EnterWriteLock();
    try
    {
        if(strings.TryGetValue(str, out val))
            return val;
            
        strings.Add(str,str);
        return str;
    }
    finally
    {
        locker.ExitWriteLock();
    }
}

First, let us look at the code that is making use of EnterUpgradeableReadLock:

public static string Intern(string str)
{
    string val;
    
    locker.EnterUpgradeableReadLock();
    try
    {
        if(strings.TryGetValue(str, out val))
            return val;
            
        locker.EnterWriteLock();
        try
        {
            strings.Add(str,str);
        }
        finally
        {
            locker.ExitWriteLock();
        }
        return str;
    }
    finally
    {
        locker.ExitUpgradeableReadLock();
    }
}

And, well, it is somewhat simpler, I’ll admit.

The reason that Upgradable Read was introduce is that in the 2.0 ReaderWriterLock, there was a lot of confusion about how you upgrade from read lock to write lock. Essentially, the upgrade process would give up the lock, allowing other threads to sneak in. The Upgradable Read is an explicit way to handle that, since it doesn’t free the lock when you upgrade.

But there is one huge problem with this code. Only one thread is able to enter upgradable read lock. That means that in code such as this, where this is the only access that we have, we have in effect turned the reader writer lock into a single write lock. Since only one thread can enter upgradable read, it means that we might have well used a standard lock(syncLock) statement.

My original code is slightly more complex, since it has to check the presence of the value twice, but it also have far more parallelism, since multiple threads can read from the strings table at the same time, which is not possible in the Upgradable Read mode.

Upgradable Read is only useful if you have multiple ways of accessing the data, some that are purely read and some (rare) that are upgradable reads. If most / all of your calls go through upgradable read code paths, you are better off using read/write and handling the lock release explicitly.

time to read 4 min | 610 words

Memory problems happen when you application use more memory that you would like. It isn’t necessarily paging or causing OutOfMemory, but it is using enough memory to generate complaints. The most common cases for memory issues are:

  • Memory leaks
  • Garbage spewers
  • In memory nuts
  • Framework bugs

Let me take each of them in turn.

Memory leaks in a managed language are almost always related to dangling references, such as in a cache with no expiration or events where you never unsubscribe. Those are usually nasty to figure out, because tracking down what is holding the memory can be unpleasant. But, by the same token, it is also fairly straightforward to do so.

Garbage spewers are pieces of code that allocate a lot of memory that will have to be freed soon afterward. A common case of that is:

public string Concat(string[] items)
{
   string result = "";
   foreach(var item in items)
      results += item;
 
   return result;
}

This is going to allocate a lot of memory, which will have to be freed soon after. This will get cleaned up eventually, but it will put a lot of pressure on the GC first, will cause the application to consume more memory and in general won’t play nice with others. While the code above is the simplest way to explain this, it is fairly common in ways that are harder to detect, a common case would be to load a DTO from the database, convert that to an entity and convert that to a view model. Along the way, you are going to consume a lot of memory for doing pretty much the same thing.

Now the caveat here is that most objects are actually small, so you don’t really notice that, but if you are working with large objects, or a lot of them, this is something that is going to hit you.

In memory nuts refer to a common problem, you simply put your entire dataset in memory, and commonly refer to it by direct model traversal. When your dataset becomes too big, however… well, that is the point where the pain is really going to hit you. Usually, fixing this is a costly process, because your code assumes that the entire thing is in memory. Even if you can easily save it to persistent storage, fixing all the places where the code assumes that everything is just a pointer reference away is a big problem.

Framework bugs are my least favorite, it is when you run into cases where the framework just won’t release memory. Most often, this is because you are doing something wrong, but occasionally you will hit the real framework bug, and tracking that down is a pure PITA.

In all cases, you need to set up some goals, what is acceptable memory usage, in what scenarios, over what time frame, etc. Then build test scenarios that are repeatable and try each of your improvements out. Do not try to implement too much upfront, that way lies the road to madness.

time to read 5 min | 838 words

There are some parts of our codebase that are simply going to have to be called a large number of times. Those are the ones that we want to optimize, but at the same time, unless they are ridiculously inefficient, there isn’t that much room for improvement.

Let us look at this for a second:

image_thumb

The numbers are pretty hard to read in this manner, so I generally translate it to the following table:

Method name Cost per 1,000 invocations
StringEqualsToBuffer 7 ms
get_Item 0.2 ms
get_Length 0.2 ms
GetHashCode 4 ms
Equals 1 ms

It is important to note that what I am trying to check here is relative cost of calling a method. I use the thousands invocation just to give us back a number that we can actually understand easily, instead of dealing with nanoseconds.

As you can see, all of the methods in this piece of code are actually pretty fast, the slowest will complete in under ten nanoseconds. The problem is that they are called a lot. StringEqualsToBuffer cost me 90 seconds in this test run. This means that to improve its performance, we need to get it to drop to even fewer nanoseconds, or reduce the number of times it is called. Both of which are going to be hard.

You can look at how I dealt with this particular case in this post, but right now I want to talk about the decision process, not just the action that I took.

Usually, in such situations, I find the most costly function (StringEqualsToBuffer in this case) and then find any functions that it called, in this case, we can see that get_Item and get_Length are both costly functions called from StringEqualsToBuffer. Stupid micro optimization tactics, like referencing a field directly instead of through a property have enormous consequences in this type of scenario.

Next, we have things like GetHashCode, which looks to be very slow (it takes 4 nanoseconds to complete, I have hard time calling it slow :-)). This function is slow not because we are doing something that can be optimized, but simply because of what it does. Since we can’t optimize the code itself, we want to do the next best thing, and see if we can optimize the number of times that this code is called. In other words, apply caching to the issue. Applying caching means that we need to handle invalidation, so we need to consider whatever we will gain something from that, mind you. Often, the cost of managing the cache can be higher than the cost of calculating the value from scratch when we are talking about this kind of latencies.

Another issue to consider is the common memory vs. time argument, it is easy to err into one side of them when you are focused on micro benchmarks. You get a routine that completes in 1 nanosecond in the common case but uses up 10 Mb of cache. Sometimes you want that, sometimes it is a very bad tradeoff.

I generally start with simple performance tuning, finding out hotspots and figuring out how to fix them. Usually, it is some sort of big O problem, either in the function itself or what it is called on. Those tend to be easy to fix and produce a lot of benefit. Afterward, you get to true algorithmic fixes (find a better algo for this problem). Next, I run tests for memory usage, seeing if under the most extreme likely conditions, I am hitting my specified limits.

I’ll talk about reducing memory usage in a separate post, but once you run through that, another run to verified that you haven’t traded off in the other direction (reduced memory at the expense of running time) would complete the process.

FUTURE POSTS

  1. Cryptographic documents in RavenDB - 3 days from now
  2. Recording: How To Run AI Agents Natively In Your Database - 6 days from now

There are posts all the way to Sep 29, 2025

RECENT SERIES

  1. Recording (18):
    22 Sep 2025 - How To Create Powerful and Secure AI Agents with RavenDB
  2. Webinar (8):
    16 Sep 2025 - Building AI Agents in RavenDB
  3. RavenDB 7.1 (7):
    11 Jul 2025 - The Gen AI release
  4. Production postmorterm (2):
    11 Jun 2025 - The rookie server's untimely promotion
  5. RavenDB News (2):
    02 May 2025 - May 2025
View all series

Syndication

Main feed ... ...
Comments feed   ... ...
}