The benefits of a plugins based system: RavenDB

time to read 4 min | 689 words

I just fixed an interesting bug with RavenDB. Basically, a user was complaining that if they went ahead and (effectively randomly) deleted files from the RavenDB directory, RavenDB would break. The bug is that it used to work previously and now it doesn’t work anymore. The actual bug is a bit more complex than that, but I think that the description I just gave is a good indication of how I think of this bug.

Nevertheless, this actually demonstrate a problem with RavenDB. RavenDB stores index information in 3 different locations:

  • Index statistics are tracked inside the storage (the RavenDB data file)
  • Index definition are stored in the /IndexDefinitions directory, as plain JSON files.
  • The index data itself is tracked inside the /Indexes directory

Granted, the only way that those locations can go out of sync is if the user is actively trying to corrupt RavenDB by messing with the internal stuff directly, but still…

I decided that I wanted to add explicit support for detecting and recovering from this scenario, so I wrote the following:

namespace Raven.Database.Plugins.Builtins
{
    public class DeleteRemovedIndexes : IStartupTask
    {
        public void Execute(DocumentDatabase database)
        {
            database.TransactionalStorage.Batch(actions =>
            {
                var indexNames = actions.Indexing.GetIndexesStats().Select(x => x.Name).ToList();
                foreach (var indexName in indexNames)
                {
                    if(database.IndexDefinitionStorage.Contains(indexName) )
                        continue;

                    // index is not found on disk, better kill for good
                    // Even though technically we are running into a situation that is considered to be corrupt data
                    // we can safely recover from it by removing the other parts of the index.
                    database.IndexStorage.DeleteIndex(indexName);
                    actions.Indexing.DeleteIndex(indexName);
                }
            });
        }
    }
}

IStartupTask is marked with InheritableExport, and the rest just plugs into RavenDB using MEF. Just by the action of creating this class, I have added new behavior to Raven. Moreover, this is now clearly executed in the proper place and in its own class, making it very easy to understand what is going on.

A lot of the stuff in RavenDB is wired in this fashion, and it makes adding things to it a snap.