We have a bunch of smart people whose job description does not include breaking RavenDB, but nevertheless they manage to do so on a regular basis. This is usually done while subjecting RavenDB to new and interesting ways to distress it. The latest issue was being able to crash the system with an impossible bug. That was interesting to go through, so it is worth a post.
Take a look at the following code:
This code will your application (you can discard multi threading as a source, by the way) in a particularly surprising way.
Let’s assume that we have a system that is running under low memory conditions. The following sequence of events is going to occur:
- BufferedChannel is allocated.
- The BufferedChannel constructor is being run.
- The attempt to allocate the _destinations list fails.
- An exception is thrown upward and handled.
So far, so good, right? Except that now your system is living on borrowed time.
You see, this class has a finalizer. And the fact that the constructor wasn’t called doesn’t matter to the finalizer. It will still call the Finalize method, but that method calls to Dispose, and Dispose expect to get a valid object. At this point, we are going to get a Null Reference Exception from the finalizer, which is considered to be a critical error and fail the entire process.
For additional fun, this kind of failure will happen only in under harsh conditions. When the OS refuses allocations, which doesn’t happen very often. We have been running memory starvation routines for a while, and it takes a specific set of failures to get it failing in just the right manner to cause this.