The benefits of a plugins based system: RavenDB
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.
Comments
To get rid of such bugs you have to provide a decoy - store real data files hidden deep in windows system folder and create a fake directory where users will be able to mess with files without any consequences.
Does the deletion of an index trigger a new indexing task implicit?
No,
It just deletes the index.
Ok, so what triggers the index recreation?
Louis,
There isn't index recreation, this is to detect that the user has physically deleted the files as a really crude way of deleting an index.
If someone has physical access to the data files they can screw up the database. If they wanted to they could use a hex editor to edit the MDB files on a SQL Server database.
Some concerns are simply unwarranted. If you fuck with the data, the data gets fucked.
Curious
So how would compare using MEF to say using Castle?
Thanks
Ajai
Ajai,
From usage perspective?
In Windsor, I would use if I wanted to handle things like:
lifetimes
aop
configuration
di
I use MEF more in the sense of discovery and SL than anything else.
Thanks, I was asking from usage perspective. Your response about discovery is illuminating.
So in DI would be usually interested in a single implementation of an interface.
Where as MEF scenario discovering many plugins implementing that interface...
Ajai,
Windsor tends to be behind the scene, and MEF tend to be more explicit.
With Windsor, you give it all your services, and it manages them for you.
With MEF it finds all your components, and you manage them.
There are other issues, from life cycles to how you resolve stuff that are also important, but they are not fundamentals.
You can use either one in each role, but it is probably better to use each for its own specific scenario.
Comment preview