Wednesday 22 January 2014

RavenDB Import / Export in code

The recommended way of doing backups automatically is using Smuggler on the server in a scheduled job or if you do manual backups and restores it’s through the RavenDB management studio but what happens if you’ve not got the ability to do either of those? It’s time to build in import/export functionality into your application.

This was a requirement that I was looking at a while back and then when I did some further investigation more recently I had found a thread on the RavenDB Google group and the associated pull request to allow for smuggling functionality without the http server running; problem solved! So how can it be used?

The changes that were made extracted out an interface ISmugglerApi to allow for a common mechanism for both the server/http version and the internal embedded implementations.

So how do we backup? This is an implementation from an MVC application perspective. Context is a way to determine the difference in implementation if you require to develop and/or deploy onto the different platforms.

public async Task<ActionResult> Backup()
{
    SmugglerOptions smugglerOptions = new SmugglerOptions { BackupPath = Server.MapPath(ServerMapPath) };
    switch (Context)
    {
        case Context.Embedded:
            DataDumper dumper = new DataDumper(((EmbeddableDocumentStore)MvcApplication.Store).DocumentDatabase, smugglerOptions);
            var embeddedExport = dumper.ExportData(null, smugglerOptions, false);
            await embeddedExport;
            break;

        case Context.Server:
            var connectionStringOptions = new RavenConnectionStringOptions
            {
                ApiKey = “insert ApiKey”,
                DefaultDatabase = “db_name,
                Url = “http://localhost:8080”
            };

            var smugglerApi = new SmugglerApi(smugglerOptions, connectionStringOptions);
            var serverExport = smugglerApi.ExportData(null, smugglerOptions, false);
            await serverExport;
            break;
    }

    return File(Server.MapPath(ServerMapPath), "application/binary", "backup.dump");
}

For the embedded version you specify the mapped server path on the local machine at the location the server will write the file to, initiate a new instance of the DataDumper class and call ExportData. There is no need for a stream or to get the incremental flag for this basic usage.

The hosted server version you have to specify they api key, database name and the url of the server to use. These are all bit of information you should know if you or can get access to if you are developing in this way. Once this is established you can call ExportData in the same way as the DataDumper call.

Restoring the backed up file is very similar but in reverse.

public async Task<ActionResult> Restore()
{
    SmugglerOptions smugglerOptions = new SmugglerOptions { BackupPath = Server.MapPath(ServerMapPath) };
    switch (Context)
    {
        case Context.Embedded:
            DataDumper dumper = new DataDumper(((EmbeddableDocumentStore)MvcApplication.Store).DocumentDatabase, smugglerOptions);
            var embeddedImport = dumper.ImportData(smugglerOptions);
            await embeddedImport;
            break;

        case Context.Server:
            var connectionStringOptions = new RavenConnectionStringOptions
            {
                ApiKey = “insert ApiKey”,
                DefaultDatabase = “db_name”,
                Url = “http://localhost:8080”
            };

            var smugglerApi = new SmugglerApi(smugglerOptions, connectionStringOptions);
            var serverImport = smugglerApi.ImportData(smugglerOptions);
            await serverImport;
            break;
    }

    return View("Index");
}

There are many other options which can be used but the above is one of the simplest implementations.

When I was doing some research on how to use this mechanism there were no examples I could find in the blogosphere however reading the the code and looking at the unit tests of the RavenDB source it was enough of an example to work with. I’d highly recommend reading the unit tests and the source of open source if you are struggling to find examples of how to use it; it is good documentation especially if the mechanism you are trying to use is used in the software itself.

No comments: